diff options
163 files changed, 2442 insertions, 2354 deletions
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 @@ <ItemGroup> <Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\CommonIO.1.0.0.7\lib\net45\CommonIO.dll</HintPath> + <HintPath>..\packages\CommonIO.1.0.0.8\lib\net45\CommonIO.dll</HintPath> </Reference> <Reference Include="ImageMagickSharp, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> 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 /// <summary> /// Gets the cache file path based on a set of parameters /// </summary> - 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 @@ <?xml version="1.0" encoding="utf-8"?> <packages> - <package id="CommonIO" version="1.0.0.7" targetFramework="net45" /> + <package id="CommonIO" version="1.0.0.8" targetFramework="net45" /> <package id="ImageMagickSharp" version="1.0.0.18" targetFramework="net45" /> <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" /> </packages>
\ 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<ConnectSupporterSummary> - { - } - - [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<object> 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<string, string>", ParameterType = "query", Verb = "POST")] + public Dictionary<string, string> 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 + /// </summary> + 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<string, string>(); + + 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 @@ <ItemGroup> <Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\CommonIO.1.0.0.7\lib\net45\CommonIO.dll</HintPath> + <HintPath>..\packages\CommonIO.1.0.0.8\lib\net45\CommonIO.dll</HintPath> </Reference> <Reference Include="MoreLinq"> <HintPath>..\packages\morelinq.1.4.0\lib\net35\MoreLinq.dll</HintPath> @@ -80,7 +80,6 @@ <Compile Include="FilterService.cs" /> <Compile Include="IHasDtoOptions.cs" /> <Compile Include="Library\ChapterService.cs" /> - <Compile Include="PinLoginService.cs" /> <Compile Include="Playback\Dash\ManifestBuilder.cs" /> <Compile Include="Playback\Dash\MpegDashService.cs" /> <Compile Include="Playback\MediaInfoService.cs" /> 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<PinCreationResult> - { - [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<PinStatusResult> - { - [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<PinExchangeResult> - { - [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<string, MyPinStatus> _activeRequests = new ConcurrentDictionary<string, MyPinStatus>(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); } /// <summary> @@ -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 /// </summary> 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 /// <value>The task manager.</value> private ITaskManager TaskManager { get; set; } + private readonly IServerConfigurationManager _config; + /// <summary> /// Initializes a new instance of the <see cref="ScheduledTaskService" /> class. /// </summary> /// <param name="taskManager">The task manager.</param> - /// <exception cref="System.ArgumentNullException">taskManager</exception> - public ScheduledTaskService(ITaskManager taskManager) + /// <exception cref="ArgumentNullException">taskManager</exception> + public ScheduledTaskService(ITaskManager taskManager, IServerConfigurationManager config) { if (taskManager == null) { @@ -103,6 +106,7 @@ namespace MediaBrowser.Api.ScheduledTasks } TaskManager = taskManager; + _config = config; } /// <summary> @@ -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 @@ <?xml version="1.0" encoding="utf-8"?> <packages> - <package id="CommonIO" version="1.0.0.7" targetFramework="net45" /> + <package id="CommonIO" version="1.0.0.8" targetFramework="net45" /> <package id="morelinq" version="1.4.0" targetFramework="net45" /> <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" /> </packages>
\ No newline at end of file diff --git a/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj b/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj index cb3a284c1..4bee8c042 100644 --- a/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj +++ b/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj @@ -49,7 +49,7 @@ <ItemGroup> <Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\CommonIO.1.0.0.7\lib\net45\CommonIO.dll</HintPath> + <HintPath>..\packages\CommonIO.1.0.0.8\lib\net45\CommonIO.dll</HintPath> </Reference> <Reference Include="MoreLinq"> <HintPath>..\packages\morelinq.1.4.0\lib\net35\MoreLinq.dll</HintPath> 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<IPAddress> _localIpAddresses; + private List<IPAddress> _localIpAddresses; private readonly object _localIpAddressSyncLock = new object(); /// <summary> @@ -29,24 +29,20 @@ namespace MediaBrowser.Common.Implementations.Networking /// <returns>IPAddress.</returns> public IEnumerable<IPAddress> GetLocalIpAddresses() { - const int cacheMinutes = 3; - var forceRefresh = (DateTime.UtcNow - _lastRefresh).TotalMinutes >= cacheMinutes; + const int cacheMinutes = 5; - if (_localIpAddresses == null || forceRefresh) + lock (_localIpAddressSyncLock) { - lock (_localIpAddressSyncLock) - { - forceRefresh = (DateTime.UtcNow - _lastRefresh).TotalMinutes >= cacheMinutes; + var forceRefresh = (DateTime.UtcNow - _lastRefresh).TotalMinutes >= cacheMinutes; - if (_localIpAddresses == null || forceRefresh) - { - 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(); } /// <summary> @@ -233,11 +233,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks /// <summary> /// The _triggers /// </summary> - private volatile List<ITaskTrigger> _triggers; - /// <summary> - /// The _triggers sync lock - /// </summary> - private readonly object _triggersSyncLock = new object(); + private List<ITaskTrigger> _triggers; /// <summary> /// Gets the triggers that define when the task will run /// </summary> @@ -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 @@ <?xml version="1.0" encoding="utf-8"?> <packages> - <package id="CommonIO" version="1.0.0.7" targetFramework="net45" /> + <package id="CommonIO" version="1.0.0.8" targetFramework="net45" /> <package id="morelinq" version="1.4.0" targetFramework="net45" /> <package id="NLog" version="4.2.3" targetFramework="net45" /> <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" /> 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 /// <param name="token">The token.</param> /// <returns><c>true</c> if [is authorization token valid] [the specified token]; otherwise, <c>false</c>.</returns> bool IsAuthorizationTokenValid(string token); - - /// <summary> - /// Gets the connect supporter summary. - /// </summary> - /// <returns>Task<ConnectSupporterSummary>.</returns> - Task<ConnectSupporterSummary> GetConnectSupporterSummary(); - - /// <summary> - /// Removes the connect supporter. - /// </summary> - /// <param name="id">The identifier.</param> - /// <returns>Task.</returns> - Task RemoveConnectSupporter(string id); - - /// <summary> - /// Adds the connect supporter. - /// </summary> - /// <param name="id">The identifier.</param> - /// <returns>Task.</returns> - 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 { /// <summary> /// 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<Guid> 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<QueryResult<BaseItem>> GetItems(InternalItemsQuery query) + { + var user = query.User; + + Func<BaseItem, bool> filter = i => UserViewBuilder.Filter(i, user, query, UserDataManager, LibraryManager); + + IEnumerable<BaseItem> 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); + } + /// <summary> /// Gets the episodes. /// </summary> 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<QueryResult<BaseItem>> GetItems(InternalItemsQuery query) + { + var user = query.User; + + Func<BaseItem, bool> filter = i => UserViewBuilder.Filter(i, user, query, UserDataManager, LibraryManager); + + IEnumerable<BaseItem> 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<Season> 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 /// </summary> /// <value><c>true</c> if played; otherwise, <c>false</c>.</value> public bool Played { get; set; } - + /// <summary> + /// Gets or sets the index of the audio stream. + /// </summary> + /// <value>The index of the audio stream.</value> + public int? AudioStreamIndex { get; set; } + /// <summary> + /// Gets or sets the index of the subtitle stream. + /// </summary> + /// <value>The index of the subtitle stream.</value> + public int? SubtitleStreamIndex { get; set; } + /// <summary> /// 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 @@ -57,6 +57,13 @@ namespace MediaBrowser.Controller.Library Person GetPerson(string name); /// <summary> + /// Finds the by path. + /// </summary> + /// <param name="path">The path.</param> + /// <returns>BaseItem.</returns> + BaseItem FindByPath(string path); + + /// <summary> /// Gets the artist. /// </summary> /// <param name="name">The name.</param> 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 /// </summary> /// <param name="path">The path.</param> void ReportFileSystemChanged(string path); + + /// <summary> + /// Determines whether [is path locked] [the specified path]. + /// </summary> + /// <param name="path">The path.</param> + /// <returns><c>true</c> if [is path locked] [the specified path]; otherwise, <c>false</c>.</returns> + 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 @@ -26,6 +26,12 @@ namespace MediaBrowser.Controller.LiveTv public string Id { get; set; } /// <summary> + /// Gets or sets the tuner host identifier. + /// </summary> + /// <value>The tuner host identifier.</value> + public string TunerHostId { get; set; } + + /// <summary> /// Gets or sets the type of the channel. /// </summary> /// <value>The type of the channel.</value> 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 /// <summary> /// Adds the information to program dto. /// </summary> - /// <param name="item">The item.</param> - /// <param name="dto">The dto.</param> + /// <param name="programs">The programs.</param> /// <param name="fields">The fields.</param> /// <param name="user">The user.</param> - void AddInfoToProgramDto(BaseItem item, BaseItemDto dto, List<ItemFields> fields, User user = null); + /// <returns>Task.</returns> + Task AddInfoToProgramDto(List<Tuple<BaseItem,BaseItemDto>> programs, List<ItemFields> fields, User user = null); /// <summary> /// Saves the tuner host. /// </summary> 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 /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task<List<MediaSourceInfo>>.</returns> Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(string channelId, CancellationToken cancellationToken); + } + public interface IConfigurableTunerHost + { /// <summary> /// Validates the specified information. /// </summary> 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 /// <value>The clients.</value> public List<string> Clients { get; set; } + /// <summary> + /// Gets or sets a value indicating whether this instance can reset. + /// </summary> + /// <value><c>true</c> if this instance can reset; otherwise, <c>false</c>.</value> + public bool CanReset { get; set; } + public LiveTvTunerInfo() { Clients = new List<string>(); 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 @@ <ItemGroup> <Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\CommonIO.1.0.0.7\lib\net45\CommonIO.dll</HintPath> + <HintPath>..\packages\CommonIO.1.0.0.8\lib\net45\CommonIO.dll</HintPath> </Reference> <Reference Include="Interfaces.IO"> <HintPath>..\packages\Interfaces.IO.1.0.0.5\lib\portable-net45+sl4+wp71+win8+wpa81\Interfaces.IO.dll</HintPath> 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; } /// <summary> /// 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 @@ -251,6 +251,13 @@ namespace MediaBrowser.Controller.Session Task<AuthenticationResult> AuthenticateNewSession(AuthenticationRequest request); /// <summary> + /// Creates the new session. + /// </summary> + /// <param name="request">The request.</param> + /// <returns>Task<AuthenticationResult>.</returns> + Task<AuthenticationResult> CreateNewSession(AuthenticationRequest request); + + /// <summary> /// Reports the capabilities. /// </summary> /// <param name="sessionId">The session identifier.</param> 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 @@ <?xml version="1.0" encoding="utf-8"?> <packages> - <package id="CommonIO" version="1.0.0.7" targetFramework="net45" /> + <package id="CommonIO" version="1.0.0.8" targetFramework="net45" /> <package id="Interfaces.IO" version="1.0.0.5" targetFramework="net45" /> <package id="morelinq" version="1.4.0" targetFramework="net45" /> <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" /> 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<BaseItem> queryResult; - - if (folder is UserRootFolder) - { - var views = await _userViewManager.GetUserViews(new UserViewQuery { UserId = user.Id.ToString("N"), PresetViews = new[] { CollectionType.Movies, CollectionType.TvShows, CollectionType.Music } }, CancellationToken.None) - .ConfigureAwait(false); - - queryResult = new QueryResult<BaseItem> - { - Items = views.Cast<BaseItem>().ToArray() - }; - queryResult.TotalRecordCount = queryResult.Items.Length; - } - else + var queryResult = await folder.GetItems(new InternalItemsQuery { - queryResult = await folder.GetItems(new InternalItemsQuery - { - Limit = limit, - StartIndex = startIndex, - SortBy = sortOrders.ToArray(), - SortOrder = sort.SortOrder, - User = user, - Filter = FilterUnsupportedContent + Limit = limit, + StartIndex = startIndex, + SortBy = sortOrders.ToArray(), + SortOrder = sort.SortOrder, + User = user, + Filter = FilterUnsupportedContent, + PresetViews = new[] { CollectionType.Movies, CollectionType.TvShows, CollectionType.Music } - }).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 @@ <ItemGroup> <Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\CommonIO.1.0.0.7\lib\net45\CommonIO.dll</HintPath> + <HintPath>..\packages\CommonIO.1.0.0.8\lib\net45\CommonIO.dll</HintPath> </Reference> <Reference Include="MoreLinq"> <HintPath>..\packages\morelinq.1.4.0\lib\net35\MoreLinq.dll</HintPath> 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 @@ <MaxAlbumArtHeight>480</MaxAlbumArtHeight> <MaxIconWidth>48</MaxIconWidth> <MaxIconHeight>48</MaxIconHeight> - <MaxStreamingBitrate>10000000</MaxStreamingBitrate> - <MaxStaticBitrate>10000000</MaxStaticBitrate> + <MaxStreamingBitrate>12000000</MaxStreamingBitrate> + <MaxStaticBitrate>12000000</MaxStaticBitrate> <MusicStreamingTranscodingBitrate>128000</MusicStreamingTranscodingBitrate> <MusicSyncBitrate>128000</MusicSyncBitrate> <XDlnaDoc>DMS-1.50</XDlnaDoc> @@ -41,9 +41,9 @@ <DirectPlayProfile container="jpeg,png,gif,bmp,tiff" type="Photo" /> </DirectPlayProfiles> <TranscodingProfiles> - <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> - <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> - <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> + <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> + <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> + <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> </TranscodingProfiles> <ContainerProfiles /> <CodecProfiles /> 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 @@ <MaxAlbumArtHeight>480</MaxAlbumArtHeight> <MaxIconWidth>48</MaxIconWidth> <MaxIconHeight>48</MaxIconHeight> - <MaxStreamingBitrate>10000000</MaxStreamingBitrate> - <MaxStaticBitrate>10000000</MaxStaticBitrate> + <MaxStreamingBitrate>12000000</MaxStreamingBitrate> + <MaxStaticBitrate>12000000</MaxStaticBitrate> <MusicStreamingTranscodingBitrate>128000</MusicStreamingTranscodingBitrate> <MusicSyncBitrate>128000</MusicSyncBitrate> <XDlnaDoc>DMS-1.50</XDlnaDoc> @@ -34,9 +34,9 @@ <DirectPlayProfile container="avi,mp4" type="Video" /> </DirectPlayProfiles> <TranscodingProfiles> - <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> - <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> - <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> + <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> + <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> + <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> </TranscodingProfiles> <ContainerProfiles /> <CodecProfiles /> 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 @@ <MaxAlbumArtHeight>480</MaxAlbumArtHeight> <MaxIconWidth>48</MaxIconWidth> <MaxIconHeight>48</MaxIconHeight> - <MaxStreamingBitrate>10000000</MaxStreamingBitrate> - <MaxStaticBitrate>10000000</MaxStaticBitrate> + <MaxStreamingBitrate>12000000</MaxStreamingBitrate> + <MaxStaticBitrate>12000000</MaxStaticBitrate> <MusicStreamingTranscodingBitrate>128000</MusicStreamingTranscodingBitrate> <MusicSyncBitrate>128000</MusicSyncBitrate> <XDlnaDoc>DMS-1.50</XDlnaDoc> @@ -38,9 +38,9 @@ <DirectPlayProfile container="mp3,flac,m4a,wma" type="Audio" /> </DirectPlayProfiles> <TranscodingProfiles> - <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> - <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> - <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> + <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> + <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> + <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> </TranscodingProfiles> <ContainerProfiles /> <CodecProfiles /> 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 @@ <MaxAlbumArtHeight>480</MaxAlbumArtHeight> <MaxIconWidth>48</MaxIconWidth> <MaxIconHeight>48</MaxIconHeight> - <MaxStreamingBitrate>10000000</MaxStreamingBitrate> - <MaxStaticBitrate>10000000</MaxStaticBitrate> + <MaxStreamingBitrate>12000000</MaxStreamingBitrate> + <MaxStaticBitrate>12000000</MaxStaticBitrate> <MusicStreamingTranscodingBitrate>128000</MusicStreamingTranscodingBitrate> <MusicSyncBitrate>128000</MusicSyncBitrate> <XDlnaDoc>DMS-1.50</XDlnaDoc> @@ -40,8 +40,8 @@ <DirectPlayProfile container="jpeg,jpg" type="Photo" /> </DirectPlayProfiles> <TranscodingProfiles> - <TranscodingProfile container="mpeg" type="Video" videoCodec="mpeg2video" audioCodec="mp2" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> - <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> + <TranscodingProfile container="mpeg" type="Video" videoCodec="mpeg2video" audioCodec="mp2" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> + <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> </TranscodingProfiles> <ContainerProfiles /> <CodecProfiles> 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 @@ <MaxAlbumArtHeight>480</MaxAlbumArtHeight> <MaxIconWidth>48</MaxIconWidth> <MaxIconHeight>48</MaxIconHeight> - <MaxStreamingBitrate>10000000</MaxStreamingBitrate> - <MaxStaticBitrate>10000000</MaxStaticBitrate> + <MaxStreamingBitrate>12000000</MaxStreamingBitrate> + <MaxStaticBitrate>12000000</MaxStaticBitrate> <MusicStreamingTranscodingBitrate>128000</MusicStreamingTranscodingBitrate> <MusicSyncBitrate>128000</MusicSyncBitrate> <XDlnaDoc>DMS-1.50</XDlnaDoc> @@ -44,9 +44,9 @@ <DirectPlayProfile container="jpeg" type="Photo" /> </DirectPlayProfiles> <TranscodingProfiles> - <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> - <TranscodingProfile container="mp4" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> - <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> + <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> + <TranscodingProfile container="mp4" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> + <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> </TranscodingProfiles> <ContainerProfiles /> <CodecProfiles> 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 @@ <DirectPlayProfile container="" type="Photo" /> </DirectPlayProfiles> <TranscodingProfiles> - <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> - <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> - <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> + <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> + <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> + <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> </TranscodingProfiles> <ContainerProfiles /> <CodecProfiles /> 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 @@ <MaxAlbumArtHeight>480</MaxAlbumArtHeight> <MaxIconWidth>48</MaxIconWidth> <MaxIconHeight>48</MaxIconHeight> - <MaxStreamingBitrate>10000000</MaxStreamingBitrate> - <MaxStaticBitrate>10000000</MaxStaticBitrate> + <MaxStreamingBitrate>12000000</MaxStreamingBitrate> + <MaxStaticBitrate>12000000</MaxStaticBitrate> <MusicStreamingTranscodingBitrate>128000</MusicStreamingTranscodingBitrate> <MusicSyncBitrate>128000</MusicSyncBitrate> <XDlnaDoc>DMS-1.50</XDlnaDoc> @@ -43,9 +43,9 @@ <DirectPlayProfile container="jpeg" type="Photo" /> </DirectPlayProfiles> <TranscodingProfiles> - <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> - <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> - <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> + <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> + <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> + <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> </TranscodingProfiles> <ContainerProfiles> <ContainerProfile type="Photo"> 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 @@ <MaxAlbumArtHeight>480</MaxAlbumArtHeight> <MaxIconWidth>48</MaxIconWidth> <MaxIconHeight>48</MaxIconHeight> - <MaxStreamingBitrate>10000000</MaxStreamingBitrate> - <MaxStaticBitrate>10000000</MaxStaticBitrate> + <MaxStreamingBitrate>12000000</MaxStreamingBitrate> + <MaxStaticBitrate>12000000</MaxStaticBitrate> <MusicStreamingTranscodingBitrate>128000</MusicStreamingTranscodingBitrate> <MusicSyncBitrate>128000</MusicSyncBitrate> <XDlnaDoc>DMS-1.50</XDlnaDoc> @@ -38,9 +38,9 @@ <DirectPlayProfile container="avi,mp4,mkv,ts" type="Video" /> </DirectPlayProfiles> <TranscodingProfiles> - <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> - <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> - <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> + <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> + <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> + <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> </TranscodingProfiles> <ContainerProfiles /> <CodecProfiles /> 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 @@ <MaxAlbumArtHeight>480</MaxAlbumArtHeight> <MaxIconWidth>48</MaxIconWidth> <MaxIconHeight>48</MaxIconHeight> - <MaxStreamingBitrate>10000000</MaxStreamingBitrate> - <MaxStaticBitrate>10000000</MaxStaticBitrate> + <MaxStreamingBitrate>12000000</MaxStreamingBitrate> + <MaxStaticBitrate>12000000</MaxStaticBitrate> <MusicStreamingTranscodingBitrate>128000</MusicStreamingTranscodingBitrate> <MusicSyncBitrate>128000</MusicSyncBitrate> <XDlnaDoc>DMS-1.50</XDlnaDoc> @@ -44,9 +44,9 @@ <DirectPlayProfile container="ogg" audioCodec="vorbis" type="Audio" /> </DirectPlayProfiles> <TranscodingProfiles> - <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> - <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> - <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> + <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> + <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> + <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> </TranscodingProfiles> <ContainerProfiles /> <CodecProfiles /> 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 @@ <MaxAlbumArtHeight>480</MaxAlbumArtHeight> <MaxIconWidth>48</MaxIconWidth> <MaxIconHeight>48</MaxIconHeight> - <MaxStreamingBitrate>10000000</MaxStreamingBitrate> - <MaxStaticBitrate>10000000</MaxStaticBitrate> + <MaxStreamingBitrate>12000000</MaxStreamingBitrate> + <MaxStaticBitrate>12000000</MaxStaticBitrate> <MusicStreamingTranscodingBitrate>128000</MusicStreamingTranscodingBitrate> <MusicSyncBitrate>128000</MusicSyncBitrate> <XDlnaDoc>DMS-1.50</XDlnaDoc> @@ -51,9 +51,9 @@ <DirectPlayProfile container="jpeg" type="Photo" /> </DirectPlayProfiles> <TranscodingProfiles> - <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> - <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> - <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> + <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> + <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> + <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> </TranscodingProfiles> <ContainerProfiles> <ContainerProfile type="Photo"> 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 @@ <MaxAlbumArtHeight>480</MaxAlbumArtHeight> <MaxIconWidth>48</MaxIconWidth> <MaxIconHeight>48</MaxIconHeight> - <MaxStreamingBitrate>10000000</MaxStreamingBitrate> - <MaxStaticBitrate>10000000</MaxStaticBitrate> + <MaxStreamingBitrate>12000000</MaxStreamingBitrate> + <MaxStaticBitrate>12000000</MaxStaticBitrate> <MusicStreamingTranscodingBitrate>128000</MusicStreamingTranscodingBitrate> <MusicSyncBitrate>128000</MusicSyncBitrate> <XDlnaDoc>DMS-1.50</XDlnaDoc> @@ -39,9 +39,9 @@ <DirectPlayProfile container="jpeg,gif,bmp,png" type="Photo" /> </DirectPlayProfiles> <TranscodingProfiles> - <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> - <TranscodingProfile container="mp4" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> - <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> + <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> + <TranscodingProfile container="mp4" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> + <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> </TranscodingProfiles> <ContainerProfiles /> <CodecProfiles> 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 @@ <MaxAlbumArtHeight>480</MaxAlbumArtHeight> <MaxIconWidth>48</MaxIconWidth> <MaxIconHeight>48</MaxIconHeight> - <MaxStreamingBitrate>10000000</MaxStreamingBitrate> - <MaxStaticBitrate>10000000</MaxStaticBitrate> + <MaxStreamingBitrate>12000000</MaxStreamingBitrate> + <MaxStaticBitrate>12000000</MaxStaticBitrate> <MusicStreamingTranscodingBitrate>128000</MusicStreamingTranscodingBitrate> <MusicSyncBitrate>128000</MusicSyncBitrate> <XDlnaDoc>DMS-1.50</XDlnaDoc> @@ -47,13 +47,13 @@ <DirectPlayProfile container="vro,vob" audioCodec="ac3,mp2,mp3" videoCodec="mpeg1video,mpeg2video" type="Video" /> <DirectPlayProfile container="ts" audioCodec="ac3,aac,mp3,eac3" videoCodec="mpeg2video,h264,vc1" type="Video" /> <DirectPlayProfile container="asf" audioCodec="wmav2,wmavoice" videoCodec="wmv2,wmv3" type="Video" /> - <DirectPlayProfile container="mp3" audioCodec="mp3" type="Audio" /> + <DirectPlayProfile container="mp3,flac" type="Audio" /> <DirectPlayProfile container="jpeg" type="Photo" /> </DirectPlayProfiles> <TranscodingProfiles> - <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> - <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="true" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> - <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> + <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> + <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="true" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> + <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> </TranscodingProfiles> <ContainerProfiles> <ContainerProfile type="Photo"> 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 @@ <MaxAlbumArtHeight>480</MaxAlbumArtHeight> <MaxIconWidth>48</MaxIconWidth> <MaxIconHeight>48</MaxIconHeight> - <MaxStreamingBitrate>10000000</MaxStreamingBitrate> - <MaxStaticBitrate>10000000</MaxStaticBitrate> + <MaxStreamingBitrate>12000000</MaxStreamingBitrate> + <MaxStaticBitrate>12000000</MaxStaticBitrate> <MusicStreamingTranscodingBitrate>128000</MusicStreamingTranscodingBitrate> <MusicSyncBitrate>128000</MusicSyncBitrate> <XDlnaDoc>DMS-1.50</XDlnaDoc> @@ -49,9 +49,9 @@ <DirectPlayProfile container="jpeg" type="Photo" /> </DirectPlayProfiles> <TranscodingProfiles> - <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> - <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> - <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> + <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> + <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> + <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> </TranscodingProfiles> <ContainerProfiles> <ContainerProfile type="Photo"> 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 @@ <MaxAlbumArtHeight>480</MaxAlbumArtHeight> <MaxIconWidth>48</MaxIconWidth> <MaxIconHeight>48</MaxIconHeight> - <MaxStreamingBitrate>10000000</MaxStreamingBitrate> - <MaxStaticBitrate>10000000</MaxStaticBitrate> + <MaxStreamingBitrate>12000000</MaxStreamingBitrate> + <MaxStaticBitrate>12000000</MaxStaticBitrate> <MusicStreamingTranscodingBitrate>128000</MusicStreamingTranscodingBitrate> <MusicSyncBitrate>128000</MusicSyncBitrate> <XDlnaDoc>DMS-1.50</XDlnaDoc> @@ -48,9 +48,9 @@ <DirectPlayProfile container="jpeg" type="Photo" /> </DirectPlayProfiles> <TranscodingProfiles> - <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> - <TranscodingProfile container="ts" type="Video" videoCodec="mpeg2video" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> - <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> + <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> + <TranscodingProfile container="ts" type="Video" videoCodec="mpeg2video" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> + <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> </TranscodingProfiles> <ContainerProfiles> <ContainerProfile type="Photo"> 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 @@ <MaxAlbumArtHeight>480</MaxAlbumArtHeight> <MaxIconWidth>48</MaxIconWidth> <MaxIconHeight>48</MaxIconHeight> - <MaxStreamingBitrate>10000000</MaxStreamingBitrate> - <MaxStaticBitrate>10000000</MaxStaticBitrate> + <MaxStreamingBitrate>12000000</MaxStreamingBitrate> + <MaxStaticBitrate>12000000</MaxStaticBitrate> <MusicStreamingTranscodingBitrate>128000</MusicStreamingTranscodingBitrate> <MusicSyncBitrate>128000</MusicSyncBitrate> <XDlnaDoc>DMS-1.50</XDlnaDoc> @@ -46,9 +46,9 @@ <DirectPlayProfile container="mp3" audioCodec="mp3" type="Audio" /> </DirectPlayProfiles> <TranscodingProfiles> - <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> - <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="true" transcodeSeekInfo="Auto" context="Streaming" /> - <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> + <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> + <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="true" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> + <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> </TranscodingProfiles> <ContainerProfiles> <ContainerProfile type="Photo"> 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 @@ <MaxAlbumArtHeight>480</MaxAlbumArtHeight> <MaxIconWidth>48</MaxIconWidth> <MaxIconHeight>48</MaxIconHeight> - <MaxStreamingBitrate>10000000</MaxStreamingBitrate> - <MaxStaticBitrate>10000000</MaxStaticBitrate> + <MaxStreamingBitrate>12000000</MaxStreamingBitrate> + <MaxStaticBitrate>12000000</MaxStaticBitrate> <MusicStreamingTranscodingBitrate>128000</MusicStreamingTranscodingBitrate> <MusicSyncBitrate>128000</MusicSyncBitrate> <XDlnaDoc>DMS-1.50</XDlnaDoc> @@ -49,9 +49,9 @@ <DirectPlayProfile container="asf" audioCodec="wmav2,wmapro,wmavoice" type="Audio" /> </DirectPlayProfiles> <TranscodingProfiles> - <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> - <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="true" transcodeSeekInfo="Auto" context="Streaming" /> - <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> + <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> + <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="true" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> + <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> </TranscodingProfiles> <ContainerProfiles> <ContainerProfile type="Photo"> 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 @@ <MaxAlbumArtHeight>480</MaxAlbumArtHeight> <MaxIconWidth>48</MaxIconWidth> <MaxIconHeight>48</MaxIconHeight> - <MaxStreamingBitrate>10000000</MaxStreamingBitrate> - <MaxStaticBitrate>10000000</MaxStaticBitrate> + <MaxStreamingBitrate>12000000</MaxStreamingBitrate> + <MaxStaticBitrate>12000000</MaxStaticBitrate> <MusicStreamingTranscodingBitrate>128000</MusicStreamingTranscodingBitrate> <MusicSyncBitrate>128000</MusicSyncBitrate> <XDlnaDoc>DMS-1.50</XDlnaDoc> @@ -51,9 +51,9 @@ <DirectPlayProfile container="jpeg" type="Photo" /> </DirectPlayProfiles> <TranscodingProfiles> - <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> - <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="true" transcodeSeekInfo="Auto" context="Streaming" /> - <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> + <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> + <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="true" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> + <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> </TranscodingProfiles> <ContainerProfiles> <ContainerProfile type="Photo"> 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 @@ <MaxAlbumArtHeight>480</MaxAlbumArtHeight> <MaxIconWidth>48</MaxIconWidth> <MaxIconHeight>48</MaxIconHeight> - <MaxStreamingBitrate>10000000</MaxStreamingBitrate> - <MaxStaticBitrate>10000000</MaxStaticBitrate> + <MaxStreamingBitrate>12000000</MaxStreamingBitrate> + <MaxStaticBitrate>12000000</MaxStaticBitrate> <MusicStreamingTranscodingBitrate>128000</MusicStreamingTranscodingBitrate> <MusicSyncBitrate>128000</MusicSyncBitrate> <XDlnaDoc>DMS-1.50</XDlnaDoc> @@ -56,9 +56,9 @@ <DirectPlayProfile container="jpeg" type="Photo" /> </DirectPlayProfiles> <TranscodingProfiles> - <TranscodingProfile container="mp3" type="Audio" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> - <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="true" transcodeSeekInfo="Auto" context="Streaming" /> - <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> + <TranscodingProfile container="mp3" type="Audio" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> + <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="true" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> + <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> </TranscodingProfiles> <ContainerProfiles> <ContainerProfile type="Photo"> 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 @@ <MaxAlbumArtHeight>480</MaxAlbumArtHeight> <MaxIconWidth>48</MaxIconWidth> <MaxIconHeight>48</MaxIconHeight> - <MaxStreamingBitrate>10000000</MaxStreamingBitrate> - <MaxStaticBitrate>10000000</MaxStaticBitrate> + <MaxStreamingBitrate>12000000</MaxStreamingBitrate> + <MaxStaticBitrate>12000000</MaxStaticBitrate> <MusicStreamingTranscodingBitrate>128000</MusicStreamingTranscodingBitrate> <MusicSyncBitrate>128000</MusicSyncBitrate> <XDlnaDoc>DMS-1.50</XDlnaDoc> @@ -56,9 +56,9 @@ <DirectPlayProfile container="jpeg" type="Photo" /> </DirectPlayProfiles> <TranscodingProfiles> - <TranscodingProfile container="mp3" type="Audio" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> - <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="true" transcodeSeekInfo="Auto" context="Streaming" /> - <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> + <TranscodingProfile container="mp3" type="Audio" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> + <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="true" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> + <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> </TranscodingProfiles> <ContainerProfiles> <ContainerProfile type="Photo"> 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 @@ <MaxAlbumArtHeight>480</MaxAlbumArtHeight> <MaxIconWidth>48</MaxIconWidth> <MaxIconHeight>48</MaxIconHeight> - <MaxStreamingBitrate>10000000</MaxStreamingBitrate> - <MaxStaticBitrate>10000000</MaxStaticBitrate> + <MaxStreamingBitrate>12000000</MaxStreamingBitrate> + <MaxStaticBitrate>12000000</MaxStaticBitrate> <MusicStreamingTranscodingBitrate>128000</MusicStreamingTranscodingBitrate> <MusicSyncBitrate>128000</MusicSyncBitrate> <XDlnaDoc>DMS-1.50</XDlnaDoc> @@ -46,9 +46,9 @@ <DirectPlayProfile container="jpeg,png,gif,bmp,tiff" type="Photo" /> </DirectPlayProfiles> <TranscodingProfiles> - <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> - <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3,aac,mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> - <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> + <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> + <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3,aac,mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> + <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> </TranscodingProfiles> <ContainerProfiles> <ContainerProfile type="Photo"> 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 @@ <MaxAlbumArtHeight>480</MaxAlbumArtHeight> <MaxIconWidth>48</MaxIconWidth> <MaxIconHeight>48</MaxIconHeight> - <MaxStreamingBitrate>10000000</MaxStreamingBitrate> - <MaxStaticBitrate>10000000</MaxStaticBitrate> + <MaxStreamingBitrate>12000000</MaxStreamingBitrate> + <MaxStaticBitrate>12000000</MaxStaticBitrate> <MusicStreamingTranscodingBitrate>128000</MusicStreamingTranscodingBitrate> <MusicSyncBitrate>128000</MusicSyncBitrate> <XDlnaDoc>DMS-1.50</XDlnaDoc> @@ -46,9 +46,9 @@ <DirectPlayProfile container="jpeg,png,gif,bmp,tiff" type="Photo" /> </DirectPlayProfiles> <TranscodingProfiles> - <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> - <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> - <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> + <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> + <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> + <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> </TranscodingProfiles> <ContainerProfiles> <ContainerProfile type="Photo"> 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 @@ <MaxAlbumArtHeight>480</MaxAlbumArtHeight> <MaxIconWidth>48</MaxIconWidth> <MaxIconHeight>48</MaxIconHeight> - <MaxStreamingBitrate>10000000</MaxStreamingBitrate> - <MaxStaticBitrate>10000000</MaxStaticBitrate> + <MaxStreamingBitrate>12000000</MaxStreamingBitrate> + <MaxStaticBitrate>12000000</MaxStaticBitrate> <MusicStreamingTranscodingBitrate>128000</MusicStreamingTranscodingBitrate> <MusicSyncBitrate>128000</MusicSyncBitrate> <XDlnaDoc>DMS-1.50</XDlnaDoc> @@ -41,9 +41,9 @@ <DirectPlayProfile container="jpeg,png,gif,bmp,tiff" type="Photo" /> </DirectPlayProfiles> <TranscodingProfiles> - <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> - <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> - <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> + <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> + <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> + <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> </TranscodingProfiles> <ContainerProfiles /> <CodecProfiles /> 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 @@ <MaxAlbumArtHeight>480</MaxAlbumArtHeight> <MaxIconWidth>48</MaxIconWidth> <MaxIconHeight>48</MaxIconHeight> - <MaxStreamingBitrate>10000000</MaxStreamingBitrate> - <MaxStaticBitrate>10000000</MaxStaticBitrate> + <MaxStreamingBitrate>12000000</MaxStreamingBitrate> + <MaxStaticBitrate>12000000</MaxStaticBitrate> <MusicStreamingTranscodingBitrate>128000</MusicStreamingTranscodingBitrate> <MusicSyncBitrate>128000</MusicSyncBitrate> <XDlnaDoc>DMS-1.50</XDlnaDoc> @@ -52,9 +52,9 @@ <DirectPlayProfile container="jpeg,png,gif,bmp,tiff" type="Photo" /> </DirectPlayProfiles> <TranscodingProfiles> - <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> - <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> - <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> + <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> + <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> + <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> </TranscodingProfiles> <ContainerProfiles> <ContainerProfile type="Photo"> 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 @@ <MaxAlbumArtHeight>480</MaxAlbumArtHeight> <MaxIconWidth>48</MaxIconWidth> <MaxIconHeight>48</MaxIconHeight> - <MaxStreamingBitrate>10000000</MaxStreamingBitrate> - <MaxStaticBitrate>10000000</MaxStaticBitrate> + <MaxStreamingBitrate>12000000</MaxStreamingBitrate> + <MaxStaticBitrate>12000000</MaxStaticBitrate> <MusicStreamingTranscodingBitrate>128000</MusicStreamingTranscodingBitrate> <MusicSyncBitrate>128000</MusicSyncBitrate> <XDlnaDoc>DMS-1.50</XDlnaDoc> @@ -46,9 +46,9 @@ <DirectPlayProfile container="jpeg" type="Photo" /> </DirectPlayProfiles> <TranscodingProfiles> - <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> - <TranscodingProfile container="asf" type="Video" videoCodec="wmv2" audioCodec="wmav2" estimateContentLength="true" enableMpegtsM2TsMode="false" transcodeSeekInfo="Bytes" context="Streaming" /> - <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> + <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> + <TranscodingProfile container="asf" type="Video" videoCodec="wmv2" audioCodec="wmav2" estimateContentLength="true" enableMpegtsM2TsMode="false" transcodeSeekInfo="Bytes" copyTimestamps="false" context="Streaming" /> + <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> </TranscodingProfiles> <ContainerProfiles> <ContainerProfile type="Video" container="mp4,mov"> 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 @@ <MaxAlbumArtHeight>480</MaxAlbumArtHeight> <MaxIconWidth>48</MaxIconWidth> <MaxIconHeight>48</MaxIconHeight> - <MaxStreamingBitrate>10000000</MaxStreamingBitrate> - <MaxStaticBitrate>10000000</MaxStaticBitrate> + <MaxStreamingBitrate>12000000</MaxStreamingBitrate> + <MaxStaticBitrate>12000000</MaxStaticBitrate> <MusicStreamingTranscodingBitrate>128000</MusicStreamingTranscodingBitrate> <MusicSyncBitrate>128000</MusicSyncBitrate> <XDlnaDoc>DMS-1.50</XDlnaDoc> @@ -37,7 +37,7 @@ <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests> <XmlRootAttributes /> <DirectPlayProfiles> - <DirectPlayProfile container="ts" audioCodec="ac3" videoCodec="h264" type="Video" /> + <DirectPlayProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264,mpeg2video" type="Video" /> <DirectPlayProfile container="avi" audioCodec="ac3,mp3" videoCodec="mpeg4" type="Video" /> <DirectPlayProfile container="avi" audioCodec="aac" videoCodec="h264" type="Video" /> <DirectPlayProfile container="mp4,mov,mkv" audioCodec="aac,ac3" videoCodec="h264,mpeg4,mpeg2video" type="Video" /> @@ -47,9 +47,9 @@ <DirectPlayProfile container="jpeg" type="Photo" /> </DirectPlayProfiles> <TranscodingProfiles> - <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> - <TranscodingProfile container="jpeg" type="Photo" videoCodec="jpeg" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> - <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> + <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> + <TranscodingProfile container="jpeg" type="Photo" videoCodec="jpeg" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> + <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> </TranscodingProfiles> <ContainerProfiles> <ContainerProfile type="Video" container="mp4,mov"> 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 @@ <MaxAlbumArtHeight>480</MaxAlbumArtHeight> <MaxIconWidth>48</MaxIconWidth> <MaxIconHeight>48</MaxIconHeight> - <MaxStreamingBitrate>10000000</MaxStreamingBitrate> - <MaxStaticBitrate>10000000</MaxStaticBitrate> + <MaxStreamingBitrate>12000000</MaxStreamingBitrate> + <MaxStaticBitrate>12000000</MaxStaticBitrate> <MusicStreamingTranscodingBitrate>128000</MusicStreamingTranscodingBitrate> <MusicSyncBitrate>128000</MusicSyncBitrate> <XDlnaDoc>DMS-1.50</XDlnaDoc> @@ -44,9 +44,9 @@ <DirectPlayProfile container="ogg" audioCodec="vorbis" type="Audio" /> </DirectPlayProfiles> <TranscodingProfiles> - <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> - <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> - <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" /> + <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> + <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> + <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" /> </TranscodingProfiles> <ContainerProfiles /> <CodecProfiles /> 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<SsdpMessageEventArgs> 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<UpnpDevice> 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<string> 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 /// </summary> 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); + } } /// <summary> @@ -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 @@ <?xml version="1.0" encoding="utf-8"?> <packages> - <package id="CommonIO" version="1.0.0.7" targetFramework="net45" /> + <package id="CommonIO" version="1.0.0.8" targetFramework="net45" /> <package id="morelinq" version="1.4.0" targetFramework="net45" /> <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" /> </packages>
\ 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 @@ <ItemGroup> <Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\CommonIO.1.0.0.7\lib\net45\CommonIO.dll</HintPath> + <HintPath>..\packages\CommonIO.1.0.0.8\lib\net45\CommonIO.dll</HintPath> </Reference> <Reference Include="Patterns.Logging"> <HintPath>..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.dll</HintPath> 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 @@ <?xml version="1.0" encoding="utf-8"?> <packages> - <package id="CommonIO" version="1.0.0.7" targetFramework="net45" /> + <package id="CommonIO" version="1.0.0.8" targetFramework="net45" /> <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" /> </packages>
\ 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 /// <param name="request">The request.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task.</returns> - public Task<Model.MediaInfo.MediaInfo> GetMediaInfo(MediaInfoRequest request, CancellationToken cancellationToken) + public Task<MediaInfo> GetMediaInfo(MediaInfoRequest request, CancellationToken cancellationToken) { var extractChapters = request.MediaType == DlnaProfileType.Video && request.ExtractChapters; @@ -175,7 +175,7 @@ namespace MediaBrowser.MediaEncoding.Encoder /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task{MediaInfoResult}.</returns> /// <exception cref="System.ApplicationException">ffprobe failed - streams and format are both null.</exception> - private async Task<Model.MediaInfo.MediaInfo> GetMediaInfoInternal(string inputPath, + private async Task<MediaInfo> 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 @@ </Reference> <Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\CommonIO.1.0.0.7\lib\net45\CommonIO.dll</HintPath> + <HintPath>..\packages\CommonIO.1.0.0.8\lib\net45\CommonIO.dll</HintPath> </Reference> <Reference Include="DvdLib"> <HintPath>..\packages\MediaBrowser.BdInfo.1.0.0.10\lib\net35\DvdLib.dll</HintPath> 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) - { - SetAudioRuntimeTicks(data, info); + var tags = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); + var tagStreamType = isAudio ? "audio" : "video"; - var tags = new Dictionary<string, string>(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) + { + var tagStream = data.streams.FirstOrDefault(i => string.Equals(i.codec_type, tagStreamType, StringComparison.OrdinalIgnoreCase)); - 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 tagStream.tags) { - foreach (var pair in audioStream.tags) - { - tags[pair.Key] = pair.Value; - } + tags[pair.Key] = pair.Value; } } + } - if (data.format != null && data.format.tags != null) + if (data.format != null && data.format.tags != null) + { + foreach (var pair in data.format.tags) { - foreach (var pair in data.format.tags) - { - tags[pair.Key] = pair.Value; - } + 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("<plist", StringComparison.OrdinalIgnoreCase)); + xml = "<?xml version=\"1.0\"?>" + xml; + + // <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>cast</key>\n\t<array>\n\t\t<dict>\n\t\t\t<key>name</key>\n\t\t\t<string>Blender Foundation</string>\n\t\t</dict>\n\t\t<dict>\n\t\t\t<key>name</key>\n\t\t\t<string>Janus Bager Kristensen</string>\n\t\t</dict>\n\t</array>\n\t<key>directors</key>\n\t<array>\n\t\t<dict>\n\t\t\t<key>name</key>\n\t\t\t<string>Sacha Goedegebure</string>\n\t\t</dict>\n\t</array>\n\t<key>studio</key>\n\t<string>Blender Foundation</string>\n</dict>\n</plist>\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<NameValuePair> pairs = new List<NameValuePair>(); + + // 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<NameValuePair>(); + 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<NameValuePair> ReadValueArray(XmlReader reader) + { + reader.MoveToContent(); + + List<NameValuePair> pairs = new List<NameValuePair>(); + + // 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<NameValuePair> 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 + }; + } + /// <summary> /// Converts ffprobe stream info to our MediaStream class /// </summary> - /// <param name="isAudio">if set to <c>true</c> [is audio].</param> + /// <param name="isAudio">if set to <c>true</c> [is info].</param> /// <param name="streamInfo">The stream info.</param> /// <param name="formatInfo">The format info.</param> /// <returns>MediaStream.</returns> @@ -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<string, string> tags) + private void SetAudioInfoFromTags(MediaInfo audio, Dictionary<string, string> 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 /// <summary> /// Gets the studios from the tags collection /// </summary> - /// <param name="audio">The audio.</param> + /// <param name="info">The info.</param> /// <param name="tags">The tags.</param> /// <param name="tagName">Name of the tag.</param> - private void FetchStudios(Model.MediaInfo.MediaInfo audio, Dictionary<string, string> tags, string tagName) + private void FetchStudios(MediaInfo info, Dictionary<string, string> 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 /// </summary> /// <param name="info">The information.</param> /// <param name="tags">The tags.</param> - private void FetchGenres(Model.MediaInfo.MediaInfo info, Dictionary<string, string> tags) + private void FetchGenres(MediaInfo info, Dictionary<string, string> 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 @@ <?xml version="1.0" encoding="utf-8"?> <packages> - <package id="CommonIO" version="1.0.0.7" targetFramework="net45" /> + <package id="CommonIO" version="1.0.0.8" targetFramework="net45" /> <package id="MediaBrowser.BdInfo" version="1.0.0.10" targetFramework="net45" /> <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" /> </packages>
\ 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 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <Choose> - <When Condition="$(NCrunchOriginalSolutionDir) != '' And $(NCrunchOriginalSolutionDir) != '*Undefined*'"> - <PropertyGroup> - <FodySolutionDir>$(NCrunchOriginalSolutionDir)</FodySolutionDir> - </PropertyGroup> - </When> - <When Condition="$(SolutionDir) != '' And $(SolutionDir) != '*Undefined*'"> - <PropertyGroup> - <FodySolutionDir>$(SolutionDir)</FodySolutionDir> - </PropertyGroup> - </When> - <When Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'"> - <PropertyGroup> - <FodySolutionDir>$(MSBuildProjectDirectory)\..\</FodySolutionDir> - </PropertyGroup> - </When> - </Choose> - <Choose> - <When Condition="$(KeyOriginatorFile) != '' And $(KeyOriginatorFile) != '*Undefined*'"> - <PropertyGroup> - <FodyKeyFilePath>$(KeyOriginatorFile)</FodyKeyFilePath> - </PropertyGroup> - </When> - <When Condition="$(AssemblyOriginatorKeyFile) != '' And $(AssemblyOriginatorKeyFile) != '*Undefined*'"> - <PropertyGroup> - <FodyKeyFilePath>$(AssemblyOriginatorKeyFile)</FodyKeyFilePath> - </PropertyGroup> - </When> - <Otherwise > - <PropertyGroup> - <FodyKeyFilePath></FodyKeyFilePath> - </PropertyGroup> - </Otherwise> - </Choose> - <PropertyGroup> - <IntermediateDir>$(ProjectDir)$(IntermediateOutputPath)</IntermediateDir> - <FodyMessageImportance Condition="$(FodyMessageImportance) == '' Or $(FodyMessageImportance) == '*Undefined*'">Low</FodyMessageImportance> - <FodySignAssembly Condition="$(FodySignAssembly) == '' Or $(FodySignAssembly) == '*Undefined*'">$(SignAssembly)</FodySignAssembly> - <FodyPath Condition="$(FodyPath) == '' Or $(FodyPath) == '*Undefined*'">$(MSBuildThisFileDirectory)</FodyPath> - </PropertyGroup> - <UsingTask - TaskName="Fody.WeavingTask" - AssemblyFile="$(FodyPath)\Fody.dll" /> - <Target - AfterTargets="AfterCompile" - Name="WinFodyTarget" - Condition=" '$(OS)' == 'Windows_NT'"> - - <Fody.WeavingTask - AssemblyPath="@(IntermediateAssembly)" - IntermediateDir="$(IntermediateDir)" - KeyFilePath="$(FodyKeyFilePath)" - MessageImportance="$(FodyMessageImportance)" - ProjectDirectory="$(ProjectDir)" - SolutionDir="$(FodySolutionDir)" - References="@(ReferencePath)" - SignAssembly="$(FodySignAssembly)" - ReferenceCopyLocalPaths="@(ReferenceCopyLocalPaths)" - DefineConstants="$(DefineConstants)" - /> - </Target> - - <Target - AfterTargets="AfterBuild" - Name="NonWinFodyTarget" - Condition=" '$(OS)' != 'Windows_NT'"> - <Fody.WeavingTask - AssemblyPath="$(TargetPath)" - IntermediateDir="$(IntermediateDir)" - KeyFilePath="$(FodyKeyFilePath)" - MessageImportance="$(FodyMessageImportance)" - ProjectDirectory="$(ProjectDir)" - SolutionDir="$(FodySolutionDir)" - References="@(ReferencePath)" - SignAssembly="$(FodySignAssembly)" - ReferenceCopyLocalPaths="$(ReferenceCopyLocalPaths)" - DefineConstants="$(DefineConstants)" - /> - </Target> - - - <!--Support for ncrunch--> - <ItemGroup> - <None Include="$(FodyPath)\*.*" /> - </ItemGroup> - -</Project>
\ 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 @@ <FileAlignment>512</FileAlignment> <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir> - <FodyPath>..\packages\Fody.1.19.1.0</FodyPath> <FileUpgradeFlags> </FileUpgradeFlags> <UpgradeBackupLocation> @@ -58,14 +57,6 @@ <ItemGroup> <!-- A reference to the entire .NET Framework is automatically included --> <None Include="app.config" /> - <None Include="Fody.targets" /> - <None Include="packages.config" /> - </ItemGroup> - <ItemGroup> - <Reference Include="PropertyChanged"> - <HintPath>..\packages\PropertyChanged.Fody.1.41.0.0\Lib\portable-net4+sl4+wp7+win8+MonoAndroid16+MonoTouch40\PropertyChanged.dll</HintPath> - <Private>False</Private> - </Reference> </ItemGroup> <ItemGroup> <Compile Include="..\MediaBrowser.Model\Activity\ActivityLogEntry.cs"> @@ -1254,7 +1245,13 @@ <PostBuildEvent> </PostBuildEvent> </PropertyGroup> - <Import Project="Fody.targets" /> + <Import Project="..\packages\Fody.1.29.2\build\portable-net+sl+win+wpa+wp\Fody.targets" Condition="Exists('..\packages\Fody.1.29.2\build\portable-net+sl+win+wpa+wp\Fody.targets')" /> + <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> + <PropertyGroup> + <ErrorText>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}.</ErrorText> + </PropertyGroup> + <Error Condition="!Exists('..\packages\Fody.1.29.2\build\portable-net+sl+win+wpa+wp\Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Fody.1.29.2\build\portable-net+sl+win+wpa+wp\Fody.targets'))" /> + </Target> <!-- To modify your build process, add your task inside one of the targets below and uncomment it. Other similar extension points exist, see Microsoft.Common.targets. <Target Name="BeforeBuild"> diff --git a/MediaBrowser.Model.Portable/packages.config b/MediaBrowser.Model.Portable/packages.config index 1baa00e28..cd4eb36a4 100644 --- a/MediaBrowser.Model.Portable/packages.config +++ b/MediaBrowser.Model.Portable/packages.config @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> <packages> - <package id="Fody" version="1.19.1.0" targetFramework="portable-win+net45+sl40+wp71" developmentDependency="true" /> - <package id="PropertyChanged.Fody" version="1.41.0.0" targetFramework="portable-net45+sl40+wp71+win" requireReinstallation="True" /> + <package id="Fody" version="1.29.2" targetFramework="portable45-net45+win8+wp8+wpa81" developmentDependency="true" /> + <package id="PropertyChanged.Fody" version="1.50.4" targetFramework="portable45-net45+win8+wp8+wpa81" developmentDependency="true" /> </packages>
\ No newline at end of file diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index 3cb543e5d..5527c1646 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -182,8 +182,6 @@ namespace MediaBrowser.Model.Configuration public PeopleMetadataOptions PeopleMetadataOptions { get; set; } public bool FindInternetTrailers { get; set; } - public string[] InsecureApps9 { get; set; } - public bool SaveMetadataHidden { get; set; } public NameValuePair[] ContentTypes { get; set; } @@ -206,6 +204,8 @@ namespace MediaBrowser.Model.Configuration public int MigrationVersion { get; set; } + public bool DownloadImagesInAdvance { get; set; } + /// <summary> /// Initializes a new instance of the <see cref="ServerConfiguration" /> class. /// </summary> @@ -256,11 +256,6 @@ namespace MediaBrowser.Model.Configuration PeopleMetadataOptions = new PeopleMetadataOptions(); - InsecureApps9 = new[] - { - "Windows Phone" - }; - MetadataOptions = new[] { new MetadataOptions(1, 1280) {ItemType = "Book"}, diff --git a/MediaBrowser.Model/Configuration/UserConfiguration.cs b/MediaBrowser.Model/Configuration/UserConfiguration.cs index d59974a2e..a024eeab4 100644 --- a/MediaBrowser.Model/Configuration/UserConfiguration.cs +++ b/MediaBrowser.Model/Configuration/UserConfiguration.cs @@ -48,11 +48,19 @@ namespace MediaBrowser.Model.Configuration public bool HidePlayedInLatest { get; set; } public bool DisplayChannelsInline { get; set; } + public bool RememberAudioSelections { get; set; } + public bool RememberSubtitleSelections { get; set; } + public bool EnableNextEpisodeAutoPlay { get; set; } + /// <summary> /// Initializes a new instance of the <see cref="UserConfiguration" /> class. /// </summary> public UserConfiguration() { + EnableNextEpisodeAutoPlay = true; + RememberAudioSelections = true; + RememberSubtitleSelections = true; + HidePlayedInLatest = true; PlayDefaultAudioTrack = true; diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index c2713a6e7..d4ca379c0 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -425,6 +425,7 @@ namespace MediaBrowser.Model.Dlna playlistItem.TranscodeSeekInfo = transcodingProfile.TranscodeSeekInfo; playlistItem.AudioCodec = transcodingProfile.AudioCodec.Split(',')[0]; playlistItem.VideoCodec = transcodingProfile.VideoCodec; + playlistItem.CopyTimestamps = transcodingProfile.CopyTimestamps; playlistItem.SubProtocol = transcodingProfile.Protocol; playlistItem.AudioStreamIndex = audioStreamIndex; diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs index f5c54e160..79ee1b5c5 100644 --- a/MediaBrowser.Model/Dlna/StreamInfo.cs +++ b/MediaBrowser.Model/Dlna/StreamInfo.cs @@ -32,6 +32,7 @@ namespace MediaBrowser.Model.Dlna public string VideoProfile { get; set; } public bool? Cabac { get; set; } + public bool CopyTimestamps { get; set; } public string AudioCodec { get; set; } public int? AudioStreamIndex { get; set; } @@ -231,6 +232,8 @@ namespace MediaBrowser.Model.Dlna { list.Add(new NameValuePair("ItemId", item.ItemId)); } + + list.Add(new NameValuePair("CopyTimestamps", (item.CopyTimestamps).ToString().ToLower())); return list; } @@ -269,7 +272,7 @@ namespace MediaBrowser.Model.Dlna // HLS will preserve timestamps so we can just grab the full subtitle stream long startPositionTicks = StringHelper.EqualsIgnoreCase(SubProtocol, "hls") ? 0 - : (this.PlayMethod == PlayMethod.Transcode ? StartPositionTicks : 0); + : (PlayMethod == PlayMethod.Transcode && !CopyTimestamps ? StartPositionTicks : 0); // First add the selected track if (SubtitleStreamIndex.HasValue) diff --git a/MediaBrowser.Model/Dlna/TranscodingProfile.cs b/MediaBrowser.Model/Dlna/TranscodingProfile.cs index d9963eb75..e59ee6d63 100644 --- a/MediaBrowser.Model/Dlna/TranscodingProfile.cs +++ b/MediaBrowser.Model/Dlna/TranscodingProfile.cs @@ -29,6 +29,9 @@ namespace MediaBrowser.Model.Dlna [XmlAttribute("transcodeSeekInfo")] public TranscodeSeekInfo TranscodeSeekInfo { get; set; } + [XmlAttribute("copyTimestamps")] + public bool CopyTimestamps { get; set; } + [XmlAttribute("context")] public EncodingContext Context { get; set; } diff --git a/MediaBrowser.Model/Entities/PersonType.cs b/MediaBrowser.Model/Entities/PersonType.cs index bdf846095..bc274972d 100644 --- a/MediaBrowser.Model/Entities/PersonType.cs +++ b/MediaBrowser.Model/Entities/PersonType.cs @@ -34,5 +34,9 @@ namespace MediaBrowser.Model.Entities /// The conductor /// </summary> public const string Conductor = "Conductor"; + /// <summary> + /// The lyricist + /// </summary> + public const string Lyricist = "Lyricist"; } } diff --git a/MediaBrowser.Model/FileOrganization/EpisodeFileOrganizationRequest.cs b/MediaBrowser.Model/FileOrganization/EpisodeFileOrganizationRequest.cs index 0b12ebc51..b20e43e54 100644 --- a/MediaBrowser.Model/FileOrganization/EpisodeFileOrganizationRequest.cs +++ b/MediaBrowser.Model/FileOrganization/EpisodeFileOrganizationRequest.cs @@ -1,4 +1,6 @@ -namespace MediaBrowser.Model.FileOrganization +using System.Collections.Generic; + +namespace MediaBrowser.Model.FileOrganization { public class EpisodeFileOrganizationRequest { @@ -13,5 +15,12 @@ public int? EndingEpisodeNumber { get; set; } public bool RememberCorrection { get; set; } + public string NewSeriesName { get; set; } + + public string NewSeriesYear { get; set; } + + public string TargetFolder { get; set; } + + public Dictionary<string, string> NewSeriesProviderIds { get; set; } } }
\ No newline at end of file diff --git a/MediaBrowser.Model/Fody.targets b/MediaBrowser.Model/Fody.targets deleted file mode 100644 index a668a51fc..000000000 --- a/MediaBrowser.Model/Fody.targets +++ /dev/null @@ -1,89 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <Choose> - <When Condition="$(NCrunchOriginalSolutionDir) != '' And $(NCrunchOriginalSolutionDir) != '*Undefined*'"> - <PropertyGroup> - <FodySolutionDir>$(NCrunchOriginalSolutionDir)</FodySolutionDir> - </PropertyGroup> - </When> - <When Condition="$(SolutionDir) != '' And $(SolutionDir) != '*Undefined*'"> - <PropertyGroup> - <FodySolutionDir>$(SolutionDir)</FodySolutionDir> - </PropertyGroup> - </When> - <When Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'"> - <PropertyGroup> - <FodySolutionDir>$(MSBuildProjectDirectory)\..\</FodySolutionDir> - </PropertyGroup> - </When> - </Choose> - <Choose> - <When Condition="$(KeyOriginatorFile) != '' And $(KeyOriginatorFile) != '*Undefined*'"> - <PropertyGroup> - <FodyKeyFilePath>$(KeyOriginatorFile)</FodyKeyFilePath> - </PropertyGroup> - </When> - <When Condition="$(AssemblyOriginatorKeyFile) != '' And $(AssemblyOriginatorKeyFile) != '*Undefined*'"> - <PropertyGroup> - <FodyKeyFilePath>$(AssemblyOriginatorKeyFile)</FodyKeyFilePath> - </PropertyGroup> - </When> - <Otherwise > - <PropertyGroup> - <FodyKeyFilePath></FodyKeyFilePath> - </PropertyGroup> - </Otherwise> - </Choose> - <PropertyGroup> - <IntermediateDir>$(ProjectDir)$(IntermediateOutputPath)</IntermediateDir> - <FodyMessageImportance Condition="$(FodyMessageImportance) == '' Or $(FodyMessageImportance) == '*Undefined*'">Low</FodyMessageImportance> - <FodySignAssembly Condition="$(FodySignAssembly) == '' Or $(FodySignAssembly) == '*Undefined*'">$(SignAssembly)</FodySignAssembly> - <FodyPath Condition="$(FodyPath) == '' Or $(FodyPath) == '*Undefined*'">$(MSBuildThisFileDirectory)</FodyPath> - </PropertyGroup> - <UsingTask - TaskName="Fody.WeavingTask" - AssemblyFile="$(FodyPath)\Fody.dll" /> - <Target - AfterTargets="AfterCompile" - Name="WinFodyTarget" - Condition=" '$(OS)' == 'Windows_NT'"> - - <Fody.WeavingTask - AssemblyPath="@(IntermediateAssembly)" - IntermediateDir="$(IntermediateDir)" - KeyFilePath="$(FodyKeyFilePath)" - MessageImportance="$(FodyMessageImportance)" - ProjectDirectory="$(ProjectDir)" - SolutionDir="$(FodySolutionDir)" - References="@(ReferencePath)" - SignAssembly="$(FodySignAssembly)" - ReferenceCopyLocalPaths="@(ReferenceCopyLocalPaths)" - DefineConstants="$(DefineConstants)" - /> - </Target> - - <Target - AfterTargets="AfterBuild" - Name="NonWinFodyTarget" - Condition=" '$(OS)' != 'Windows_NT'"> - <Fody.WeavingTask - AssemblyPath="$(TargetPath)" - IntermediateDir="$(IntermediateDir)" - KeyFilePath="$(FodyKeyFilePath)" - MessageImportance="$(FodyMessageImportance)" - ProjectDirectory="$(ProjectDir)" - SolutionDir="$(FodySolutionDir)" - References="@(ReferencePath)" - SignAssembly="$(FodySignAssembly)" - ReferenceCopyLocalPaths="$(ReferenceCopyLocalPaths)" - DefineConstants="$(DefineConstants)" - /> - </Target> - - - <!--Support for ncrunch--> - <ItemGroup> - <None Include="$(FodyPath)\*.*" /> - </ItemGroup> - -</Project>
\ 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 @@ -<?xml version="1.0" encoding="utf-8"?> +<?xml version="1.0" encoding="utf-8"?> <Weavers> <PropertyChanged /> </Weavers>
\ 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 /// <value>The clients.</value> public List<string> Clients { get; set; } + /// <summary> + /// Gets or sets a value indicating whether this instance can reset. + /// </summary> + /// <value><c>true</c> if this instance can reset; otherwise, <c>false</c>.</value> + public bool CanReset { get; set; } + public LiveTvTunerInfoDto() { Clients = new List<string>(); 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 @@ <AssemblyName>MediaBrowser.Model</AssemblyName> <FileAlignment>512</FileAlignment> <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir> - <FodyPath>..\packages\Fody.1.19.1.0</FodyPath> <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> <ReleaseVersion> </ReleaseVersion> @@ -442,30 +441,30 @@ <Compile Include="Users\UserAction.cs" /> <Compile Include="Users\UserActionType.cs" /> <Compile Include="Users\UserPolicy.cs" /> - <None Include="Fody.targets" /> <None Include="FodyWeavers.xml" /> <None Include="MediaBrowser.Model.snk" /> - <None Include="packages.config" /> </ItemGroup> <ItemGroup> <Reference Include="Microsoft.CSharp" /> <Reference Include="System" /> <Reference Include="System.Core" /> <Reference Include="System.Runtime.Serialization" /> - <Reference Include="PropertyChanged"> - <HintPath>..\packages\PropertyChanged.Fody.1.41.0.0\Lib\NET35\PropertyChanged.dll</HintPath> - <Private>False</Private> - </Reference> <Reference Include="System.Xml" /> </ItemGroup> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <PropertyGroup> <PostBuildEvent /> </PropertyGroup> - <Import Project="Fody.targets" /> <PropertyGroup> <PostBuildEvent /> </PropertyGroup> + <Import Project="..\packages\Fody.1.29.2\build\dotnet\Fody.targets" Condition="Exists('..\packages\Fody.1.29.2\build\dotnet\Fody.targets')" /> + <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> + <PropertyGroup> + <ErrorText>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}.</ErrorText> + </PropertyGroup> + <Error Condition="!Exists('..\packages\Fody.1.29.2\build\dotnet\Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Fody.1.29.2\build\dotnet\Fody.targets'))" /> + </Target> <!-- To modify your build process, add your task inside one of the targets below and uncomment it. Other similar extension points exist, see Microsoft.Common.targets. <Target Name="BeforeBuild"> diff --git a/MediaBrowser.Model/MediaInfo/MediaInfo.cs b/MediaBrowser.Model/MediaInfo/MediaInfo.cs index 21f258693..de082635d 100644 --- a/MediaBrowser.Model/MediaInfo/MediaInfo.cs +++ b/MediaBrowser.Model/MediaInfo/MediaInfo.cs @@ -10,11 +10,6 @@ namespace MediaBrowser.Model.MediaInfo public List<ChapterInfo> Chapters { get; set; } /// <summary> - /// Gets or sets the title. - /// </summary> - /// <value>The title.</value> - public string Title { get; set; } - /// <summary> /// Gets or sets the album. /// </summary> /// <value>The album.</value> @@ -47,10 +42,20 @@ namespace MediaBrowser.Model.MediaInfo /// <value>The official rating.</value> public string OfficialRating { get; set; } /// <summary> + /// Gets or sets the official rating description. + /// </summary> + /// <value>The official rating description.</value> + public string OfficialRatingDescription { get; set; } + /// <summary> /// Gets or sets the overview. /// </summary> /// <value>The overview.</value> public string Overview { get; set; } + /// <summary> + /// Gets or sets the short overview. + /// </summary> + /// <value>The short overview.</value> + public string ShortOverview { get; set; } public MediaInfo() { diff --git a/MediaBrowser.Model/packages.config b/MediaBrowser.Model/packages.config index 3d7793afb..df2ddcf0f 100644 --- a/MediaBrowser.Model/packages.config +++ b/MediaBrowser.Model/packages.config @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> <packages> - <package id="Fody" version="1.19.1.0" targetFramework="net45" developmentDependency="true" /> - <package id="PropertyChanged.Fody" version="1.41.0.0" targetFramework="net45" /> + <package id="Fody" version="1.29.2" targetFramework="net45" developmentDependency="true" /> + <package id="PropertyChanged.Fody" version="1.50.4" targetFramework="net45" developmentDependency="true" /> </packages>
\ No newline at end of file diff --git a/MediaBrowser.Providers/Folders/DefaultImageProvider.cs b/MediaBrowser.Providers/Folders/DefaultImageProvider.cs index a5a2e1e72..ca543163d 100644 --- a/MediaBrowser.Providers/Folders/DefaultImageProvider.cs +++ b/MediaBrowser.Providers/Folders/DefaultImageProvider.cs @@ -25,8 +25,7 @@ namespace MediaBrowser.Providers.Folders { return new List<ImageType> { - ImageType.Primary, - ImageType.Thumb + ImageType.Primary }; } @@ -57,13 +56,6 @@ namespace MediaBrowser.Providers.Folders ProviderName = Name, Url = url, Type = ImageType.Primary - }, - - new RemoteImageInfo - { - ProviderName = Name, - Url = url, - Type = ImageType.Thumb } }); } @@ -121,7 +113,7 @@ namespace MediaBrowser.Providers.Folders } if (string.Equals(viewType, CollectionType.BoxSets, StringComparison.OrdinalIgnoreCase)) { - return urlPrefix + "collections.jpg"; + //return urlPrefix + "collections.jpg"; } if (string.IsNullOrWhiteSpace(viewType)) { diff --git a/MediaBrowser.Providers/Manager/ItemImageProvider.cs b/MediaBrowser.Providers/Manager/ItemImageProvider.cs index 370187801..73e09c1e6 100644 --- a/MediaBrowser.Providers/Manager/ItemImageProvider.cs +++ b/MediaBrowser.Providers/Manager/ItemImageProvider.cs @@ -18,6 +18,7 @@ using System.Threading; using System.Threading.Tasks; using CommonIO; using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.LiveTv; using MediaBrowser.Model.MediaInfo; namespace MediaBrowser.Providers.Manager @@ -520,6 +521,16 @@ namespace MediaBrowser.Providers.Manager private bool EnableImageStub(IHasImages item, ImageType type) { + if (item is LiveTvProgram) + { + return true; + } + + if (_config.Configuration.DownloadImagesInAdvance) + { + return false; + } + if (item.LocationType == LocationType.Remote || item.LocationType == LocationType.Virtual) { return true; diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs index e18da565d..416cc51bd 100644 --- a/MediaBrowser.Providers/Manager/MetadataService.cs +++ b/MediaBrowser.Providers/Manager/MetadataService.cs @@ -283,12 +283,7 @@ namespace MediaBrowser.Providers.Manager if (!string.IsNullOrWhiteSpace(person.ImageUrl) && !personEntity.HasImage(ImageType.Primary)) { - personEntity.SetImage(new ItemImageInfo - { - Path = person.ImageUrl, - Type = ImageType.Primary, - IsPlaceholder = true - }, 0); + await AddPersonImage(personEntity, person.ImageUrl, cancellationToken).ConfigureAwait(false); saveEntity = true; updateType = updateType | ItemUpdateType.ImageUpdate; @@ -302,6 +297,23 @@ namespace MediaBrowser.Providers.Manager } } + private async Task AddPersonImage(Person personEntity, string imageUrl, CancellationToken cancellationToken) + { + if (ServerConfigurationManager.Configuration.DownloadImagesInAdvance) + { + await ProviderManager.SaveImage(personEntity, imageUrl, null, ImageType.Primary, null, cancellationToken).ConfigureAwait(false); + } + else + { + personEntity.SetImage(new ItemImageInfo + { + Path = imageUrl, + Type = ImageType.Primary, + IsPlaceholder = true + }, 0); + } + } + private readonly Task _cachedTask = Task.FromResult(true); protected virtual Task AfterMetadataRefresh(TItemType item, MetadataRefreshOptions refreshOptions, CancellationToken cancellationToken) { diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 24397dd5a..d316d2cd8 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -50,7 +50,7 @@ </Reference> <Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\CommonIO.1.0.0.7\lib\net45\CommonIO.dll</HintPath> + <HintPath>..\packages\CommonIO.1.0.0.8\lib\net45\CommonIO.dll</HintPath> </Reference> <Reference Include="DvdLib, Version=1.0.5167.21152, Culture=neutral, PublicKeyToken=7a2f3f5ec8d93575, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs index 4cf507d15..78906fa85 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs @@ -125,9 +125,9 @@ namespace MediaBrowser.Providers.MediaInfo private async Task FetchDataFromTags(Audio audio, Model.MediaInfo.MediaInfo data) { // Only set Name if title was found in the dictionary - if (!string.IsNullOrEmpty(data.Title)) + if (!string.IsNullOrEmpty(data.Name)) { - audio.Name = data.Title; + audio.Name = data.Name; } if (!audio.LockedFields.Contains(MetadataFields.Cast)) diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs index ebbc045ab..ee05a89a8 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs @@ -383,6 +383,11 @@ namespace MediaBrowser.Providers.MediaInfo } } + if (!string.IsNullOrWhiteSpace(data.OfficialRatingDescription) || isFullRefresh) + { + video.OfficialRatingDescription = data.OfficialRatingDescription; + } + if (!video.LockedFields.Contains(MetadataFields.Genres)) { if (video.Genres.Count == 0 || isFullRefresh) @@ -437,6 +442,13 @@ namespace MediaBrowser.Providers.MediaInfo video.ParentIndexNumber = data.ParentIndexNumber; } } + if (!string.IsNullOrWhiteSpace(data.Name)) + { + if (string.IsNullOrWhiteSpace(video.Name) || string.Equals(video.Name, Path.GetFileNameWithoutExtension(video.Path), StringComparison.OrdinalIgnoreCase)) + { + video.Name = data.Name; + } + } // If we don't have a ProductionYear try and get it from PremiereDate if (video.PremiereDate.HasValue && !video.ProductionYear.HasValue) @@ -451,6 +463,11 @@ namespace MediaBrowser.Providers.MediaInfo video.Overview = data.Overview; } } + + if (string.IsNullOrWhiteSpace(video.ShortOverview) || isFullRefresh) + { + video.ShortOverview = data.ShortOverview; + } } private async Task FetchPeople(Video video, Model.MediaInfo.MediaInfo data, MetadataRefreshOptions options) diff --git a/MediaBrowser.Providers/People/MovieDbPersonProvider.cs b/MediaBrowser.Providers/People/MovieDbPersonProvider.cs index 5525b547f..43302dd89 100644 --- a/MediaBrowser.Providers/People/MovieDbPersonProvider.cs +++ b/MediaBrowser.Providers/People/MovieDbPersonProvider.cs @@ -99,7 +99,7 @@ namespace MediaBrowser.Providers.People var requestCount = _requestCount; - if (requestCount >= 10) + if (requestCount >= 20) { //_logger.Debug("Throttling Tmdb people"); diff --git a/MediaBrowser.Providers/packages.config b/MediaBrowser.Providers/packages.config index 08f8ef3b0..89172044a 100644 --- a/MediaBrowser.Providers/packages.config +++ b/MediaBrowser.Providers/packages.config @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <packages> - <package id="CommonIO" version="1.0.0.7" targetFramework="net45" /> + <package id="CommonIO" version="1.0.0.8" targetFramework="net45" /> <package id="MediaBrowser.BdInfo" version="1.0.0.10" targetFramework="net45" /> <package id="morelinq" version="1.4.0" targetFramework="net45" /> <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" /> diff --git a/MediaBrowser.Server.Implementations/Connect/ConnectManager.cs b/MediaBrowser.Server.Implementations/Connect/ConnectManager.cs index fdc7e9ee2..d7477225c 100644 --- a/MediaBrowser.Server.Implementations/Connect/ConnectManager.cs +++ b/MediaBrowser.Server.Implementations/Connect/ConnectManager.cs @@ -10,7 +10,6 @@ using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Security; using MediaBrowser.Model.Connect; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Events; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Net; using MediaBrowser.Model.Serialization; @@ -24,7 +23,6 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using CommonIO; -using MediaBrowser.Common.IO; namespace MediaBrowser.Server.Implementations.Connect { @@ -121,7 +119,6 @@ namespace MediaBrowser.Server.Implementations.Connect _securityManager = securityManager; _fileSystem = fileSystem; - _userManager.UserConfigurationUpdated += _userManager_UserConfigurationUpdated; _config.ConfigurationUpdated += _config_ConfigurationUpdated; LoadCachedData(); @@ -1071,90 +1068,6 @@ namespace MediaBrowser.Server.Implementations.Connect } } - public async Task<ConnectSupporterSummary> GetConnectSupporterSummary() - { - var url = GetConnectUrl("keyAssociation"); - - var options = new HttpRequestOptions - { - Url = url, - CancellationToken = CancellationToken.None - }; - - var postData = new Dictionary<string, string> - { - {"serverId", ConnectServerId}, - {"supporterKey", _securityManager.SupporterKey} - }; - - options.SetPostData(postData); - - SetServerAccessToken(options); - SetApplicationHeader(options); - - // No need to examine the response - using (var stream = (await _httpClient.SendAsync(options, "POST").ConfigureAwait(false)).Content) - { - return _json.DeserializeFromStream<ConnectSupporterSummary>(stream); - } - } - - public async Task AddConnectSupporter(string id) - { - var url = GetConnectUrl("keyAssociation"); - - var options = new HttpRequestOptions - { - Url = url, - CancellationToken = CancellationToken.None - }; - - var postData = new Dictionary<string, string> - { - {"serverId", ConnectServerId}, - {"supporterKey", _securityManager.SupporterKey}, - {"userId", id} - }; - - options.SetPostData(postData); - - SetServerAccessToken(options); - SetApplicationHeader(options); - - // No need to examine the response - using (var stream = (await _httpClient.SendAsync(options, "POST").ConfigureAwait(false)).Content) - { - } - } - - public async Task RemoveConnectSupporter(string id) - { - var url = GetConnectUrl("keyAssociation"); - - var options = new HttpRequestOptions - { - Url = url, - CancellationToken = CancellationToken.None - }; - - var postData = new Dictionary<string, string> - { - {"serverId", ConnectServerId}, - {"supporterKey", _securityManager.SupporterKey}, - {"userId", id} - }; - - options.SetPostData(postData); - - SetServerAccessToken(options); - SetApplicationHeader(options); - - // No need to examine the response - using (var stream = (await _httpClient.SendAsync(options, "DELETE").ConfigureAwait(false)).Content) - { - } - } - public async Task Authenticate(string username, string passwordMd5) { if (string.IsNullOrWhiteSpace(username)) @@ -1186,64 +1099,6 @@ namespace MediaBrowser.Server.Implementations.Connect } } - async void _userManager_UserConfigurationUpdated(object sender, GenericEventArgs<User> e) - { - var user = e.Argument; - - await TryUploadUserPreferences(user, CancellationToken.None).ConfigureAwait(false); - } - - private async Task TryUploadUserPreferences(User user, CancellationToken cancellationToken) - { - if (user == null) - { - throw new ArgumentNullException("user"); - } - - if (string.IsNullOrEmpty(user.ConnectUserId)) - { - return; - } - if (string.IsNullOrEmpty(ConnectAccessKey)) - { - return; - } - - var url = GetConnectUrl("user/preferences"); - url += "?userId=" + user.ConnectUserId; - url += "&key=userpreferences"; - - var options = new HttpRequestOptions - { - Url = url, - CancellationToken = cancellationToken - }; - - var postData = new Dictionary<string, string>(); - postData["data"] = _json.SerializeToString(ConnectUserPreferences.FromUserConfiguration(user.Configuration)); - options.SetPostData(postData); - - SetServerAccessToken(options); - SetApplicationHeader(options); - - try - { - // No need to examine the response - using (var stream = (await _httpClient.SendAsync(options, "POST").ConfigureAwait(false)).Content) - { - } - } - catch (Exception ex) - { - _logger.ErrorException("Error uploading user preferences", ex); - } - } - - private async Task DownloadUserPreferences(User user, CancellationToken cancellationToken) - { - - } - public async Task<User> GetLocalUser(string connectUserId) { var user = _userManager.Users diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs index 590c5fd3f..a19a122c3 100644 --- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs +++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs @@ -26,6 +26,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading.Tasks; using CommonIO; namespace MediaBrowser.Server.Implementations.Dto @@ -92,11 +93,17 @@ namespace MediaBrowser.Server.Implementations.Dto var syncDictionary = GetSyncedItemProgressDictionary(syncJobItems); var list = new List<BaseItemDto>(); + var programTuples = new List<Tuple<BaseItem, BaseItemDto>> { }; foreach (var item in items) { var dto = GetBaseItemDtoInternal(item, options, syncDictionary, user, owner); + if (item is LiveTvProgram) + { + programTuples.Add(new Tuple<BaseItem, BaseItemDto>(item, dto)); + } + var byName = item as IItemByName; if (byName != null) @@ -118,6 +125,12 @@ namespace MediaBrowser.Server.Implementations.Dto list.Add(dto); } + if (programTuples.Count > 0) + { + var task = _livetvManager().AddInfoToProgramDto(programTuples, options.Fields, user); + Task.WaitAll(task); + } + return list; } @@ -139,6 +152,13 @@ namespace MediaBrowser.Server.Implementations.Dto var dto = GetBaseItemDtoInternal(item, options, GetSyncedItemProgressDictionary(syncProgress), user, owner); + if (item is LiveTvProgram) + { + var list = new List<Tuple<BaseItem, BaseItemDto>> { new Tuple<BaseItem, BaseItemDto>(item, dto) }; + var task = _livetvManager().AddInfoToProgramDto(list, options.Fields, user); + Task.WaitAll(task); + } + var byName = item as IItemByName; if (byName != null) @@ -393,11 +413,6 @@ namespace MediaBrowser.Server.Implementations.Dto _livetvManager().AddInfoToRecordingDto(item, dto, user); } - else if (item is LiveTvProgram) - { - _livetvManager().AddInfoToProgramDto(item, dto, fields, user); - } - return dto; } @@ -639,6 +654,8 @@ namespace MediaBrowser.Server.Implementations.Dto private IEnumerable<string> GetCacheTags(BaseItem item, ImageType type, int limit) { return item.GetImages(type) + // Convert to a list now in case GetImageCacheTag is slow + .ToList() .Select(p => GetImageCacheTag(item, p)) .Where(i => i != null) .Take(limit) diff --git a/MediaBrowser.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/MediaBrowser.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs index 51a104241..703096a2b 100644 --- a/MediaBrowser.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs +++ b/MediaBrowser.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs @@ -213,8 +213,18 @@ namespace MediaBrowser.Server.Implementations.EntryPoints if (userSessions.Count > 0) { - var info = GetLibraryUpdateInfo(itemsAdded, itemsUpdated, itemsRemoved, foldersAddedTo, - foldersRemovedFrom, id); + LibraryUpdateInfo info; + + try + { + info = GetLibraryUpdateInfo(itemsAdded, itemsUpdated, itemsRemoved, foldersAddedTo, + foldersRemovedFrom, id); + } + catch (Exception ex) + { + _logger.ErrorException("Error in GetLibraryUpdateInfo", ex); + return; + } foreach (var userSession in userSessions) { diff --git a/MediaBrowser.Server.Implementations/EntryPoints/UsageReporter.cs b/MediaBrowser.Server.Implementations/EntryPoints/UsageReporter.cs index be2817fd2..2473f3af6 100644 --- a/MediaBrowser.Server.Implementations/EntryPoints/UsageReporter.cs +++ b/MediaBrowser.Server.Implementations/EntryPoints/UsageReporter.cs @@ -57,7 +57,9 @@ namespace MediaBrowser.Server.Implementations.EntryPoints CancellationToken = cancellationToken, // Seeing block length errors - EnableHttpCompression = false + EnableHttpCompression = false, + + LogRequest = false }; options.SetPostData(data); @@ -99,7 +101,9 @@ namespace MediaBrowser.Server.Implementations.EntryPoints CancellationToken = cancellationToken, // Seeing block length errors - EnableHttpCompression = false + EnableHttpCompression = false, + + LogRequest = false }; options.SetPostData(data); diff --git a/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs b/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs index 9d43dabcd..f9e167a8f 100644 --- a/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs +++ b/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs @@ -64,6 +64,13 @@ namespace MediaBrowser.Server.Implementations.FileOrganization FileSize = new FileInfo(path).Length }; + if (_libraryMonitor.IsPathLocked(path)) + { + result.Status = FileSortingStatus.Failure; + result.StatusMessage = "Path is locked by other processes. Please try again later."; + return result; + } + var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions(); var resolver = new Naming.TV.EpisodeResolver(namingOptions, new PatternsLogger()); @@ -135,7 +142,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization if (previousResult != null) { // Don't keep saving the same result over and over if nothing has changed - if (previousResult.Status == result.Status && result.Status != FileSortingStatus.Success) + if (previousResult.Status == result.Status && previousResult.StatusMessage == result.StatusMessage && result.Status != FileSortingStatus.Success) { return previousResult; } @@ -150,7 +157,43 @@ namespace MediaBrowser.Server.Implementations.FileOrganization { var result = _organizationService.GetResult(request.ResultId); - var series = (Series)_libraryManager.GetItemById(new Guid(request.SeriesId)); + Series series = null; + + if (request.NewSeriesProviderIds.Count > 0) + { + // We're having a new series here + SeriesInfo seriesRequest = new SeriesInfo(); + seriesRequest.ProviderIds = request.NewSeriesProviderIds; + + var refreshOptions = new MetadataRefreshOptions(_fileSystem); + series = new Series(); + series.Id = Guid.NewGuid(); + series.Name = request.NewSeriesName; + + int year; + if (int.TryParse(request.NewSeriesYear, out year)) + { + series.ProductionYear = year; + } + + var seriesFolderName = series.Name; + if (series.ProductionYear.HasValue) + { + seriesFolderName = string.Format("{0} ({1})", seriesFolderName, series.ProductionYear); + } + + series.Path = Path.Combine(request.TargetFolder, seriesFolderName); + + series.ProviderIds = request.NewSeriesProviderIds; + + await series.RefreshMetadata(refreshOptions, cancellationToken); + } + + if (series == null) + { + // Existing Series + series = (Series)_libraryManager.GetItemById(new Guid(request.SeriesId)); + } await OrganizeEpisode(result.OriginalPath, series, @@ -243,16 +286,29 @@ namespace MediaBrowser.Server.Implementations.FileOrganization { if (options.TvOptions.CopyOriginalFile && fileExists && IsSameEpisode(sourcePath, newPath)) { - _logger.Info("File {0} already copied to new path {1}, stopping organization", sourcePath, newPath); + var msg = string.Format("File '{0}' already copied to new path '{1}', stopping organization", sourcePath, newPath); + _logger.Info(msg); + result.Status = FileSortingStatus.SkippedExisting; + result.StatusMessage = msg; + return; + } + + if (fileExists) + { + var msg = string.Format("File '{0}' already exists as '{1}', stopping organization", sourcePath, newPath); + _logger.Info(msg); result.Status = FileSortingStatus.SkippedExisting; - result.StatusMessage = string.Empty; + result.StatusMessage = msg; + result.TargetPath = newPath; return; } - if (fileExists || otherDuplicatePaths.Count > 0) + if (otherDuplicatePaths.Count > 0) { + var msg = string.Format("File '{0}' already exists as '{1}', stopping organization", sourcePath, otherDuplicatePaths); + _logger.Info(msg); result.Status = FileSortingStatus.SkippedExisting; - result.StatusMessage = string.Empty; + result.StatusMessage = msg; result.DuplicatePaths = otherDuplicatePaths; return; } @@ -502,7 +558,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization } } - return series ?? new Series(); + return series; } /// <summary> diff --git a/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs b/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs index e69ff367a..c284007f7 100644 --- a/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -348,6 +348,12 @@ namespace MediaBrowser.Server.Implementations.HttpServer return Task.FromResult(true); } + if (string.Equals(localPath, "/emby/pin", StringComparison.OrdinalIgnoreCase)) + { + httpRes.RedirectToUrl("web/pin.html"); + return Task.FromResult(true); + } + if (!string.IsNullOrWhiteSpace(GlobalResponse)) { httpRes.StatusCode = 503; diff --git a/MediaBrowser.Server.Implementations/HttpServer/Security/AuthService.cs b/MediaBrowser.Server.Implementations/HttpServer/Security/AuthService.cs index f6b14fcab..d8f7d889c 100644 --- a/MediaBrowser.Server.Implementations/HttpServer/Security/AuthService.cs +++ b/MediaBrowser.Server.Implementations/HttpServer/Security/AuthService.cs @@ -134,20 +134,17 @@ namespace MediaBrowser.Server.Implementations.HttpServer.Security private bool IsExemptFromAuthenticationToken(AuthorizationInfo auth, IAuthenticationAttributes authAttribtues) { - if (!_config.Configuration.IsStartupWizardCompleted && - authAttribtues.AllowBeforeStartupWizard) + if (!_config.Configuration.IsStartupWizardCompleted && authAttribtues.AllowBeforeStartupWizard) { return true; } - return _config.Configuration.InsecureApps9.Contains(auth.Client ?? string.Empty, - StringComparer.OrdinalIgnoreCase); + return false; } private bool IsExemptFromRoles(AuthorizationInfo auth, IAuthenticationAttributes authAttribtues, AuthenticationInfo tokenInfo) { - if (!_config.Configuration.IsStartupWizardCompleted && - authAttribtues.AllowBeforeStartupWizard) + if (!_config.Configuration.IsStartupWizardCompleted && authAttribtues.AllowBeforeStartupWizard) { return true; } diff --git a/MediaBrowser.Server.Implementations/HttpServer/Security/AuthorizationContext.cs b/MediaBrowser.Server.Implementations/HttpServer/Security/AuthorizationContext.cs index 75d54a80a..357f5c976 100644 --- a/MediaBrowser.Server.Implementations/HttpServer/Security/AuthorizationContext.cs +++ b/MediaBrowser.Server.Implementations/HttpServer/Security/AuthorizationContext.cs @@ -45,7 +45,6 @@ namespace MediaBrowser.Server.Implementations.HttpServer.Security { var auth = GetAuthorizationDictionary(httpReq); - string userId = null; string deviceId = null; string device = null; string client = null; @@ -53,9 +52,6 @@ namespace MediaBrowser.Server.Implementations.HttpServer.Security if (auth != null) { - // TODO: Remove this - auth.TryGetValue("UserId", out userId); - auth.TryGetValue("DeviceId", out deviceId); auth.TryGetValue("Device", out device); auth.TryGetValue("Client", out client); @@ -78,7 +74,6 @@ namespace MediaBrowser.Server.Implementations.HttpServer.Security Client = client, Device = device, DeviceId = deviceId, - UserId = userId, Version = version, Token = token }; diff --git a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs index 764eb7c68..0559e08ea 100644 --- a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs +++ b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs @@ -78,6 +78,12 @@ namespace MediaBrowser.Server.Implementations.IO TemporarilyIgnore(path); } + public bool IsPathLocked(string path) + { + var lockedPaths = _tempIgnoredPaths.Keys.ToList(); + return lockedPaths.Any(i => string.Equals(i, path, StringComparison.OrdinalIgnoreCase) || _fileSystem.ContainsSubPath(i, path)); + } + public async void ReportFileSystemChangeComplete(string path, bool refreshPath) { if (string.IsNullOrEmpty(path)) @@ -658,7 +664,7 @@ namespace MediaBrowser.Server.Implementations.IO while (item == null && !string.IsNullOrEmpty(path)) { - item = LibraryManager.RootFolder.FindByPath(path); + item = LibraryManager.FindByPath(path); path = Path.GetDirectoryName(path); } @@ -690,8 +696,21 @@ namespace MediaBrowser.Server.Implementations.IO foreach (var watcher in _fileSystemWatchers.Values.ToList()) { + watcher.Created -= watcher_Changed; + watcher.Deleted -= watcher_Changed; + watcher.Renamed -= watcher_Changed; watcher.Changed -= watcher_Changed; - watcher.EnableRaisingEvents = false; + + try + { + watcher.EnableRaisingEvents = false; + } + catch (InvalidOperationException) + { + // Seeing this under mono on linux sometimes + // Collection was modified; enumeration operation may not execute. + } + watcher.Dispose(); } diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index 333b1fbe9..721603efe 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -788,6 +788,29 @@ namespace MediaBrowser.Server.Implementations.Library return _userRootFolder; } + public BaseItem FindByPath(string path) + { + var query = new InternalItemsQuery + { + Path = path + }; + + // Only use the database result if there's exactly one item, otherwise we run the risk of returning old data that hasn't been cleaned yet. + var items = GetItemIds(query).Select(GetItemById).Where(i => i != null).ToArray(); + + if (items.Length == 1) + { + return items[0]; + } + + if (items.Length == 0) + { + return null; + } + + return RootFolder.FindByPath(path); + } + /// <summary> /// Gets a Person /// </summary> diff --git a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs index b132eedec..e4a085f42 100644 --- a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs +++ b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs @@ -30,8 +30,9 @@ namespace MediaBrowser.Server.Implementations.Library private IMediaSourceProvider[] _providers; private readonly ILogger _logger; + private readonly IUserDataManager _userDataManager; - public MediaSourceManager(IItemRepository itemRepo, IUserManager userManager, ILibraryManager libraryManager, ILogger logger, IJsonSerializer jsonSerializer, IFileSystem fileSystem) + public MediaSourceManager(IItemRepository itemRepo, IUserManager userManager, ILibraryManager libraryManager, ILogger logger, IJsonSerializer jsonSerializer, IFileSystem fileSystem, IUserDataManager userDataManager) { _itemRepo = itemRepo; _userManager = userManager; @@ -39,6 +40,7 @@ namespace MediaBrowser.Server.Implementations.Library _logger = logger; _jsonSerializer = jsonSerializer; _fileSystem = fileSystem; + _userDataManager = userDataManager; } public void AddParts(IEnumerable<IMediaSourceProvider> providers) @@ -140,7 +142,7 @@ namespace MediaBrowser.Server.Implementations.Library { if (user != null) { - SetUserProperties(source, user); + SetUserProperties(hasMediaSources, source, user); } if (source.Protocol == MediaProtocol.File) { @@ -257,25 +259,38 @@ namespace MediaBrowser.Server.Implementations.Library { foreach (var source in sources) { - SetUserProperties(source, user); + SetUserProperties(item, source, user); } } return sources; } - private void SetUserProperties(MediaSourceInfo source, User user) + private void SetUserProperties(IHasUserData item, MediaSourceInfo source, User user) { - var preferredAudio = string.IsNullOrEmpty(user.Configuration.AudioLanguagePreference) - ? new string[] { } - : new[] { user.Configuration.AudioLanguagePreference }; + var userData = item == null ? new UserItemData() : _userDataManager.GetUserData(user.Id, item.GetUserDataKey()); + + SetDefaultAudioStreamIndex(source, userData, user); + SetDefaultSubtitleStreamIndex(source, userData, user); + } + private void SetDefaultSubtitleStreamIndex(MediaSourceInfo source, UserItemData userData, User user) + { + if (userData.SubtitleStreamIndex.HasValue && user.Configuration.RememberSubtitleSelections) + { + var index = userData.SubtitleStreamIndex.Value; + // Make sure the saved index is still valid + if (index == -1 || source.MediaStreams.Any(i => i.Type == MediaStreamType.Subtitle && i.Index == index)) + { + source.DefaultSubtitleStreamIndex = index; + return; + } + } + var preferredSubs = string.IsNullOrEmpty(user.Configuration.SubtitleLanguagePreference) ? new List<string> { } : new List<string> { user.Configuration.SubtitleLanguagePreference }; - source.DefaultAudioStreamIndex = MediaStreamSelector.GetDefaultAudioStreamIndex(source.MediaStreams, preferredAudio, user.Configuration.PlayDefaultAudioTrack); - var defaultAudioIndex = source.DefaultAudioStreamIndex; var audioLangage = defaultAudioIndex == null ? null @@ -290,6 +305,26 @@ namespace MediaBrowser.Server.Implementations.Library user.Configuration.SubtitleMode, audioLangage); } + private void SetDefaultAudioStreamIndex(MediaSourceInfo source, UserItemData userData, User user) + { + if (userData.AudioStreamIndex.HasValue && user.Configuration.RememberAudioSelections) + { + var index = userData.AudioStreamIndex.Value; + // Make sure the saved index is still valid + if (source.MediaStreams.Any(i => i.Type == MediaStreamType.Audio && i.Index == index)) + { + source.DefaultAudioStreamIndex = index; + return; + } + } + + var preferredAudio = string.IsNullOrEmpty(user.Configuration.AudioLanguagePreference) + ? new string[] { } + : new[] { user.Configuration.AudioLanguagePreference }; + + source.DefaultAudioStreamIndex = MediaStreamSelector.GetDefaultAudioStreamIndex(source.MediaStreams, preferredAudio, user.Configuration.PlayDefaultAudioTrack); + } + private IEnumerable<MediaSourceInfo> SortMediaSources(IEnumerable<MediaSourceInfo> sources) { return sources.OrderBy(i => @@ -349,11 +384,14 @@ namespace MediaBrowser.Server.Implementations.Library var json = _jsonSerializer.SerializeToString(mediaSource); _logger.Debug("Live stream opened: " + json); var clone = _jsonSerializer.DeserializeFromString<MediaSourceInfo>(json); - + if (!string.IsNullOrWhiteSpace(request.UserId)) { var user = _userManager.GetUserById(request.UserId); - SetUserProperties(clone, user); + var item = string.IsNullOrWhiteSpace(request.ItemId) + ? null + : _libraryManager.GetItemById(request.ItemId); + SetUserProperties(item, clone, user); } return new LiveStreamResponse diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs index 23424bf3c..c73470b51 100644 --- a/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs +++ b/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs @@ -78,7 +78,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies } if (string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase) || - string.Equals(collectionType, CollectionType.Photos, StringComparison.OrdinalIgnoreCase)) + string.Equals(collectionType, CollectionType.Photos, StringComparison.OrdinalIgnoreCase)) { return ResolveVideos<Video>(parent, files, directoryService, false); } @@ -137,7 +137,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies var resolverResult = resolver.Resolve(files.Select(i => new FileMetadata { Id = i.FullName, - IsFolder = false + IsFolder = i.IsDirectory }).ToList(), suppportMultiEditions).ToList(); @@ -169,9 +169,28 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies result.Items.Add(videoItem); } + result.ExtraFiles.AddRange(files.Where(i => !ContainsFile(resolverResult, i))); + return result; } + private bool ContainsFile(List<VideoInfo> result, FileSystemMetadata file) + { + return result.Any(i => ContainsFile(i, file)); + } + + private bool ContainsFile(VideoInfo result, FileSystemMetadata file) + { + return result.Files.Any(i => ContainsFile(i, file)) || + result.AlternateVersions.Any(i => ContainsFile(i, file)) || + result.Extras.Any(i => ContainsFile(i, file)); + } + + private bool ContainsFile(VideoFileInfo result, FileSystemMetadata file) + { + return string.Equals(result.Path, file.FullName, StringComparison.OrdinalIgnoreCase); + } + /// <summary> /// Resolves the specified args. /// </summary> diff --git a/MediaBrowser.Server.Implementations/Library/Validators/PeopleValidator.cs b/MediaBrowser.Server.Implementations/Library/Validators/PeopleValidator.cs index 2161c1454..884e1e322 100644 --- a/MediaBrowser.Server.Implementations/Library/Validators/PeopleValidator.cs +++ b/MediaBrowser.Server.Implementations/Library/Validators/PeopleValidator.cs @@ -125,10 +125,21 @@ namespace MediaBrowser.Server.Implementations.Library.Validators validIds.Add(item.Id); + var hasMetdata = !string.IsNullOrWhiteSpace(item.Overview); + var performFullRefresh = !hasMetdata && (DateTime.UtcNow - item.DateLastRefreshed).TotalDays >= 30; + + var defaultMetadataRefreshMode = performFullRefresh + ? MetadataRefreshMode.FullRefresh + : MetadataRefreshMode.Default; + + var imageRefreshMode = performFullRefresh + ? ImageRefreshMode.FullRefresh + : ImageRefreshMode.Default; + var options = new MetadataRefreshOptions(_fileSystem) { - MetadataRefreshMode = person.Value ? MetadataRefreshMode.Default : MetadataRefreshMode.ValidationOnly, - ImageRefreshMode = person.Value ? ImageRefreshMode.Default : ImageRefreshMode.ValidationOnly + MetadataRefreshMode = person.Value ? defaultMetadataRefreshMode : MetadataRefreshMode.ValidationOnly, + ImageRefreshMode = person.Value ? imageRefreshMode : ImageRefreshMode.ValidationOnly }; await item.RefreshMetadata(options, cancellationToken).ConfigureAwait(false); diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs index 9ac96165f..d33b2c51d 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs @@ -23,7 +23,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV _fileSystem = fileSystem; } - public async Task Record(MediaSourceInfo mediaSource, string targetFile, Action onStarted, CancellationToken cancellationToken) + public async Task Record(MediaSourceInfo mediaSource, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken) { var httpRequestOptions = new HttpRequestOptions() { @@ -42,7 +42,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV _logger.Info("Copying recording stream to file stream"); - await response.Content.CopyToAsync(output, StreamDefaults.DefaultCopyToBufferSize, cancellationToken).ConfigureAwait(false); + var durationToken = new CancellationTokenSource(duration); + var linkedToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token; + + await response.Content.CopyToAsync(output, StreamDefaults.DefaultCopyToBufferSize, linkedToken).ConfigureAwait(false); } } } diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 9e4cb66a8..3ab08274c 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -87,7 +87,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV _timerProvider.RestartTimers(); SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged; - } void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e) @@ -104,8 +103,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV public event EventHandler<RecordingStatusChangedEventArgs> RecordingStatusChanged; - private readonly ConcurrentDictionary<string, CancellationTokenSource> _activeRecordings = - new ConcurrentDictionary<string, CancellationTokenSource>(StringComparer.OrdinalIgnoreCase); + private readonly ConcurrentDictionary<string, ActiveRecordingInfo> _activeRecordings = + new ConcurrentDictionary<string, ActiveRecordingInfo>(StringComparer.OrdinalIgnoreCase); public string Name { @@ -210,9 +209,13 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV } } - if (list.Count > 0) + foreach (var provider in GetListingProviders()) { - foreach (var provider in GetListingProviders()) + var enabledChannels = list + .Where(i => IsListingProviderEnabledForTuner(provider.Item2, i.TunerHostId)) + .ToList(); + + if (enabledChannels.Count > 0) { try { @@ -228,6 +231,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV } } } + _channelCache = list; return list; } @@ -264,11 +268,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV { _timerProvider.Delete(remove); } - CancellationTokenSource cancellationTokenSource; + ActiveRecordingInfo activeRecordingInfo; - if (_activeRecordings.TryGetValue(timerId, out cancellationTokenSource)) + if (_activeRecordings.TryGetValue(timerId, out activeRecordingInfo)) { - cancellationTokenSource.Cancel(); + activeRecordingInfo.CancellationTokenSource.Cancel(); } } @@ -296,17 +300,20 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV } } - try - { - _fileSystem.DeleteFile(remove.Path); - } - catch (DirectoryNotFoundException) + if (!string.IsNullOrWhiteSpace(remove.Path)) { + try + { + _fileSystem.DeleteFile(remove.Path); + } + catch (DirectoryNotFoundException) + { - } - catch (FileNotFoundException) - { + } + catch (FileNotFoundException) + { + } } _recordingProvider.Delete(remove); } @@ -489,6 +496,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV } } + private bool IsListingProviderEnabledForTuner(ListingsProviderInfo info, string tunerHostId) + { + return info.EnableAllTuners || info.EnabledTuners.Contains(tunerHostId ?? string.Empty, StringComparer.OrdinalIgnoreCase); + } + private async Task<IEnumerable<ProgramInfo>> GetProgramsAsyncInternal(string channelId, DateTime startDateUtc, DateTime endDateUtc, CancellationToken cancellationToken) { var channels = await GetChannelsAsync(true, cancellationToken).ConfigureAwait(false); @@ -496,6 +508,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV foreach (var provider in GetListingProviders()) { + if (!IsListingProviderEnabledForTuner(provider.Item2, channel.TunerHostId)) + { + continue; + } + var programs = await provider.Item1.GetProgramsAsync(provider.Item2, channel.Number, channel.Name, startDateUtc, endDateUtc, cancellationToken) .ConfigureAwait(false); @@ -636,11 +653,15 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV return; } - var cancellationTokenSource = new CancellationTokenSource(); + var activeRecordingInfo = new ActiveRecordingInfo + { + CancellationTokenSource = new CancellationTokenSource(), + TimerId = timer.Id + }; - if (_activeRecordings.TryAdd(timer.Id, cancellationTokenSource)) + if (_activeRecordings.TryAdd(timer.Id, activeRecordingInfo)) { - await RecordStream(timer, recordingEndDate, cancellationTokenSource.Token).ConfigureAwait(false); + await RecordStream(timer, recordingEndDate, activeRecordingInfo, activeRecordingInfo.CancellationTokenSource.Token).ConfigureAwait(false); } else { @@ -657,7 +678,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV } } - private async Task RecordStream(TimerInfo timer, DateTime recordingEndDate, CancellationToken cancellationToken) + private async Task RecordStream(TimerInfo timer, DateTime recordingEndDate, ActiveRecordingInfo activeRecordingInfo, CancellationToken cancellationToken) { if (timer == null) { @@ -712,7 +733,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV var recordingFileName = _fileSystem.GetValidFilename(RecordingHelper.GetRecordingName(timer, info)).Trim() + ".ts"; recordPath = Path.Combine(recordPath, recordingFileName); - _fileSystem.CreateDirectory(Path.GetDirectoryName(recordPath)); var recordingId = info.Id.GetMD5().ToString("N"); var recording = _recordingProvider.GetAll().FirstOrDefault(x => string.Equals(x.Id, recordingId, StringComparison.OrdinalIgnoreCase)); @@ -760,7 +780,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV try { // HDHR doesn't seem to release the tuner right away after first probing with ffmpeg - await Task.Delay(3000, cancellationToken).ConfigureAwait(false); + //await Task.Delay(3000, cancellationToken).ConfigureAwait(false); var duration = recordingEndDate - DateTime.UtcNow; @@ -770,6 +790,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV { recordPath = Path.ChangeExtension(recordPath, ".mp4"); } + recordPath = EnsureFileUnique(recordPath, timer.Id); + _fileSystem.CreateDirectory(Path.GetDirectoryName(recordPath)); + activeRecordingInfo.Path = recordPath; + + _libraryMonitor.ReportFileSystemChangeBeginning(recordPath); recording.Path = recordPath; recording.Status = RecordingStatus.InProgress; @@ -778,9 +803,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV _logger.Info("Beginning recording. Will record for {0} minutes.", duration.TotalMinutes.ToString(CultureInfo.InvariantCulture)); - var durationToken = new CancellationTokenSource(duration); - var linkedToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token; - _logger.Info("Writing file to path: " + recordPath); _logger.Info("Opening recording stream from tuner provider"); @@ -790,10 +812,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV isResourceOpen = false; }; - await recorder.Record(mediaStreamInfo, recordPath, onStarted, linkedToken).ConfigureAwait(false); + await recorder.Record(mediaStreamInfo, recordPath, duration, onStarted, cancellationToken).ConfigureAwait(false); recording.Status = RecordingStatus.Completed; - _logger.Info("Recording completed"); + _logger.Info("Recording completed: {0}", recordPath); } finally { @@ -801,21 +823,23 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV { result.Item2.Release(); } + + _libraryMonitor.ReportFileSystemChangeComplete(recordPath, false); } } catch (OperationCanceledException) { - _logger.Info("Recording stopped"); + _logger.Info("Recording stopped: {0}", recordPath); recording.Status = RecordingStatus.Completed; } catch (Exception ex) { - _logger.ErrorException("Error recording", ex); + _logger.ErrorException("Error recording to {0}", ex, recordPath); recording.Status = RecordingStatus.Error; } finally { - CancellationTokenSource removed; + ActiveRecordingInfo removed; _activeRecordings.TryRemove(timer.Id, out removed); } @@ -841,6 +865,40 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV } } + private string EnsureFileUnique(string path, string timerId) + { + var originalPath = path; + var index = 1; + + while (FileExists(path, timerId)) + { + var parent = Path.GetDirectoryName(originalPath); + var name = Path.GetFileNameWithoutExtension(originalPath); + name += "-" + index.ToString(CultureInfo.InvariantCulture); + + path = Path.ChangeExtension(Path.Combine(parent, name), Path.GetExtension(originalPath)); + index++; + } + + return path; + } + + private bool FileExists(string path, string timerId) + { + if (_fileSystem.FileExists(path)) + { + return true; + } + + var hasRecordingAtPath = _activeRecordings.Values.ToList().Any(i => string.Equals(i.Path, path, StringComparison.OrdinalIgnoreCase) && !string.Equals(i.TimerId, timerId, StringComparison.OrdinalIgnoreCase)); + + if (hasRecordingAtPath) + { + return true; + } + return false; + } + private async Task<IRecorder> GetRecorder() { if (GetConfiguration().EnableRecordingEncoding) @@ -1024,7 +1082,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV { foreach (var pair in _activeRecordings.ToList()) { - pair.Value.Cancel(); + pair.Value.CancellationTokenSource.Cancel(); } } @@ -1041,5 +1099,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV IsRegistered = true }); } + + class ActiveRecordingInfo + { + public string Path { get; set; } + public string TimerId { get; set; } + public CancellationTokenSource CancellationTokenSource { get; set; } + } } }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs index 5f4d32732..62c9cd171 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs @@ -38,7 +38,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV _json = json; } - public async Task Record(MediaSourceInfo mediaSource, string targetFile, Action onStarted, CancellationToken cancellationToken) + public async Task Record(MediaSourceInfo mediaSource, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken) { _targetPath = targetFile; _fileSystem.CreateDirectory(Path.GetDirectoryName(targetFile)); @@ -56,7 +56,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV RedirectStandardInput = true, FileName = _mediaEncoder.EncoderPath, - Arguments = GetCommandLineArgs(mediaSource, targetFile), + Arguments = GetCommandLineArgs(mediaSource, targetFile, duration), WindowStyle = ProcessWindowStyle.Hidden, ErrorDialog = false @@ -88,11 +88,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV // MUST read both stdout and stderr asynchronously or a deadlock may occurr process.BeginOutputReadLine(); + onStarted(); + // Important - don't await the log task or we won't be able to kill ffmpeg when the user stops playback StartStreamingLog(process.StandardError.BaseStream, _logFileStream); - onStarted(); - // Wait for the file to exist before proceeeding while (!_hasExited) { @@ -100,7 +100,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV } } - private string GetCommandLineArgs(MediaSourceInfo mediaSource, string targetFile) + private string GetCommandLineArgs(MediaSourceInfo mediaSource, string targetFile, TimeSpan duration) { string videoArgs; if (EncodeVideo(mediaSource)) @@ -116,14 +116,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV videoArgs = "-codec:v:0 copy"; } - var commandLineArgs = "-fflags +genpts -i \"{0}\" -sn {2} -map_metadata -1 -threads 0 {3} -y \"{1}\""; + var commandLineArgs = "-fflags +genpts -async 1 -vsync -1 -i \"{0}\" -t {4} -sn {2} -map_metadata -1 -threads 0 {3} -y \"{1}\""; if (mediaSource.ReadAtNativeFramerate) { commandLineArgs = "-re " + commandLineArgs; } - commandLineArgs = string.Format(commandLineArgs, mediaSource.Path, targetFile, videoArgs, GetAudioArgs(mediaSource)); + commandLineArgs = string.Format(commandLineArgs, mediaSource.Path, targetFile, videoArgs, GetAudioArgs(mediaSource), _mediaEncoder.GetTimeParameter(duration.Ticks)); return commandLineArgs; } @@ -143,7 +143,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV { audioChannels = audioStream.Channels ?? audioChannels; } - return "-codec:a:0 aac -strict experimental -ab 320000 -ac " + audioChannels.ToString(CultureInfo.InvariantCulture); + return "-codec:a:0 aac -strict experimental -ab 320000"; } private bool EncodeVideo(MediaSourceInfo mediaSource) diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs index 12e73c1f3..268a4f751 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs @@ -7,6 +7,15 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV { public interface IRecorder { - Task Record(MediaSourceInfo mediaSource, string targetFile, Action onStarted, CancellationToken cancellationToken); + /// <summary> + /// Records the specified media source. + /// </summary> + /// <param name="mediaSource">The media source.</param> + /// <param name="targetFile">The target file.</param> + /// <param name="duration">The duration.</param> + /// <param name="onStarted">The on started.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + Task Record(MediaSourceInfo mediaSource, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken); } } diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs index b29a7562c..68b3f1f71 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs @@ -13,7 +13,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV where T : class { private readonly object _fileDataLock = new object(); - private volatile List<T> _items; + private List<T> _items; private readonly IJsonSerializer _jsonSerializer; protected readonly ILogger Logger; private readonly string _dataPath; @@ -31,17 +31,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV public IReadOnlyList<T> GetAll() { - if (_items == null) + lock (_fileDataLock) { - lock (_fileDataLock) + if (_items == null) { - if (_items == null) - { - _items = GetItemsFromFile(_dataPath); - } + _items = GetItemsFromFile(_dataPath); } + return _items; } - return _items; } private List<T> GetItemsFromFile(string path) diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs index a5c869d45..37e10d925 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs @@ -56,13 +56,13 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV name += " " + info.OriginalAirDate.Value.ToString("yyyy-MM-dd"); } - if (addHyphen) - { - name += " -"; - } - if (!string.IsNullOrWhiteSpace(info.EpisodeTitle)) { + if (addHyphen) + { + name += " -"; + } + name += " " + info.EpisodeTitle; } } diff --git a/MediaBrowser.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/MediaBrowser.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs index 87d7ff3eb..449943229 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs @@ -951,6 +951,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings public string stationID { get; set; } public List<Program> programs { get; set; } public MetadataSchedule metadata { get; set; } + + public Day() + { + programs = new List<Program>(); + } } // diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs index 04f99cdce..81ad6a387 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs @@ -178,7 +178,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv SourceType = info.SourceType, Status = info.Status, ChannelName = channelName, - Url = info.Url + Url = info.Url, + CanReset = info.CanReset }; if (!string.IsNullOrEmpty(info.ChannelId)) diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index 14bfcba27..abb2710e7 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -350,7 +350,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv foreach (var source in list) { - Normalize(source, item.ChannelType == ChannelType.TV); + Normalize(source, service, item.ChannelType == ChannelType.TV); } return list; @@ -379,12 +379,13 @@ namespace MediaBrowser.Server.Implementations.LiveTv { MediaSourceInfo info; bool isVideo; + ILiveTvService service; if (isChannel) { var channel = GetInternalChannel(id); isVideo = channel.ChannelType == ChannelType.TV; - var service = GetService(channel); + service = GetService(channel); _logger.Info("Opening channel stream from {0}, external channel Id: {1}", service.Name, channel.ExternalId); info = await service.GetChannelStream(channel.ExternalId, mediaSourceId, cancellationToken).ConfigureAwait(false); info.RequiresClosing = true; @@ -400,7 +401,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv { var recording = await GetInternalRecording(id, cancellationToken).ConfigureAwait(false); isVideo = !string.Equals(recording.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase); - var service = GetService(recording); + service = GetService(recording); _logger.Info("Opening recording stream from {0}, external recording Id: {1}", service.Name, recording.ExternalId); info = await service.GetRecordingStream(recording.ExternalId, null, cancellationToken).ConfigureAwait(false); @@ -415,7 +416,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv } _logger.Info("Live stream info: {0}", _jsonSerializer.SerializeToString(info)); - Normalize(info, isVideo); + Normalize(info, service, isVideo); var data = new LiveStreamData { @@ -440,7 +441,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv } } - private void Normalize(MediaSourceInfo mediaSource, bool isVideo) + private void Normalize(MediaSourceInfo mediaSource, ILiveTvService service, bool isVideo) { if (mediaSource.MediaStreams.Count == 0) { @@ -537,6 +538,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv mediaSource.Bitrate = total; } } + + if (!(service is EmbyTV.EmbyTV)) + { + // We can't trust that we'll be able to direct stream it through emby server, no matter what the provider says + mediaSource.SupportsDirectStream = true; + } } private async Task<LiveTvChannel> GetChannel(ChannelInfo channelInfo, string serviceName, Guid parentFolderId, CancellationToken cancellationToken) @@ -801,11 +808,21 @@ namespace MediaBrowser.Server.Implementations.LiveTv { if (!string.IsNullOrWhiteSpace(info.ImagePath)) { - item.SetImagePath(ImageType.Primary, info.ImagePath); + item.SetImage(new ItemImageInfo + { + Path = info.ImagePath, + Type = ImageType.Primary, + IsPlaceholder = true + }, 0); } else if (!string.IsNullOrWhiteSpace(info.ImageUrl)) { - item.SetImagePath(ImageType.Primary, info.ImageUrl); + item.SetImage(new ItemImageInfo + { + Path = info.ImageUrl, + Type = ImageType.Primary, + IsPlaceholder = true + }, 0); } } @@ -1472,70 +1489,79 @@ namespace MediaBrowser.Server.Implementations.LiveTv }; } - public void AddInfoToProgramDto(BaseItem item, BaseItemDto dto, List<ItemFields> fields, User user = null) + public async Task AddInfoToProgramDto(List<Tuple<BaseItem, BaseItemDto>> tuples, List<ItemFields> fields, User user = null) { - var program = (LiveTvProgram)item; - - dto.StartDate = program.StartDate; - dto.EpisodeTitle = program.EpisodeTitle; + var recordingTuples = new List<Tuple<BaseItemDto, string, string>>(); - if (program.IsRepeat) - { - dto.IsRepeat = program.IsRepeat; - } - if (program.IsMovie) - { - dto.IsMovie = program.IsMovie; - } - if (program.IsSeries) - { - dto.IsSeries = program.IsSeries; - } - if (program.IsSports) - { - dto.IsSports = program.IsSports; - } - if (program.IsLive) - { - dto.IsLive = program.IsLive; - } - if (program.IsNews) - { - dto.IsNews = program.IsNews; - } - if (program.IsKids) - { - dto.IsKids = program.IsKids; - } - if (program.IsPremiere) + foreach (var tuple in tuples) { - dto.IsPremiere = program.IsPremiere; - } + var program = (LiveTvProgram)tuple.Item1; + var dto = tuple.Item2; - if (fields.Contains(ItemFields.ChannelInfo)) - { - var channel = GetInternalChannel(program.ChannelId); + dto.StartDate = program.StartDate; + dto.EpisodeTitle = program.EpisodeTitle; + + if (program.IsRepeat) + { + dto.IsRepeat = program.IsRepeat; + } + if (program.IsMovie) + { + dto.IsMovie = program.IsMovie; + } + if (program.IsSeries) + { + dto.IsSeries = program.IsSeries; + } + if (program.IsSports) + { + dto.IsSports = program.IsSports; + } + if (program.IsLive) + { + dto.IsLive = program.IsLive; + } + if (program.IsNews) + { + dto.IsNews = program.IsNews; + } + if (program.IsKids) + { + dto.IsKids = program.IsKids; + } + if (program.IsPremiere) + { + dto.IsPremiere = program.IsPremiere; + } - if (channel != null) + if (fields.Contains(ItemFields.ChannelInfo)) { - dto.ChannelName = channel.Name; - dto.MediaType = channel.MediaType; + var channel = GetInternalChannel(program.ChannelId); - if (channel.HasImage(ImageType.Primary)) + if (channel != null) { - dto.ChannelPrimaryImageTag = _tvDtoService.GetImageTag(channel); + dto.ChannelName = channel.Name; + dto.MediaType = channel.MediaType; + + if (channel.HasImage(ImageType.Primary)) + { + dto.ChannelPrimaryImageTag = _tvDtoService.GetImageTag(channel); + } } } - } - if (fields.Contains(ItemFields.ServiceName)) - { var service = GetService(program); - if (service != null) + var serviceName = service == null ? null : service.Name; + + if (fields.Contains(ItemFields.ServiceName)) { - dto.ServiceName = service.Name; + dto.ServiceName = serviceName; } + + recordingTuples.Add(new Tuple<BaseItemDto, string, string>(dto, serviceName, program.ExternalId)); } + + await AddRecordingInfo(recordingTuples, CancellationToken.None).ConfigureAwait(false); } public void AddInfoToRecordingDto(BaseItem item, BaseItemDto dto, User user = null) @@ -2343,7 +2369,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv throw new ResourceNotFoundException(); } - await provider.Validate(info).ConfigureAwait(false); + var configurable = provider as IConfigurableTunerHost; + if (configurable != null) + { + await configurable.Validate(info).ConfigureAwait(false); + } var config = GetConfiguration(); diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs index 4ebc173b5..fb27631e5 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs @@ -64,7 +64,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts return list; } - private List<TunerHostInfo> GetTunerHosts() + protected virtual List<TunerHostInfo> GetTunerHosts() { return GetConfiguration().TunerHosts .Where(i => i.IsEnabled && string.Equals(i.Type, Type, StringComparison.OrdinalIgnoreCase)) diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunDiscovery.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunDiscovery.cs index 92a33993a..0a03e60fa 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunDiscovery.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunDiscovery.cs @@ -90,7 +90,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun await _liveTvManager.SaveTunerHost(new TunerHostInfo { Type = HdHomerunHost.DeviceType, - Url = url + Url = url, + DataVersion = 1 }).ConfigureAwait(false); } diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index 0671a9b56..bdfbee521 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -14,13 +14,14 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Dlna; namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun { - public class HdHomerunHost : BaseTunerHost, ITunerHost + public class HdHomerunHost : BaseTunerHost, ITunerHost, IConfigurableTunerHost { private readonly IHttpClient _httpClient; @@ -47,6 +48,18 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun private const string ChannelIdPrefix = "hdhr_"; + private string GetChannelId(TunerHostInfo info, Channels i) + { + var id = ChannelIdPrefix + i.GuideNumber.ToString(CultureInfo.InvariantCulture); + + if (info.DataVersion >= 1) + { + id += '_' + (i.GuideName ?? string.Empty).GetMD5().ToString("N"); + } + + return id; + } + protected override async Task<IEnumerable<ChannelInfo>> GetChannelsInternal(TunerHostInfo info, CancellationToken cancellationToken) { var options = new HttpRequestOptions @@ -64,8 +77,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun { Name = i.GuideName, Number = i.GuideNumber.ToString(CultureInfo.InvariantCulture), - Id = ChannelIdPrefix + i.GuideNumber.ToString(CultureInfo.InvariantCulture), - IsFavorite = i.Favorite + Id = GetChannelId(info, i), + IsFavorite = i.Favorite, + TunerHostId = info.Id }); @@ -82,30 +96,19 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun private async Task<string> GetModelInfo(TunerHostInfo info, CancellationToken cancellationToken) { - string model = null; - using (var stream = await _httpClient.Get(new HttpRequestOptions() { - Url = string.Format("{0}/", GetApiUrl(info, false)), + Url = string.Format("{0}/discover.json", GetApiUrl(info, false)), CancellationToken = cancellationToken, CacheLength = TimeSpan.FromDays(1), CacheMode = CacheMode.Unconditional, TimeoutMs = Convert.ToInt32(TimeSpan.FromSeconds(5).TotalMilliseconds) })) { - using (var sr = new StreamReader(stream, System.Text.Encoding.UTF8)) - { - while (!sr.EndOfStream) - { - string line = StripXML(sr.ReadLine()); - if (line.StartsWith("Model:")) { model = line.Replace("Model: ", ""); } - //if (line.StartsWith("Device ID:")) { deviceID = line.Replace("Device ID: ", ""); } - //if (line.StartsWith("Firmware:")) { firmware = line.Replace("Firmware: ", ""); } - } - } - } + var response = JsonSerializer.DeserializeFromStream<DiscoverResponse>(stream); - return model; + return response.ModelNumber; + } } public async Task<List<LiveTvTunerInfo>> GetTunerInfos(TunerHostInfo info, CancellationToken cancellationToken) @@ -347,6 +350,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun return Config.GetConfiguration<EncodingOptions>("encoding"); } + private string GetHdHrIdFromChannelId(string channelId) + { + return channelId.Split('_')[1]; + } + protected override async Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(TunerHostInfo info, string channelId, CancellationToken cancellationToken) { var list = new List<MediaSourceInfo>(); @@ -355,9 +363,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun { return list; } - channelId = channelId.Substring(ChannelIdPrefix.Length); + var hdhrId = GetHdHrIdFromChannelId(channelId); - list.Add(GetMediaSource(info, channelId, "native")); + list.Add(GetMediaSource(info, hdhrId, "native")); try { @@ -366,12 +374,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun if (model.IndexOf("hdtc", StringComparison.OrdinalIgnoreCase) != -1) { - list.Insert(0, GetMediaSource(info, channelId, "heavy")); + list.Insert(0, GetMediaSource(info, hdhrId, "heavy")); - list.Add(GetMediaSource(info, channelId, "internet480")); - list.Add(GetMediaSource(info, channelId, "internet360")); - list.Add(GetMediaSource(info, channelId, "internet240")); - list.Add(GetMediaSource(info, channelId, "mobile")); + list.Add(GetMediaSource(info, hdhrId, "internet480")); + list.Add(GetMediaSource(info, hdhrId, "internet360")); + list.Add(GetMediaSource(info, hdhrId, "internet240")); + list.Add(GetMediaSource(info, hdhrId, "mobile")); } } catch (Exception ex) @@ -400,9 +408,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun { throw new ArgumentException("Channel not found"); } - channelId = channelId.Substring(ChannelIdPrefix.Length); + var hdhrId = GetHdHrIdFromChannelId(channelId); - return GetMediaSource(info, channelId, streamId); + return GetMediaSource(info, hdhrId, streamId); } public async Task Validate(TunerHostInfo info) @@ -419,5 +427,17 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun return info.Any(i => i.Status == LiveTvTunerStatus.Available); } + + public class DiscoverResponse + { + public string FriendlyName { get; set; } + public string ModelNumber { get; set; } + public string FirmwareName { get; set; } + public string FirmwareVersion { get; set; } + public string DeviceID { get; set; } + public string DeviceAuth { get; set; } + public string BaseURL { get; set; } + public string LineupURL { get; set; } + } } } diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs index f87d4f43f..523f14dfc 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs @@ -8,19 +8,17 @@ using MediaBrowser.Model.Logging; using MediaBrowser.Model.MediaInfo; using System; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using CommonIO; -using MediaBrowser.Common.IO; using MediaBrowser.Common.Net; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.Serialization; namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts { - public class M3UTunerHost : BaseTunerHost, ITunerHost + public class M3UTunerHost : BaseTunerHost, ITunerHost, IConfigurableTunerHost { private readonly IFileSystem _fileSystem; private readonly IHttpClient _httpClient; @@ -46,65 +44,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts protected override async Task<IEnumerable<ChannelInfo>> GetChannelsInternal(TunerHostInfo info, CancellationToken cancellationToken) { - var urlHash = info.Url.GetMD5().ToString("N"); - - // Read the file and display it line by line. - using (var reader = new StreamReader(await GetListingsStream(info, cancellationToken).ConfigureAwait(false))) - { - return GetChannels(reader, urlHash); - } - } - - private List<M3UChannel> GetChannels(StreamReader reader, string urlHash) - { - var channels = new List<M3UChannel>(); - - string channnelName = null; - string channelNumber = null; - string line; - - while ((line = reader.ReadLine()) != null) - { - line = line.Trim(); - if (string.IsNullOrWhiteSpace(line)) - { - continue; - } - - if (line.StartsWith("#EXTM3U", StringComparison.OrdinalIgnoreCase)) - { - continue; - } - - if (line.StartsWith("#EXTINF:", StringComparison.OrdinalIgnoreCase)) - { - line = line.Substring(8); - Logger.Info("Found m3u channel: {0}", line); - var parts = line.Split(new[] { ',' }, 2); - channelNumber = parts[0]; - channnelName = parts[1]; - } - else if (!string.IsNullOrWhiteSpace(channelNumber)) - { - channels.Add(new M3UChannel - { - Name = channnelName, - Number = channelNumber, - Id = ChannelIdPrefix + urlHash + line.GetMD5().ToString("N"), - Path = line - }); - - channelNumber = null; - channnelName = null; - } - } - return channels; + return await new M3uParser(Logger, _fileSystem, _httpClient).Parse(info.Url, ChannelIdPrefix, info.Id, cancellationToken).ConfigureAwait(false); } public Task<List<LiveTvTunerInfo>> GetTunerInfos(CancellationToken cancellationToken) { - var list = GetConfiguration().TunerHosts - .Where(i => i.IsEnabled && string.Equals(i.Type, Type, StringComparison.OrdinalIgnoreCase)) + var list = GetTunerHosts() .Select(i => new LiveTvTunerInfo() { Name = Name, @@ -125,18 +70,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts return sources.First(); } - class M3UChannel : ChannelInfo - { - public string Path { get; set; } - - public M3UChannel() - { - } - } - public async Task Validate(TunerHostInfo info) { - using (var stream = await GetListingsStream(info, CancellationToken.None).ConfigureAwait(false)) + using (var stream = await new M3uParser(Logger, _fileSystem, _httpClient).GetListingsStream(info.Url, CancellationToken.None).ConfigureAwait(false)) { } @@ -147,15 +83,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts return channelId.StartsWith(ChannelIdPrefix, StringComparison.OrdinalIgnoreCase); } - private Task<Stream> GetListingsStream(TunerHostInfo info, CancellationToken cancellationToken) - { - if (info.Url.StartsWith("http", StringComparison.OrdinalIgnoreCase)) - { - return _httpClient.Get(info.Url, cancellationToken); - } - return Task.FromResult(_fileSystem.OpenRead(info.Url)); - } - protected override async Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(TunerHostInfo info, string channelId, CancellationToken cancellationToken) { var urlHash = info.Url.GetMD5().ToString("N"); diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs new file mode 100644 index 000000000..f8f003fa1 --- /dev/null +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs @@ -0,0 +1,132 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; +using CommonIO; +using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.Net; +using MediaBrowser.Controller.LiveTv; +using MediaBrowser.Model.Logging; + +namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts +{ + public class M3uParser + { + private readonly ILogger _logger; + private readonly IFileSystem _fileSystem; + private readonly IHttpClient _httpClient; + + public M3uParser(ILogger logger, IFileSystem fileSystem, IHttpClient httpClient) + { + _logger = logger; + _fileSystem = fileSystem; + _httpClient = httpClient; + } + + public async Task<List<M3UChannel>> Parse(string url, string channelIdPrefix, string tunerHostId, CancellationToken cancellationToken) + { + var urlHash = url.GetMD5().ToString("N"); + + // Read the file and display it line by line. + using (var reader = new StreamReader(await GetListingsStream(url, cancellationToken).ConfigureAwait(false))) + { + return GetChannels(reader, urlHash, channelIdPrefix, tunerHostId); + } + } + + public Task<Stream> GetListingsStream(string url, CancellationToken cancellationToken) + { + if (url.StartsWith("http", StringComparison.OrdinalIgnoreCase)) + { + return _httpClient.Get(url, cancellationToken); + } + return Task.FromResult(_fileSystem.OpenRead(url)); + } + + private List<M3UChannel> GetChannels(StreamReader reader, string urlHash, string channelIdPrefix, string tunerHostId) + { + var channels = new List<M3UChannel>(); + string line; + string extInf = ""; + while ((line = reader.ReadLine()) != null) + { + line = line.Trim(); + if (string.IsNullOrWhiteSpace(line)) + { + continue; + } + + if (line.StartsWith("#EXTM3U", StringComparison.OrdinalIgnoreCase)) + { + continue; + } + + if (line.StartsWith("#EXTINF:", StringComparison.OrdinalIgnoreCase)) + { + extInf = line.Substring(8).Trim(); + _logger.Info("Found m3u channel: {0}", extInf); + } + else if (!string.IsNullOrWhiteSpace(extInf)) + { + var channel = GetChannelnfo(extInf, tunerHostId); + channel.Id = channelIdPrefix + urlHash + line.GetMD5().ToString("N"); + channel.Path = line; + channels.Add(channel); + extInf = ""; + } + } + return channels; + } + private M3UChannel GetChannelnfo(string extInf, string tunerHostId) + { + var titleIndex = extInf.LastIndexOf(','); + var channel = new M3UChannel(); + channel.TunerHostId = tunerHostId; + + channel.Number = extInf.Trim().Split(' ')[0] ?? "0"; + channel.Name = extInf.Substring(titleIndex + 1); + + if(channel.Number == "-1") { channel.Number = "0"; } + + //Check for channel number with the format from SatIp + int number; + var numberIndex = channel.Name.IndexOf('.'); + if (numberIndex > 0) + { + if (int.TryParse(channel.Name.Substring(0, numberIndex), out number)) + { + channel.Number = number.ToString(); + channel.Name = channel.Name.Substring(numberIndex + 1); + } + } + channel.ImageUrl = FindProperty("tvg-logo", extInf, null); + channel.Number = FindProperty("tvg-id", extInf, channel.Number); + channel.Number = FindProperty("channel-id", extInf, channel.Number); + channel.Name = FindProperty("tvg-name", extInf, channel.Name); + channel.Name = FindProperty("tvg-id", extInf, channel.Name); + return channel; + + } + private string FindProperty(string property, string properties, string defaultResult = "") + { + var reg = new Regex(@"([a-z0-9\-_]+)=\""([^""]+)\""", RegexOptions.IgnoreCase); + var matches = reg.Matches(properties); + foreach (Match match in matches) + { + if (match.Groups[1].Value == property) + { + return match.Groups[2].Value; + } + } + return defaultResult; + } + } + + + public class M3UChannel : ChannelInfo + { + public string Path { get; set; } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpDiscovery.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpDiscovery.cs index 08c42bb46..6781e498a 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpDiscovery.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpDiscovery.cs @@ -1,10 +1,14 @@ using System; using System.Collections.Generic; +using System.Globalization; +using System.IO; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; +using System.Xml; using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Dlna; @@ -13,6 +17,7 @@ using MediaBrowser.Controller.Plugins; using MediaBrowser.Model.Extensions; using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Serialization; namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp { @@ -24,14 +29,26 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp private readonly ILiveTvManager _liveTvManager; private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1); private readonly IHttpClient _httpClient; + private readonly IJsonSerializer _json; - public SatIpDiscovery(IDeviceDiscovery deviceDiscovery, IServerConfigurationManager config, ILogger logger, ILiveTvManager liveTvManager, IHttpClient httpClient) + public static SatIpDiscovery Current; + + private readonly List<TunerHostInfo> _discoveredHosts = new List<TunerHostInfo>(); + + public List<TunerHostInfo> DiscoveredHosts + { + get { return _discoveredHosts.ToList(); } + } + + public SatIpDiscovery(IDeviceDiscovery deviceDiscovery, IServerConfigurationManager config, ILogger logger, ILiveTvManager liveTvManager, IHttpClient httpClient, IJsonSerializer json) { _deviceDiscovery = deviceDiscovery; _config = config; _logger = logger; _liveTvManager = liveTvManager; _httpClient = httpClient; + _json = json; + Current = this; } public void Run() @@ -42,7 +59,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp void _deviceDiscovery_DeviceDiscovered(object sender, SsdpMessageEventArgs e) { string st = null; - if (e.Headers.TryGetValue("ST", out st) && string.Equals(st, "urn:ses-com:device:SatIPServer:1", StringComparison.OrdinalIgnoreCase)) + string nt = null; + e.Headers.TryGetValue("ST", out st); + e.Headers.TryGetValue("NT", out nt); + + if (string.Equals(st, "urn:ses-com:device:SatIPServer:1", StringComparison.OrdinalIgnoreCase) || + string.Equals(nt, "urn:ses-com:device:SatIPServer:1", StringComparison.OrdinalIgnoreCase)) { string location; if (e.Headers.TryGetValue("Location", out location) && !string.IsNullOrWhiteSpace(location)) @@ -61,26 +83,23 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp try { - var options = GetConfiguration(); - - //if (options.TunerHosts.Any(i => - // string.Equals(i.Type, SatIpHost.DeviceType, StringComparison.OrdinalIgnoreCase) && - // UriEquals(i.Url, url))) - //{ - // return; - //} + if (_discoveredHosts.Any(i => string.Equals(i.Type, SatIpHost.DeviceType, StringComparison.OrdinalIgnoreCase) && string.Equals(location, i.Url, StringComparison.OrdinalIgnoreCase))) + { + return; + } - //// Strip off the port - //url = new Uri(url).GetComponents(UriComponents.AbsoluteUri & ~UriComponents.Port, UriFormat.UriEscaped).TrimEnd('/'); + _logger.Debug("Will attempt to add SAT device {0}", location); + var info = await GetInfo(location, CancellationToken.None).ConfigureAwait(false); - //await TestUrl(url).ConfigureAwait(false); + _discoveredHosts.Add(info); + } + catch (OperationCanceledException) + { - //await _liveTvManager.SaveTunerHost(new TunerHostInfo - //{ - // Type = SatIpHost.DeviceType, - // Url = url + } + catch (NotImplementedException) + { - //}).ConfigureAwait(false); } catch (Exception ex) { @@ -92,43 +111,141 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp } } - private async Task TestUrl(string url) + public void Dispose() { - // Test it by pulling down the lineup - using (await _httpClient.Get(new HttpRequestOptions + } + + public async Task<SatIpTunerHostInfo> GetInfo(string url, CancellationToken cancellationToken) + { + var result = new SatIpTunerHostInfo { - Url = string.Format("{0}/lineup.json", url), - CancellationToken = CancellationToken.None - })) + Url = url, + IsEnabled = true, + Type = SatIpHost.DeviceType, + Tuners = 1, + TunersAvailable = 1 + }; + + using (var stream = await _httpClient.Get(url, cancellationToken).ConfigureAwait(false)) + { + 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 "device": + using (var subtree = reader.ReadSubtree()) + { + FillFromDeviceNode(result, subtree); + } + break; + default: + reader.Skip(); + break; + } + } + } + } + } + } + + if (string.IsNullOrWhiteSpace(result.Id)) { + throw new NotImplementedException(); } - } - private bool UriEquals(string savedUri, string location) - { - return string.Equals(NormalizeUrl(location), NormalizeUrl(savedUri), StringComparison.OrdinalIgnoreCase); - } + // Device hasn't implemented an m3u list + if (string.IsNullOrWhiteSpace(result.M3UUrl)) + { + result.IsEnabled = false; + } - private string NormalizeUrl(string url) - { - if (!url.StartsWith("http", StringComparison.OrdinalIgnoreCase)) + else if (!result.M3UUrl.StartsWith("http", StringComparison.OrdinalIgnoreCase)) { - url = "http://" + url; + var fullM3uUrl = url.Substring(0, url.LastIndexOf('/')); + result.M3UUrl = fullM3uUrl + "/" + result.M3UUrl.TrimStart('/'); } - url = url.TrimEnd('/'); + _logger.Debug("SAT device result: {0}", _json.SerializeToString(result)); - // Strip off the port - return new Uri(url).GetComponents(UriComponents.AbsoluteUri & ~UriComponents.Port, UriFormat.UriEscaped); + return result; } - private LiveTvOptions GetConfiguration() + private void FillFromDeviceNode(SatIpTunerHostInfo info, XmlReader reader) { - return _config.GetConfiguration<LiveTvOptions>("livetv"); - } + reader.MoveToContent(); - public void Dispose() - { + while (reader.Read()) + { + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.LocalName) + { + case "UDN": + { + info.Id = reader.ReadElementContentAsString(); + break; + } + + case "friendlyName": + { + info.FriendlyName = reader.ReadElementContentAsString(); + break; + } + + case "satip:X_SATIPCAP": + case "X_SATIPCAP": + { + // <satip:X_SATIPCAP xmlns:satip="urn:ses-com:satip">DVBS2-2</satip:X_SATIPCAP> + var value = reader.ReadElementContentAsString() ?? string.Empty; + var parts = value.Split(new[] { '-' }, StringSplitOptions.RemoveEmptyEntries); + if (parts.Length == 2) + { + int intValue; + if (int.TryParse(parts[1], NumberStyles.Any, CultureInfo.InvariantCulture, out intValue)) + { + info.TunersAvailable = intValue; + } + + if (int.TryParse(parts[0].Substring(parts[0].Length - 1), NumberStyles.Any, CultureInfo.InvariantCulture, out intValue)) + { + info.Tuners = intValue; + } + } + break; + } + + case "satip:X_SATIPM3U": + case "X_SATIPM3U": + { + // <satip:X_SATIPM3U xmlns:satip="urn:ses-com:satip">/channellist.lua?select=m3u</satip:X_SATIPM3U> + info.M3UUrl = reader.ReadElementContentAsString(); + break; + } + + default: + reader.Skip(); + break; + } + } + } } } + + public class SatIpTunerHostInfo : TunerHostInfo + { + public int Tuners { get; set; } + public int TunersAvailable { get; set; } + public string M3UUrl { get; set; } + public string FriendlyName { get; set; } + } } diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpHost.cs index 205cdf74e..181169e9a 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpHost.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpHost.cs @@ -1,45 +1,171 @@ -namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using CommonIO; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.Net; +using MediaBrowser.Controller.LiveTv; +using MediaBrowser.Controller.MediaEncoding; +using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.LiveTv; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.MediaInfo; +using MediaBrowser.Model.Serialization; + +namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp { - public class SatIpHost /*: BaseTunerHost*/ + public class SatIpHost : BaseTunerHost, ITunerHost { - //public SatIpHost(IConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder) - // : base(config, logger, jsonSerializer, mediaEncoder) - //{ - //} + private readonly IFileSystem _fileSystem; + private readonly IHttpClient _httpClient; + + public SatIpHost(IConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IHttpClient httpClient) + : base(config, logger, jsonSerializer, mediaEncoder) + { + _fileSystem = fileSystem; + _httpClient = httpClient; + } + + private const string ChannelIdPrefix = "sat_"; + + protected override async Task<IEnumerable<ChannelInfo>> GetChannelsInternal(TunerHostInfo tuner, CancellationToken cancellationToken) + { + var satInfo = (SatIpTunerHostInfo) tuner; - //protected override Task<IEnumerable<ChannelInfo>> GetChannelsInternal(TunerHostInfo tuner, CancellationToken cancellationToken) - //{ - // throw new NotImplementedException(); - //} + return await new M3uParser(Logger, _fileSystem, _httpClient).Parse(satInfo.M3UUrl, ChannelIdPrefix, tuner.Id, cancellationToken).ConfigureAwait(false); + } public static string DeviceType { get { return "satip"; } } - //public override string Type - //{ - // get { return DeviceType; } - //} + public override string Type + { + get { return DeviceType; } + } + + protected override async Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken) + { + var urlHash = tuner.Url.GetMD5().ToString("N"); + var prefix = ChannelIdPrefix + urlHash; + if (!channelId.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) + { + return null; + } + + var channels = await GetChannels(tuner, true, cancellationToken).ConfigureAwait(false); + var m3uchannels = channels.Cast<M3UChannel>(); + var channel = m3uchannels.FirstOrDefault(c => string.Equals(c.Id, channelId, StringComparison.OrdinalIgnoreCase)); + if (channel != null) + { + var path = channel.Path; + MediaProtocol protocol = MediaProtocol.File; + if (path.StartsWith("http", StringComparison.OrdinalIgnoreCase)) + { + protocol = MediaProtocol.Http; + } + else if (path.StartsWith("rtmp", StringComparison.OrdinalIgnoreCase)) + { + protocol = MediaProtocol.Rtmp; + } + else if (path.StartsWith("rtsp", StringComparison.OrdinalIgnoreCase)) + { + protocol = MediaProtocol.Rtsp; + } + + var mediaSource = new MediaSourceInfo + { + Path = channel.Path, + Protocol = protocol, + MediaStreams = new List<MediaStream> + { + new MediaStream + { + Type = MediaStreamType.Video, + // Set the index to -1 because we don't know the exact index of the video stream within the container + Index = -1, + IsInterlaced = true + }, + new MediaStream + { + Type = MediaStreamType.Audio, + // Set the index to -1 because we don't know the exact index of the audio stream within the container + Index = -1 + + } + }, + RequiresOpening = false, + RequiresClosing = false + }; + + return new List<MediaSourceInfo> { mediaSource }; + } + return new List<MediaSourceInfo> { }; + } + + protected override async Task<MediaSourceInfo> GetChannelStream(TunerHostInfo tuner, string channelId, string streamId, CancellationToken cancellationToken) + { + var sources = await GetChannelStreamMediaSources(tuner, channelId, cancellationToken).ConfigureAwait(false); + + return sources.First(); + } + + protected override async Task<bool> IsAvailableInternal(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken) + { + var updatedInfo = await SatIpDiscovery.Current.GetInfo(tuner.Url, cancellationToken).ConfigureAwait(false); - //protected override Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken) - //{ - // throw new NotImplementedException(); - //} + return updatedInfo.TunersAvailable > 0; + } + + protected override bool IsValidChannelId(string channelId) + { + return channelId.StartsWith(ChannelIdPrefix, StringComparison.OrdinalIgnoreCase); + } - //protected override Task<MediaSourceInfo> GetChannelStream(TunerHostInfo tuner, string channelId, string streamId, CancellationToken cancellationToken) - //{ - // throw new NotImplementedException(); - //} + protected override List<TunerHostInfo> GetTunerHosts() + { + return SatIpDiscovery.Current.DiscoveredHosts; + } - //protected override Task<bool> IsAvailableInternal(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken) - //{ - // throw new NotImplementedException(); - //} + public string Name + { + get { return "Sat IP"; } + } - //protected override bool IsValidChannelId(string channelId) - //{ - // throw new NotImplementedException(); - //} + public Task<List<LiveTvTunerInfo>> GetTunerInfos(CancellationToken cancellationToken) + { + var list = GetTunerHosts() + .SelectMany(i => GetTunerInfos(i, cancellationToken)) + .ToList(); + + return Task.FromResult(list); + } + + public List<LiveTvTunerInfo> GetTunerInfos(TunerHostInfo info, CancellationToken cancellationToken) + { + var satInfo = (SatIpTunerHostInfo) info; + + var list = new List<LiveTvTunerInfo>(); + + for (var i = 0; i < satInfo.Tuners; i++) + { + list.Add(new LiveTvTunerInfo + { + Name = satInfo.FriendlyName ?? Name, + SourceType = Type, + Status = LiveTvTunerStatus.Available, + Id = info.Url.GetMD5().ToString("N") + i.ToString(CultureInfo.InvariantCulture), + Url = info.Url + }); + } + + return list; + } } } diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index 5f2fab457..a511d31b5 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -43,7 +43,7 @@ <ItemGroup> <Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\CommonIO.1.0.0.7\lib\net45\CommonIO.dll</HintPath> + <HintPath>..\packages\CommonIO.1.0.0.8\lib\net45\CommonIO.dll</HintPath> </Reference> <Reference Include="Emby.XmlTv, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> @@ -52,9 +52,9 @@ <Reference Include="Interfaces.IO"> <HintPath>..\packages\Interfaces.IO.1.0.0.5\lib\portable-net45+sl4+wp71+win8+wpa81\Interfaces.IO.dll</HintPath> </Reference> - <Reference Include="MediaBrowser.Naming, Version=1.0.5884.23751, Culture=neutral, processorArchitecture=MSIL"> + <Reference Include="MediaBrowser.Naming, Version=1.0.5891.29179, Culture=neutral, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\MediaBrowser.Naming.1.0.0.47\lib\portable-net45+sl4+wp71+win8+wpa81\MediaBrowser.Naming.dll</HintPath> + <HintPath>..\packages\MediaBrowser.Naming.1.0.0.48\lib\portable-net45+sl4+wp71+win8+wpa81\MediaBrowser.Naming.dll</HintPath> </Reference> <Reference Include="MoreLinq"> <HintPath>..\packages\morelinq.1.4.0\lib\net35\MoreLinq.dll</HintPath> @@ -65,9 +65,9 @@ <Reference Include="ServiceStack.Api.Swagger"> <HintPath>..\ThirdParty\ServiceStack\ServiceStack.Api.Swagger.dll</HintPath> </Reference> - <Reference Include="SocketHttpListener, Version=1.0.5840.28948, Culture=neutral, processorArchitecture=MSIL"> + <Reference Include="SocketHttpListener, Version=1.0.5900.37035, Culture=neutral, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\SocketHttpListener.1.0.0.25\lib\net45\SocketHttpListener.dll</HintPath> + <HintPath>..\packages\SocketHttpListener.1.0.0.27\lib\net45\SocketHttpListener.dll</HintPath> </Reference> <Reference Include="System" /> <Reference Include="System.Core" /> @@ -234,6 +234,7 @@ <Compile Include="LiveTv\TunerHosts\BaseTunerHost.cs" /> <Compile Include="LiveTv\TunerHosts\HdHomerun\HdHomerunHost.cs" /> <Compile Include="LiveTv\TunerHosts\HdHomerun\HdHomerunDiscovery.cs" /> + <Compile Include="LiveTv\TunerHosts\M3uParser.cs" /> <Compile Include="LiveTv\TunerHosts\M3UTunerHost.cs" /> <Compile Include="LiveTv\ProgramImageProvider.cs" /> <Compile Include="LiveTv\RecordingImageProvider.cs" /> diff --git a/MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs b/MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs index 6ca1bae6d..5f1bf0216 100644 --- a/MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs +++ b/MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs @@ -239,7 +239,11 @@ namespace MediaBrowser.Server.Implementations.Persistence typeof(Year).Name, typeof(Channel).Name, typeof(AggregateFolder).Name, - typeof(CollectionFolder).Name + typeof(CollectionFolder).Name, + + // LiveTVManager handles recordings + typeof(LiveTvAudioRecording).Name, + typeof(LiveTvVideoRecording).Name } }); diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs index a85872951..cd439d1f2 100644 --- a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs @@ -130,6 +130,7 @@ namespace MediaBrowser.Server.Implementations.Persistence "create table if not exists TypedBaseItems (guid GUID primary key, type TEXT, data BLOB, ParentId GUID)", "create index if not exists idx_TypedBaseItems on TypedBaseItems(guid)", + "create index if not exists idx_PathTypedBaseItems on TypedBaseItems(Path)", "create index if not exists idx_ParentIdTypedBaseItems on TypedBaseItems(ParentId)", "create table if not exists AncestorIds (ItemId GUID, AncestorId GUID, AncestorIdText TEXT, PRIMARY KEY (ItemId, AncestorId))", @@ -1014,7 +1015,7 @@ namespace MediaBrowser.Server.Implementations.Persistence if (!reader.IsDBNull(31)) { - item.OfficialRating = reader.GetString(31); + item.OfficialRatingDescription = reader.GetString(31); } if (!reader.IsDBNull(32)) @@ -1804,6 +1805,12 @@ namespace MediaBrowser.Server.Implementations.Persistence cmd.Parameters.Add(cmd, "@ParentId", DbType.Guid).Value = query.ParentId.Value; } + if (!string.IsNullOrWhiteSpace(query.Path)) + { + whereClauses.Add("Path=@Path"); + cmd.Parameters.Add(cmd, "@Path", DbType.String).Value = query.Path; + } + if (query.MinEndDate.HasValue) { whereClauses.Add("EndDate>=@MinEndDate"); diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteUserDataRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteUserDataRepository.cs index 8b86d19a2..63c41c71f 100644 --- a/MediaBrowser.Server.Implementations/Persistence/SqliteUserDataRepository.cs +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteUserDataRepository.cs @@ -56,6 +56,9 @@ namespace MediaBrowser.Server.Implementations.Persistence }; _connection.RunQueries(queries, Logger); + + _connection.AddColumn(Logger, "userdata", "AudioStreamIndex", "int"); + _connection.AddColumn(Logger, "userdata", "SubtitleStreamIndex", "int"); } /// <summary> @@ -127,7 +130,7 @@ namespace MediaBrowser.Server.Implementations.Persistence using (var cmd = _connection.CreateCommand()) { - cmd.CommandText = "replace into userdata (key, userId, rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate) values (@key, @userId, @rating,@played,@playCount,@isFavorite,@playbackPositionTicks,@lastPlayedDate)"; + cmd.CommandText = "replace into userdata (key, userId, rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate,AudioStreamIndex,SubtitleStreamIndex) values (@key, @userId, @rating,@played,@playCount,@isFavorite,@playbackPositionTicks,@lastPlayedDate,@AudioStreamIndex,@SubtitleStreamIndex)"; cmd.Parameters.Add(cmd, "@key", DbType.String).Value = key; cmd.Parameters.Add(cmd, "@userId", DbType.Guid).Value = userId; @@ -137,6 +140,8 @@ namespace MediaBrowser.Server.Implementations.Persistence cmd.Parameters.Add(cmd, "@isFavorite", DbType.Boolean).Value = userData.IsFavorite; cmd.Parameters.Add(cmd, "@playbackPositionTicks", DbType.Int64).Value = userData.PlaybackPositionTicks; cmd.Parameters.Add(cmd, "@lastPlayedDate", DbType.DateTime).Value = userData.LastPlayedDate; + cmd.Parameters.Add(cmd, "@AudioStreamIndex", DbType.Int32).Value = userData.AudioStreamIndex; + cmd.Parameters.Add(cmd, "@SubtitleStreamIndex", DbType.Int32).Value = userData.SubtitleStreamIndex; cmd.Transaction = transaction; @@ -199,7 +204,7 @@ namespace MediaBrowser.Server.Implementations.Persistence { using (var cmd = _connection.CreateCommand()) { - cmd.CommandText = "replace into userdata (key, userId, rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate) values (@key, @userId, @rating,@played,@playCount,@isFavorite,@playbackPositionTicks,@lastPlayedDate)"; + cmd.CommandText = "replace into userdata (key, userId, rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate,AudioStreamIndex,SubtitleStreamIndex) values (@key, @userId, @rating,@played,@playCount,@isFavorite,@playbackPositionTicks,@lastPlayedDate,@AudioStreamIndex,@SubtitleStreamIndex)"; cmd.Parameters.Add(cmd, "@key", DbType.String).Value = userItemData.Key; cmd.Parameters.Add(cmd, "@userId", DbType.Guid).Value = userId; @@ -209,6 +214,8 @@ namespace MediaBrowser.Server.Implementations.Persistence cmd.Parameters.Add(cmd, "@isFavorite", DbType.Boolean).Value = userItemData.IsFavorite; cmd.Parameters.Add(cmd, "@playbackPositionTicks", DbType.Int64).Value = userItemData.PlaybackPositionTicks; cmd.Parameters.Add(cmd, "@lastPlayedDate", DbType.DateTime).Value = userItemData.LastPlayedDate; + cmd.Parameters.Add(cmd, "@AudioStreamIndex", DbType.Int32).Value = userItemData.AudioStreamIndex; + cmd.Parameters.Add(cmd, "@SubtitleStreamIndex", DbType.Int32).Value = userItemData.SubtitleStreamIndex; cmd.Transaction = transaction; @@ -275,7 +282,7 @@ namespace MediaBrowser.Server.Implementations.Persistence using (var cmd = _connection.CreateCommand()) { - cmd.CommandText = "select key,userid,rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate from userdata where key = @key and userId=@userId"; + cmd.CommandText = "select key,userid,rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate,AudioStreamIndex,SubtitleStreamIndex from userdata where key = @key and userId=@userId"; cmd.Parameters.Add(cmd, "@key", DbType.String).Value = key; cmd.Parameters.Add(cmd, "@userId", DbType.Guid).Value = userId; @@ -310,7 +317,7 @@ namespace MediaBrowser.Server.Implementations.Persistence using (var cmd = _connection.CreateCommand()) { - cmd.CommandText = "select key,userid,rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate from userdata where userId=@userId"; + cmd.CommandText = "select key,userid,rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate,AudioStreamIndex,SubtitleStreamIndex from userdata where userId=@userId"; cmd.Parameters.Add(cmd, "@userId", DbType.Guid).Value = userId; @@ -350,6 +357,16 @@ namespace MediaBrowser.Server.Implementations.Persistence userData.LastPlayedDate = reader.GetDateTime(7).ToUniversalTime(); } + if (!reader.IsDBNull(8)) + { + userData.AudioStreamIndex = reader.GetInt32(8); + } + + if (!reader.IsDBNull(9)) + { + userData.SubtitleStreamIndex = reader.GetInt32(9); + } + return userData; } diff --git a/MediaBrowser.Server.Implementations/Photos/BaseDynamicImageProvider.cs b/MediaBrowser.Server.Implementations/Photos/BaseDynamicImageProvider.cs index c30d35ed2..4a69646f8 100644 --- a/MediaBrowser.Server.Implementations/Photos/BaseDynamicImageProvider.cs +++ b/MediaBrowser.Server.Implementations/Photos/BaseDynamicImageProvider.cs @@ -1,6 +1,5 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.IO; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; @@ -14,6 +13,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using CommonIO; +using MediaBrowser.Model.Configuration; namespace MediaBrowser.Server.Implementations.Photos { @@ -47,6 +47,41 @@ namespace MediaBrowser.Server.Implementations.Photos }; } + private IEnumerable<ImageType> GetEnabledImages(IHasImages item) + { + //var options = ProviderManager.GetMetadataOptions(item); + + return GetSupportedImages(item); + //return GetSupportedImages(item).Where(i => IsEnabled(options, i, item)).ToList(); + } + + private bool IsEnabled(MetadataOptions options, ImageType type, IHasImages item) + { + if (type == ImageType.Backdrop) + { + if (item.LockedFields.Contains(MetadataFields.Backdrops)) + { + return false; + } + } + else if (type == ImageType.Screenshot) + { + if (item.LockedFields.Contains(MetadataFields.Screenshots)) + { + return false; + } + } + else + { + if (item.LockedFields.Contains(MetadataFields.Images)) + { + return false; + } + } + + return options.IsEnabled(type); + } + public async Task<ItemUpdateType> FetchAsync(T item, MetadataRefreshOptions options, CancellationToken cancellationToken) { if (!Supports(item)) @@ -55,7 +90,7 @@ namespace MediaBrowser.Server.Implementations.Photos } var updateType = ItemUpdateType.None; - var supportedImages = GetSupportedImages(item).ToList(); + var supportedImages = GetEnabledImages(item).ToList(); if (supportedImages.Contains(ImageType.Primary)) { @@ -69,7 +104,6 @@ namespace MediaBrowser.Server.Implementations.Photos updateType = updateType | thumbResult; } - return updateType; } @@ -220,7 +254,7 @@ namespace MediaBrowser.Server.Implementations.Photos return false; } - var supportedImages = GetSupportedImages(item).ToList(); + var supportedImages = GetEnabledImages(item).ToList(); if (supportedImages.Contains(ImageType.Primary) && HasChanged(item, ImageType.Primary)) { diff --git a/MediaBrowser.Server.Implementations/Session/SessionManager.cs b/MediaBrowser.Server.Implementations/Session/SessionManager.cs index a9ce5ba54..08953b0be 100644 --- a/MediaBrowser.Server.Implementations/Session/SessionManager.cs +++ b/MediaBrowser.Server.Implementations/Session/SessionManager.cs @@ -680,7 +680,7 @@ namespace MediaBrowser.Server.Implementations.Session foreach (var user in users) { - await OnPlaybackProgress(user.Id, key, libraryItem, info.PositionTicks).ConfigureAwait(false); + await OnPlaybackProgress(user, key, libraryItem, info).ConfigureAwait(false); } } @@ -712,15 +712,40 @@ namespace MediaBrowser.Server.Implementations.Session StartIdleCheckTimer(); } - private async Task OnPlaybackProgress(Guid userId, string userDataKey, BaseItem item, long? positionTicks) + private async Task OnPlaybackProgress(User user, string userDataKey, BaseItem item, PlaybackProgressInfo info) { - var data = _userDataRepository.GetUserData(userId, userDataKey); + var data = _userDataRepository.GetUserData(user.Id, userDataKey); + + var positionTicks = info.PositionTicks; if (positionTicks.HasValue) { _userDataRepository.UpdatePlayState(item, data, positionTicks.Value); - await _userDataRepository.SaveUserData(userId, item, data, UserDataSaveReason.PlaybackProgress, CancellationToken.None).ConfigureAwait(false); + UpdatePlaybackSettings(user, info, data); + + await _userDataRepository.SaveUserData(user.Id, item, data, UserDataSaveReason.PlaybackProgress, CancellationToken.None).ConfigureAwait(false); + } + } + + private void UpdatePlaybackSettings(User user, PlaybackProgressInfo info, UserItemData data) + { + if (user.Configuration.RememberAudioSelections) + { + data.AudioStreamIndex = info.AudioStreamIndex; + } + else + { + data.AudioStreamIndex = null; + } + + if (user.Configuration.RememberSubtitleSelections) + { + data.SubtitleStreamIndex = info.SubtitleStreamIndex; + } + else + { + data.SubtitleStreamIndex = null; } } @@ -958,6 +983,26 @@ namespace MediaBrowser.Server.Implementations.Session } } + if (user != null && command.ItemIds.Length == 1 && user.Configuration.EnableNextEpisodeAutoPlay) + { + var episode = _libraryManager.GetItemById(command.ItemIds[0]) as Episode; + if (episode != null) + { + var series = episode.Series; + if (series != null) + { + var episodes = series.GetEpisodes(user, false, false) + .SkipWhile(i => i.Id != episode.Id) + .ToList(); + + if (episodes.Count > 0) + { + command.ItemIds = episodes.Select(i => i.Id.ToString("N")).ToArray(); + } + } + } + } + var controllingSession = GetSession(controllingSessionId); AssertCanControl(session, controllingSession); if (controllingSession.UserId.HasValue) @@ -1268,7 +1313,17 @@ namespace MediaBrowser.Server.Implementations.Session /// </summary> /// <param name="request">The request.</param> /// <returns>Task{SessionInfo}.</returns> - public async Task<AuthenticationResult> AuthenticateNewSession(AuthenticationRequest request) + public Task<AuthenticationResult> AuthenticateNewSession(AuthenticationRequest request) + { + return AuthenticateNewSessionInternal(request, true); + } + + public Task<AuthenticationResult> CreateNewSession(AuthenticationRequest request) + { + return AuthenticateNewSessionInternal(request, false); + } + + private async Task<AuthenticationResult> AuthenticateNewSessionInternal(AuthenticationRequest request, bool enforcePassword) { var user = _userManager.Users .FirstOrDefault(i => string.Equals(request.Username, i.Name, StringComparison.OrdinalIgnoreCase)); @@ -1281,13 +1336,16 @@ namespace MediaBrowser.Server.Implementations.Session } } - var result = await _userManager.AuthenticateUser(request.Username, request.PasswordSha1, request.PasswordMd5, request.RemoteEndPoint).ConfigureAwait(false); - - if (!result) + if (enforcePassword) { - EventHelper.FireEventIfNotNull(AuthenticationFailed, this, new GenericEventArgs<AuthenticationRequest>(request), _logger); + var result = await _userManager.AuthenticateUser(request.Username, request.PasswordSha1, request.PasswordMd5, request.RemoteEndPoint).ConfigureAwait(false); + + if (!result) + { + EventHelper.FireEventIfNotNull(AuthenticationFailed, this, new GenericEventArgs<AuthenticationRequest>(request), _logger); - throw new SecurityException("Invalid user or password entered."); + throw new SecurityException("Invalid user or password entered."); + } } var token = await GetAuthorizationToken(user.Id.ToString("N"), request.DeviceId, request.App, request.AppVersion, request.DeviceName).ConfigureAwait(false); @@ -1310,7 +1368,8 @@ namespace MediaBrowser.Server.Implementations.Session ServerId = _appHost.SystemId }; } - + + private async Task<string> GetAuthorizationToken(string userId, string deviceId, string app, string appVersion, string deviceName) { var existing = _authRepo.Get(new AuthenticationInfoQuery diff --git a/MediaBrowser.Server.Implementations/Sync/SyncHelper.cs b/MediaBrowser.Server.Implementations/Sync/SyncHelper.cs index b6242950f..fb4e0c6be 100644 --- a/MediaBrowser.Server.Implementations/Sync/SyncHelper.cs +++ b/MediaBrowser.Server.Implementations/Sync/SyncHelper.cs @@ -10,11 +10,11 @@ namespace MediaBrowser.Server.Implementations.Sync { if (string.Equals(quality, "medium", StringComparison.OrdinalIgnoreCase)) { - profileBitrate = Math.Min(Convert.ToInt32(profileBitrate.Value * .7), 4000000); + profileBitrate = Math.Min(profileBitrate.Value, 4000000); } else if (string.Equals(quality, "low", StringComparison.OrdinalIgnoreCase)) { - profileBitrate = Math.Min(Convert.ToInt32(profileBitrate.Value * .5), 1500000); + profileBitrate = Math.Min(profileBitrate.Value, 1500000); } } diff --git a/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs b/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs index 95934908d..7086735c0 100644 --- a/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs +++ b/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs @@ -19,6 +19,7 @@ using MediaBrowser.Model.Sync; using MoreLinq; using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Threading; @@ -125,7 +126,23 @@ namespace MediaBrowser.Server.Implementations.Sync private string GetSyncJobItemName(BaseItem item) { - return item.Name; + var name = item.Name; + var episode = item as Episode; + + if (episode != null) + { + if (episode.IndexNumber.HasValue) + { + name = "E" + episode.IndexNumber.Value.ToString(CultureInfo.InvariantCulture) + " - " + name; + } + + if (episode.ParentIndexNumber.HasValue) + { + name = "S" + episode.ParentIndexNumber.Value.ToString(CultureInfo.InvariantCulture) + ", " + name; + } + } + + return name; } public Task UpdateJobStatus(string id) @@ -291,6 +308,11 @@ namespace MediaBrowser.Server.Implementations.Sync throw new ArgumentException("Unrecognized category: " + category); } + if (parent == null) + { + return new List<BaseItem>(); + } + query.User = user; var result = await parent.GetItems(query).ConfigureAwait(false); @@ -321,6 +343,12 @@ namespace MediaBrowser.Server.Implementations.Sync return series.GetEpisodes(user, false, false); } + var season = item as Season; + if (season != null) + { + return season.GetEpisodes(user, false, false); + } + if (item.IsFolder) { var folder = (Folder)item; @@ -360,6 +388,9 @@ namespace MediaBrowser.Server.Implementations.Sync { await EnsureSyncJobItems(null, cancellationToken).ConfigureAwait(false); + // Look job items that are supposedly transfering, but need to be requeued because the synced files have been deleted somehow + await HandleDeletedSyncFiles(cancellationToken).ConfigureAwait(false); + // If it already has a converting status then is must have been aborted during conversion var result = _syncManager.GetJobItems(new SyncJobItemQuery { @@ -372,6 +403,28 @@ namespace MediaBrowser.Server.Implementations.Sync CleanDeadSyncFiles(); } + private async Task HandleDeletedSyncFiles(CancellationToken cancellationToken) + { + // Look job items that are supposedly transfering, but need to be requeued because the synced files have been deleted somehow + var result = _syncManager.GetJobItems(new SyncJobItemQuery + { + Statuses = new[] { SyncJobItemStatus.ReadyToTransfer, SyncJobItemStatus.Transferring }, + AddMetadata = false + }); + + foreach (var item in result.Items) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (string.IsNullOrWhiteSpace(item.OutputPath) || !_fileSystem.FileExists(item.OutputPath)) + { + item.Status = SyncJobItemStatus.Queued; + await _syncManager.UpdateSyncJobItemInternal(item).ConfigureAwait(false); + await UpdateJobStatus(item.JobId).ConfigureAwait(false); + } + } + } + private void CleanDeadSyncFiles() { // TODO @@ -699,7 +752,7 @@ namespace MediaBrowser.Server.Implementations.Sync var path = Path.Combine(temporaryPath, filename); - _fileSystem.CreateDirectory(Path.GetDirectoryName(path)); + _fileSystem.CreateDirectory(Path.GetDirectoryName(path)); using (var stream = await _subtitleEncoder.GetSubtitles(streamInfo.ItemId, streamInfo.MediaSourceId, subtitleStreamIndex, subtitleStreamInfo.Format, 0, null, cancellationToken).ConfigureAwait(false)) { diff --git a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs b/MediaBrowser.Server.Implementations/Sync/SyncManager.cs index 50a960956..8ebc8d91e 100644 --- a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs +++ b/MediaBrowser.Server.Implementations/Sync/SyncManager.cs @@ -559,6 +559,12 @@ namespace MediaBrowser.Server.Implementations.Sync jobItem.Status = SyncJobItemStatus.Synced; jobItem.Progress = 100; + await UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false); + + var processor = GetSyncJobProcessor(); + + await processor.UpdateJobStatus(jobItem.JobId).ConfigureAwait(false); + if (!string.IsNullOrWhiteSpace(jobItem.TemporaryPath)) { try @@ -573,12 +579,6 @@ namespace MediaBrowser.Server.Implementations.Sync _logger.ErrorException("Error deleting temporary job file: {0}", ex, jobItem.OutputPath); } } - - await UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false); - - var processor = GetSyncJobProcessor(); - - await processor.UpdateJobStatus(jobItem.JobId).ConfigureAwait(false); } private SyncJobProcessor GetSyncJobProcessor() @@ -1015,7 +1015,7 @@ namespace MediaBrowser.Server.Implementations.Sync { var jobItem = _repo.GetJobItem(id); - if (jobItem.Status != SyncJobItemStatus.Queued && jobItem.Status != SyncJobItemStatus.ReadyToTransfer && jobItem.Status != SyncJobItemStatus.Converting && jobItem.Status != SyncJobItemStatus.Failed && jobItem.Status != SyncJobItemStatus.Synced) + if (jobItem.Status != SyncJobItemStatus.Queued && jobItem.Status != SyncJobItemStatus.ReadyToTransfer && jobItem.Status != SyncJobItemStatus.Converting && jobItem.Status != SyncJobItemStatus.Failed && jobItem.Status != SyncJobItemStatus.Synced && jobItem.Status != SyncJobItemStatus.Transferring) { throw new ArgumentException("Operation is not valid for this job item"); } diff --git a/MediaBrowser.Server.Implementations/packages.config b/MediaBrowser.Server.Implementations/packages.config index 816f85b42..772790f37 100644 --- a/MediaBrowser.Server.Implementations/packages.config +++ b/MediaBrowser.Server.Implementations/packages.config @@ -1,11 +1,11 @@ <?xml version="1.0" encoding="utf-8"?>
<packages>
- <package id="CommonIO" version="1.0.0.7" targetFramework="net45" />
+ <package id="CommonIO" version="1.0.0.8" targetFramework="net45" />
<package id="Emby.XmlTv" version="1.0.0.48" targetFramework="net45" />
<package id="Interfaces.IO" version="1.0.0.5" targetFramework="net45" />
- <package id="MediaBrowser.Naming" version="1.0.0.47" targetFramework="net45" />
+ <package id="MediaBrowser.Naming" version="1.0.0.48" targetFramework="net45" />
<package id="Mono.Nat" version="1.2.24.0" targetFramework="net45" />
<package id="morelinq" version="1.4.0" targetFramework="net45" />
<package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
- <package id="SocketHttpListener" version="1.0.0.25" targetFramework="net45" />
+ <package id="SocketHttpListener" version="1.0.0.27" targetFramework="net45" />
</packages>
\ No newline at end of file diff --git a/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj b/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj index 7ad1c55f1..dde4c544e 100644 --- a/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj +++ b/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj @@ -476,6 +476,9 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\photos.html">
<Link>Resources\dashboard-ui\photos.html</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\pin.html">
+ <Link>Resources\dashboard-ui\pin.html</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\playbackconfiguration.html">
<Link>Resources\dashboard-ui\playbackconfiguration.html</Link>
</BundleResource>
@@ -1316,6 +1319,18 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\visibleinviewport.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\visibleinviewport.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\actionsheet\actionsheet.css">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\actionsheet\actionsheet.css</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\actionsheet\actionsheet.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\actionsheet\actionsheet.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\confirm\confirm.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\confirm\confirm.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\confirm\nativeconfirm.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\confirm\nativeconfirm.js</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\images\basicimagefetcher.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\images\basicimagefetcher.js</Link>
</BundleResource>
@@ -1877,6 +1892,9 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-demo-helpers\.gitignore">
<Link>Resources\dashboard-ui\bower_components\iron-demo-helpers\.gitignore</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-demo-helpers\.travis.yml">
+ <Link>Resources\dashboard-ui\bower_components\iron-demo-helpers\.travis.yml</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-demo-helpers\CONTRIBUTING.md">
<Link>Resources\dashboard-ui\bower_components\iron-demo-helpers\CONTRIBUTING.md</Link>
</BundleResource>
@@ -2012,6 +2030,9 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-flex-layout\index.html">
<Link>Resources\dashboard-ui\bower_components\iron-flex-layout\index.html</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-flex-layout\iron-flex-layout-classes.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-flex-layout\iron-flex-layout-classes.html</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-flex-layout\iron-flex-layout.html">
<Link>Resources\dashboard-ui\bower_components\iron-flex-layout\iron-flex-layout.html</Link>
</BundleResource>
@@ -2021,12 +2042,18 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-flex-layout\classes\iron-shadow-flex-layout.html">
<Link>Resources\dashboard-ui\bower_components\iron-flex-layout\classes\iron-shadow-flex-layout.html</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-flex-layout\demo\demo-snippet.html">
- <Link>Resources\dashboard-ui\bower_components\iron-flex-layout\demo\demo-snippet.html</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-flex-layout\demo\index.html">
<Link>Resources\dashboard-ui\bower_components\iron-flex-layout\demo\index.html</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-flex-layout\test\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-flex-layout\test\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-flex-layout\test\iron-flex-layout-classes.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-flex-layout\test\iron-flex-layout-classes.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-flex-layout\test\iron-flex-layout.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-flex-layout\test\iron-flex-layout.html</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-form-element-behavior\.bower.json">
<Link>Resources\dashboard-ui\bower_components\iron-form-element-behavior\.bower.json</Link>
</BundleResource>
@@ -2495,6 +2522,12 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-resizable-behavior\.gitignore">
<Link>Resources\dashboard-ui\bower_components\iron-resizable-behavior\.gitignore</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-resizable-behavior\.travis.yml">
+ <Link>Resources\dashboard-ui\bower_components\iron-resizable-behavior\.travis.yml</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-resizable-behavior\CONTRIBUTING.md">
+ <Link>Resources\dashboard-ui\bower_components\iron-resizable-behavior\CONTRIBUTING.md</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-resizable-behavior\README.md">
<Link>Resources\dashboard-ui\bower_components\iron-resizable-behavior\README.md</Link>
</BundleResource>
@@ -2651,50 +2684,17 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\.bower.json">
<Link>Resources\dashboard-ui\bower_components\jquery\.bower.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\.editorconfig">
- <Link>Resources\dashboard-ui\bower_components\jquery\.editorconfig</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\.gitattributes">
- <Link>Resources\dashboard-ui\bower_components\jquery\.gitattributes</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\.gitignore">
- <Link>Resources\dashboard-ui\bower_components\jquery\.gitignore</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\.jscsrc">
- <Link>Resources\dashboard-ui\bower_components\jquery\.jscsrc</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\.jshintignore">
- <Link>Resources\dashboard-ui\bower_components\jquery\.jshintignore</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\.jshintrc">
- <Link>Resources\dashboard-ui\bower_components\jquery\.jshintrc</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\.mailmap">
- <Link>Resources\dashboard-ui\bower_components\jquery\.mailmap</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\.npmignore">
- <Link>Resources\dashboard-ui\bower_components\jquery\.npmignore</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\.travis.yml">
- <Link>Resources\dashboard-ui\bower_components\jquery\.travis.yml</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\AUTHORS.txt">
<Link>Resources\dashboard-ui\bower_components\jquery\AUTHORS.txt</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\CONTRIBUTING.md">
- <Link>Resources\dashboard-ui\bower_components\jquery\CONTRIBUTING.md</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\Gruntfile.js">
- <Link>Resources\dashboard-ui\bower_components\jquery\Gruntfile.js</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\LICENSE.txt">
<Link>Resources\dashboard-ui\bower_components\jquery\LICENSE.txt</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\README.md">
<Link>Resources\dashboard-ui\bower_components\jquery\README.md</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\package.json">
- <Link>Resources\dashboard-ui\bower_components\jquery\package.json</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\jquery\bower.json</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\dist\jquery.js">
<Link>Resources\dashboard-ui\bower_components\jquery\dist\jquery.js</Link>
@@ -2705,44 +2705,14 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\dist\jquery.min.map">
<Link>Resources\dashboard-ui\bower_components\jquery\dist\jquery.min.map</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\external\npo\npo.js">
- <Link>Resources\dashboard-ui\bower_components\jquery\external\npo\npo.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\external\qunit\LICENSE.txt">
- <Link>Resources\dashboard-ui\bower_components\jquery\external\qunit\LICENSE.txt</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\external\qunit\MIT-LICENSE.txt">
- <Link>Resources\dashboard-ui\bower_components\jquery\external\qunit\MIT-LICENSE.txt</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\external\qunit\qunit.css">
- <Link>Resources\dashboard-ui\bower_components\jquery\external\qunit\qunit.css</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\external\qunit\qunit.js">
- <Link>Resources\dashboard-ui\bower_components\jquery\external\qunit\qunit.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\external\qunit-assert-step\MIT-LICENSE.txt">
- <Link>Resources\dashboard-ui\bower_components\jquery\external\qunit-assert-step\MIT-LICENSE.txt</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\external\qunit-assert-step\qunit-assert-step.js">
- <Link>Resources\dashboard-ui\bower_components\jquery\external\qunit-assert-step\qunit-assert-step.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\external\requirejs\require.js">
- <Link>Resources\dashboard-ui\bower_components\jquery\external\requirejs\require.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\external\sinon\sinon-1.14.1.js">
- <Link>Resources\dashboard-ui\bower_components\jquery\external\sinon\sinon-1.14.1.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\external\sizzle\LICENSE.txt">
- <Link>Resources\dashboard-ui\bower_components\jquery\external\sizzle\LICENSE.txt</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\dist\jquery.slim.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\dist\jquery.slim.js</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\external\sizzle\dist\sizzle.js">
- <Link>Resources\dashboard-ui\bower_components\jquery\external\sizzle\dist\sizzle.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\dist\jquery.slim.min.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\dist\jquery.slim.min.js</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\external\sizzle\dist\sizzle.min.js">
- <Link>Resources\dashboard-ui\bower_components\jquery\external\sizzle\dist\sizzle.min.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\external\sizzle\dist\sizzle.min.map">
- <Link>Resources\dashboard-ui\bower_components\jquery\external\sizzle\dist\sizzle.min.map</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\dist\jquery.slim.min.map">
+ <Link>Resources\dashboard-ui\bower_components\jquery\dist\jquery.slim.min.map</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\.jshintrc">
<Link>Resources\dashboard-ui\bower_components\jquery\src\.jshintrc</Link>
@@ -2810,6 +2780,9 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\serialize.js">
<Link>Resources\dashboard-ui\bower_components\jquery\src\serialize.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\support.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\support.js</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\traversing.js">
<Link>Resources\dashboard-ui\bower_components\jquery\src\traversing.js</Link>
</BundleResource>
@@ -2849,6 +2822,9 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\attributes\val.js">
<Link>Resources\dashboard-ui\bower_components\jquery\src\attributes\val.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\core\DOMEval.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\core\DOMEval.js</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\core\access.js">
<Link>Resources\dashboard-ui\bower_components\jquery\src\core\access.js</Link>
</BundleResource>
@@ -2888,12 +2864,24 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\data\Data.js">
<Link>Resources\dashboard-ui\bower_components\jquery\src\data\Data.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\data\accepts.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\data\accepts.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\data\support.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\data\support.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\deferred\exceptionHook.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\deferred\exceptionHook.js</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\effects\Tween.js">
<Link>Resources\dashboard-ui\bower_components\jquery\src\effects\Tween.js</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\effects\animatedSelector.js">
<Link>Resources\dashboard-ui\bower_components\jquery\src\effects\animatedSelector.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\effects\support.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\effects\support.js</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\event\ajax.js">
<Link>Resources\dashboard-ui\bower_components\jquery\src\event\ajax.js</Link>
</BundleResource>
@@ -2921,6 +2909,9 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\manipulation\buildFragment.js">
<Link>Resources\dashboard-ui\bower_components\jquery\src\manipulation\buildFragment.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\manipulation\createSafeFragment.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\manipulation\createSafeFragment.js</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\manipulation\getAll.js">
<Link>Resources\dashboard-ui\bower_components\jquery\src\manipulation\getAll.js</Link>
</BundleResource>
@@ -2939,366 +2930,6 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\traversing\findFilter.js">
<Link>Resources\dashboard-ui\bower_components\jquery\src\traversing\findFilter.js</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\.jshintrc">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\.jshintrc</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\delegatetest.html">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\delegatetest.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\hovertest.html">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\hovertest.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\index.html">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\index.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\jquery.js">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\jquery.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\localfile.html">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\localfile.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\networkerror.html">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\networkerror.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\promises_aplus_adapter.js">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\promises_aplus_adapter.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\readywait.html">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\readywait.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\xhtml.php">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\xhtml.php</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\1x1.jpg">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\1x1.jpg</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\atom+xml.php">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\atom+xml.php</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\badcall.js">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\badcall.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\badjson.js">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\badjson.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\cleanScript.html">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\cleanScript.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\dashboard.xml">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\dashboard.xml</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\echoData.php">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\echoData.php</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\echoQuery.php">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\echoQuery.php</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\errorWithJSON.php">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\errorWithJSON.php</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\errorWithText.php">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\errorWithText.php</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\etag.php">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\etag.php</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\headers.php">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\headers.php</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\if_modified_since.php">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\if_modified_since.php</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\iframe.html">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\iframe.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\jquery-1.9.1.js">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\jquery-1.9.1.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\json.php">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\json.php</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\json_obj.js">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\json_obj.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\jsonp.php">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\jsonp.php</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\name.html">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\name.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\name.php">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\name.php</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\nocontent.php">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\nocontent.php</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\params_html.php">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\params_html.php</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\readywaitasset.js">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\readywaitasset.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\readywaitloader.js">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\readywaitloader.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\script.php">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\script.php</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\statusText.php">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\statusText.php</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\test.html">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\test.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\test.php">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\test.php</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\test2.html">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\test2.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\test3.html">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\test3.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\testbar.php">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\testbar.php</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\testinit.js">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\testinit.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\testrunner.js">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\testrunner.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\testsuite.css">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\testsuite.css</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\text.php">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\text.php</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\with_fries.xml">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\with_fries.xml</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\with_fries_over_jsonp.php">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\with_fries_over_jsonp.php</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\ajax\content-type.php">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\ajax\content-type.php</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\ajax\evalScript.php">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\ajax\evalScript.php</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\ajax\method.php">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\ajax\method.php</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\ajax\onunload.html">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\ajax\onunload.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\ajax\unreleasedXHR.html">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\ajax\unreleasedXHR.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\core\aliased.html">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\core\aliased.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\core\cc_on.html">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\core\cc_on.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\core\dont_return.php">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\core\dont_return.php</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\core\dynamic_ready.html">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\core\dynamic_ready.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\core\onready.html">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\core\onready.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\css\cssWidthBeforeDocReady.html">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\css\cssWidthBeforeDocReady.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\data\dataAttrs.html">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\data\dataAttrs.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\dimensions\documentLarge.html">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\dimensions\documentLarge.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\event\focusElem.html">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\event\focusElem.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\event\focusinCrossFrame.html">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\event\focusinCrossFrame.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\event\interactiveReady.html">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\event\interactiveReady.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\event\longLoadScript.php">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\event\longLoadScript.php</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\event\onbeforeunload.html">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\event\onbeforeunload.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\event\promiseReady.html">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\event\promiseReady.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\event\syncReady.html">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\event\syncReady.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\event\triggerunload.html">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\event\triggerunload.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\manipulation\iframe-denied.html">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\manipulation\iframe-denied.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\offset\absolute.html">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\offset\absolute.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\offset\body.html">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\offset\body.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\offset\fixed.html">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\offset\fixed.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\offset\relative.html">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\offset\relative.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\offset\scroll.html">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\offset\scroll.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\offset\static.html">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\offset\static.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\offset\table.html">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\offset\table.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\selector\html5_selector.html">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\selector\html5_selector.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\selector\sizzle_cache.html">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\selector\sizzle_cache.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\support\bodyBackground.html">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\support\bodyBackground.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\support\csp-clean.php">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\support\csp-clean.php</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\support\csp-log.php">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\support\csp-log.php</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\support\csp.js">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\support\csp.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\support\csp.php">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\support\csp.php</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\data\support\getComputedSupport.js">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\data\support\getComputedSupport.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\integration\gh-1764-fullscreen.html">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\integration\gh-1764-fullscreen.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\integration\gh-2343-ie-radio-click.html">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\integration\gh-2343-ie-radio-click.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\integration\data\gh-1764-fullscreen-iframe.css">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\integration\data\gh-1764-fullscreen-iframe.css</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\integration\data\gh-1764-fullscreen-iframe.html">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\integration\data\gh-1764-fullscreen-iframe.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\integration\data\gh-1764-fullscreen.js">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\integration\data\gh-1764-fullscreen.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\node_smoke_tests\.jshintrc">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\node_smoke_tests\.jshintrc</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\node_smoke_tests\document_missing.js">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\node_smoke_tests\document_missing.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\node_smoke_tests\document_passed.js">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\node_smoke_tests\document_passed.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\node_smoke_tests\document_present_originally.js">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\node_smoke_tests\document_present_originally.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\node_smoke_tests\iterable_with_native_symbol.js">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\node_smoke_tests\iterable_with_native_symbol.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\node_smoke_tests\iterable_with_symbol_polyfill.js">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\node_smoke_tests\iterable_with_symbol_polyfill.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\node_smoke_tests\lib\ensure_global_not_created.js">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\node_smoke_tests\lib\ensure_global_not_created.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\node_smoke_tests\lib\ensure_iterability_es6.js">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\node_smoke_tests\lib\ensure_iterability_es6.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\node_smoke_tests\lib\ensure_jquery.js">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\node_smoke_tests\lib\ensure_jquery.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\unit\ajax.js">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\unit\ajax.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\unit\attributes.js">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\unit\attributes.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\unit\basic.js">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\unit\basic.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\unit\callbacks.js">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\unit\callbacks.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\unit\core.js">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\unit\core.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\unit\css.js">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\unit\css.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\unit\data.js">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\unit\data.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\unit\deferred.js">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\unit\deferred.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\unit\deprecated.js">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\unit\deprecated.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\unit\dimensions.js">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\unit\dimensions.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\unit\effects.js">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\unit\effects.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\unit\event.js">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\unit\event.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\unit\exports.js">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\unit\exports.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\unit\manipulation.js">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\unit\manipulation.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\unit\offset.js">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\unit\offset.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\unit\queue.js">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\unit\queue.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\unit\ready.js">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\unit\ready.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\unit\selector.js">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\unit\selector.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\unit\serialize.js">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\unit\serialize.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\unit\support.js">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\unit\support.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\unit\traversing.js">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\unit\traversing.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\test\unit\wrap.js">
- <Link>Resources\dashboard-ui\bower_components\jquery\test\unit\wrap.js</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jstree\.bower.json">
<Link>Resources\dashboard-ui\bower_components\jstree\.bower.json</Link>
</BundleResource>
@@ -8774,21 +8405,21 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\devices\ios\ios.css">
<Link>Resources\dashboard-ui\devices\ios\ios.css</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\devices\windowsphone\wp.css">
+ <Link>Resources\dashboard-ui\devices\windowsphone\wp.css</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\files\dummy.mp4">
<Link>Resources\dashboard-ui\files\dummy.mp4</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\legacy\buttonenabled.js">
<Link>Resources\dashboard-ui\legacy\buttonenabled.js</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\legacy\deferred.js">
- <Link>Resources\dashboard-ui\legacy\deferred.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\legacy\dashboard.js">
+ <Link>Resources\dashboard-ui\legacy\dashboard.js</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\aboutpage.js">
<Link>Resources\dashboard-ui\scripts\aboutpage.js</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\actionsheet.js">
- <Link>Resources\dashboard-ui\scripts\actionsheet.js</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\addpluginpage.js">
<Link>Resources\dashboard-ui\scripts\addpluginpage.js</Link>
</BundleResource>
@@ -9116,6 +8747,9 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\photos.js">
<Link>Resources\dashboard-ui\scripts\photos.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\pin.js">
+ <Link>Resources\dashboard-ui\scripts\pin.js</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\playbackconfiguration.js">
<Link>Resources\dashboard-ui\scripts\playbackconfiguration.js</Link>
</BundleResource>
@@ -9929,8 +9563,11 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\social-share-kit-1.0.4\dist\js\social-share-kit.min.js">
<Link>Resources\dashboard-ui\thirdparty\social-share-kit-1.0.4\dist\js\social-share-kit.min.js</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\voice\textprocessor-en-us.js">
- <Link>Resources\dashboard-ui\voice\textprocessor-en-us.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\voice\Readme.md">
+ <Link>Resources\dashboard-ui\voice\Readme.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\voice\grammarprocessor.js">
+ <Link>Resources\dashboard-ui\voice\grammarprocessor.js</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\voice\voice.css">
<Link>Resources\dashboard-ui\voice\voice.css</Link>
@@ -9938,5 +9575,38 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\voice\voice.js">
<Link>Resources\dashboard-ui\voice\voice.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\voice\voicecommands.js">
+ <Link>Resources\dashboard-ui\voice\voicecommands.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\voice\voicedialog.js">
+ <Link>Resources\dashboard-ui\voice\voicedialog.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\voice\commands\controlcommands.js">
+ <Link>Resources\dashboard-ui\voice\commands\controlcommands.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\voice\commands\disablecommands.js">
+ <Link>Resources\dashboard-ui\voice\commands\disablecommands.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\voice\commands\enablecommands.js">
+ <Link>Resources\dashboard-ui\voice\commands\enablecommands.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\voice\commands\playcommands.js">
+ <Link>Resources\dashboard-ui\voice\commands\playcommands.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\voice\commands\searchcommands.js">
+ <Link>Resources\dashboard-ui\voice\commands\searchcommands.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\voice\commands\showcommands.js">
+ <Link>Resources\dashboard-ui\voice\commands\showcommands.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\voice\commands\togglecommands.js">
+ <Link>Resources\dashboard-ui\voice\commands\togglecommands.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\voice\grammar\en-US.json">
+ <Link>Resources\dashboard-ui\voice\grammar\en-US.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\voice\grammar\grammar.json">
+ <Link>Resources\dashboard-ui\voice\grammar\grammar.json</Link>
+ </BundleResource>
</ItemGroup>
</Project>
\ No newline at end of file diff --git a/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj b/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj index 9f36599fa..e3c4541f0 100644 --- a/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj +++ b/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj @@ -54,7 +54,7 @@ <ItemGroup> <Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\CommonIO.1.0.0.7\lib\net45\CommonIO.dll</HintPath> + <HintPath>..\packages\CommonIO.1.0.0.8\lib\net45\CommonIO.dll</HintPath> </Reference> <Reference Include="Mono.Posix, Version=4.0.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> diff --git a/MediaBrowser.Server.Mono/packages.config b/MediaBrowser.Server.Mono/packages.config index 2956d69c6..99bcc7f4b 100644 --- a/MediaBrowser.Server.Mono/packages.config +++ b/MediaBrowser.Server.Mono/packages.config @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <packages> - <package id="CommonIO" version="1.0.0.7" targetFramework="net45" /> + <package id="CommonIO" version="1.0.0.8" targetFramework="net45" /> <package id="Mono.Posix" version="4.0.0.0" targetFramework="net45" /> <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" /> </packages>
\ No newline at end of file diff --git a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs index e61a64646..a54380850 100644 --- a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs +++ b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs @@ -490,7 +490,7 @@ namespace MediaBrowser.Server.Startup.Common ChannelManager = new ChannelManager(UserManager, DtoService, LibraryManager, LogManager.GetLogger("ChannelManager"), ServerConfigurationManager, FileSystemManager, UserDataManager, JsonSerializer, LocalizationManager, HttpClient, ProviderManager); RegisterSingleInstance(ChannelManager); - MediaSourceManager = new MediaSourceManager(ItemRepository, UserManager, LibraryManager, LogManager.GetLogger("MediaSourceManager"), JsonSerializer, FileSystemManager); + MediaSourceManager = new MediaSourceManager(ItemRepository, UserManager, LibraryManager, LogManager.GetLogger("MediaSourceManager"), JsonSerializer, FileSystemManager, UserDataManager); RegisterSingleInstance(MediaSourceManager); SessionManager = new SessionManager(UserDataManager, LogManager.GetLogger("SessionManager"), UserRepository, LibraryManager, UserManager, musicManager, DtoService, ImageProcessor, JsonSerializer, this, HttpClient, AuthenticationRepository, DeviceManager, MediaSourceManager); diff --git a/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj b/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj index a4028d5b0..dbbd054a8 100644 --- a/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj +++ b/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj @@ -33,7 +33,7 @@ <ItemGroup> <Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\CommonIO.1.0.0.7\lib\net45\CommonIO.dll</HintPath> + <HintPath>..\packages\CommonIO.1.0.0.8\lib\net45\CommonIO.dll</HintPath> </Reference> <Reference Include="Mono.Posix, Version=4.0.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> diff --git a/MediaBrowser.Server.Startup.Common/packages.config b/MediaBrowser.Server.Startup.Common/packages.config index 2956d69c6..99bcc7f4b 100644 --- a/MediaBrowser.Server.Startup.Common/packages.config +++ b/MediaBrowser.Server.Startup.Common/packages.config @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <packages> - <package id="CommonIO" version="1.0.0.7" targetFramework="net45" /> + <package id="CommonIO" version="1.0.0.8" targetFramework="net45" /> <package id="Mono.Posix" version="4.0.0.0" targetFramework="net45" /> <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" /> </packages>
\ No newline at end of file diff --git a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj index 80e56d6e1..d8eed80bd 100644 --- a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj +++ b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj @@ -63,7 +63,7 @@ <ItemGroup> <Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\CommonIO.1.0.0.7\lib\net45\CommonIO.dll</HintPath> + <HintPath>..\packages\CommonIO.1.0.0.8\lib\net45\CommonIO.dll</HintPath> </Reference> <Reference Include="ImageMagickSharp, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> diff --git a/MediaBrowser.ServerApplication/packages.config b/MediaBrowser.ServerApplication/packages.config index be12bcd5c..fd2f2c806 100644 --- a/MediaBrowser.ServerApplication/packages.config +++ b/MediaBrowser.ServerApplication/packages.config @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <packages> - <package id="CommonIO" version="1.0.0.7" targetFramework="net45" /> + <package id="CommonIO" version="1.0.0.8" targetFramework="net45" /> <package id="ImageMagickSharp" version="1.0.0.18" targetFramework="net45" /> <package id="MediaBrowser.IsoMounting" version="3.0.69" targetFramework="net45" /> <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" /> diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs index 227026154..788825a7b 100644 --- a/MediaBrowser.WebDashboard/Api/DashboardService.cs +++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs @@ -345,7 +345,6 @@ namespace MediaBrowser.WebDashboard.Api DeleteFoldersByName(bowerPath, "grunt"); DeleteFoldersByName(bowerPath, "rollups"); - _fileSystem.DeleteDirectory(Path.Combine(bowerPath, "jquery", "external"), true); _fileSystem.DeleteDirectory(Path.Combine(bowerPath, "jquery", "src"), true); DeleteCryptoFiles(Path.Combine(bowerPath, "cryptojslib", "components")); diff --git a/MediaBrowser.WebDashboard/Api/PackageCreator.cs b/MediaBrowser.WebDashboard/Api/PackageCreator.cs index 67eb3d53b..c3db09457 100644 --- a/MediaBrowser.WebDashboard/Api/PackageCreator.cs +++ b/MediaBrowser.WebDashboard/Api/PackageCreator.cs @@ -354,7 +354,7 @@ namespace MediaBrowser.WebDashboard.Api if (string.Equals(mode, "cordova", StringComparison.OrdinalIgnoreCase)) { - sb.Append("<meta http-equiv=\"Content-Security-Policy\" content=\"default-src * 'unsafe-inline' 'unsafe-eval'\">"); + sb.Append("<meta http-equiv=\"Content-Security-Policy\" content=\"default-src * 'unsafe-inline' 'unsafe-eval' data:;\">"); } sb.Append("<link rel=\"manifest\" href=\"manifest.json\">"); diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj index 9de20cb39..b8bb397fc 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -49,7 +49,7 @@ <ItemGroup>
<Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
- <HintPath>..\packages\CommonIO.1.0.0.7\lib\net45\CommonIO.dll</HintPath>
+ <HintPath>..\packages\CommonIO.1.0.0.8\lib\net45\CommonIO.dll</HintPath>
</Reference>
<Reference Include="Patterns.Logging">
<HintPath>..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.dll</HintPath>
@@ -140,6 +140,9 @@ <Content Include="dashboard-ui\components\remotecontrolautoplay.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="dashboard-ui\devices\windowsphone\wp.css">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\legacy\buttonenabled.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -254,9 +257,9 @@ <Content Include="dashboard-ui\favorites.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <None Include="dashboard-ui\legacy\deferred.js">
+ <Content Include="dashboard-ui\legacy\dashboard.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </None>
+ </Content>
<Content Include="dashboard-ui\livetvguideprovider.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -278,9 +281,6 @@ <Content Include="dashboard-ui\robots.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\scripts\actionsheet.js">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\scripts\globalize.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -460,12 +460,6 @@ <Content Include="dashboard-ui\components\tvproviders\schedulesdirect.template.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\voice\textprocessor-en-us.js">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\voice\voice.css">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\dashboardhosting.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -487,9 +481,6 @@ <Content Include="dashboard-ui\photos.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\scripts\appsettings.js">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\scripts\dashboardhosting.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -544,9 +535,6 @@ <Content Include="dashboard-ui\scripts\userpassword.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\voice\voice.js">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\scripts\wizardagreement.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -1066,334 +1054,70 @@ <Content Include="dashboard-ui\scripts\wizardsettings.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\action-black.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\action-white.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\alert-black.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\alert-white.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\arrow-d-black.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\arrow-d-l-black.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\arrow-d-l-white.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\arrow-d-r-black.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\arrow-d-r-white.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\arrow-d-white.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\arrow-l-black.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\arrow-l-white.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\arrow-r-black.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\arrow-r-white.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\arrow-u-black.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\arrow-u-l-black.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\arrow-u-l-white.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\arrow-u-r-black.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\arrow-u-r-white.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\arrow-u-white.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\audio-black.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\audio-white.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\back-black.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\back-white.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\bars-black.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\bars-white.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\bullets-black.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\bullets-white.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\calendar-black.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\calendar-white.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\camera-black.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\camera-white.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\carat-d-black.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\carat-d-white.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\carat-l-black.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\carat-l-white.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\carat-r-black.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\carat-r-white.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\carat-u-black.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\carat-u-white.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\check-black.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\check-white.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\clock-black.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\clock-white.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\cloud-black.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\cloud-white.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\comment-black.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\comment-white.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\delete-black.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\delete-white.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\edit-black.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\edit-white.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\eye-black.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\eye-white.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\forbidden-black.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\forbidden-white.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\forward-black.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\forward-white.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\gear-black.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\gear-white.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\grid-black.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\grid-white.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\heart-black.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\heart-white.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\home-black.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\home-white.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\info-black.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\info-white.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\location-black.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\location-white.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\lock-black.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\lock-white.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\mail-black.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\mail-white.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\minus-black.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\minus-white.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\navigation-black.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\navigation-white.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\phone-black.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\phone-white.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\plus-black.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\plus-white.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\power-black.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\power-white.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\recycle-black.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\recycle-white.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\refresh-black.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\refresh-white.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\search-black.svg">
+ <Content Include="dashboard-ui\livetv.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\search-white.svg">
+ <Content Include="dashboard-ui\thirdparty\jstree\themes\default\32px.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\shop-black.svg">
+ <Content Include="dashboard-ui\thirdparty\jstree\themes\default\40px.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\shop-white.svg">
+ <Content Include="dashboard-ui\thirdparty\jstree\themes\default\style.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\star-black.svg">
+ <Content Include="dashboard-ui\thirdparty\jstree\themes\default\style.min.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\star-white.svg">
+ <Content Include="dashboard-ui\thirdparty\jstree\themes\default\throbber.gif">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\tag-black.svg">
+ <Content Include="dashboard-ui\userlibraryaccess.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\tag-white.svg">
+ <Content Include="dashboard-ui\usernew.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\user-black.svg">
+ <Content Include="dashboard-ui\userparentalcontrol.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\user-white.svg">
+ <Content Include="dashboard-ui\userpassword.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\video-black.svg">
+ <Content Include="dashboard-ui\voice\commands\controlcommands.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\video-white.svg">
+ <Content Include="dashboard-ui\voice\commands\disablecommands.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\livetv.html">
+ <Content Include="dashboard-ui\voice\commands\enablecommands.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\thirdparty\jstree\themes\default\32px.png">
+ <Content Include="dashboard-ui\voice\commands\playcommands.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\thirdparty\jstree\themes\default\40px.png">
+ <Content Include="dashboard-ui\voice\commands\searchcommands.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\thirdparty\jstree\themes\default\style.css">
+ <Content Include="dashboard-ui\voice\commands\showcommands.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\thirdparty\jstree\themes\default\style.min.css">
+ <Content Include="dashboard-ui\voice\commands\togglecommands.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\thirdparty\jstree\themes\default\throbber.gif">
+ <Content Include="dashboard-ui\voice\grammarprocessor.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\userlibraryaccess.html">
+ <Content Include="dashboard-ui\voice\voicedialog.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\usernew.html">
+ <Content Include="dashboard-ui\voice\voice.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\userparentalcontrol.html">
+ <Content Include="dashboard-ui\voice\voice.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\userpassword.html">
+ <Content Include="dashboard-ui\voice\voicecommands.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="dashboard-ui\wizardagreement.html">
@@ -1963,247 +1687,124 @@ <None Include="dashboard-ui\css\fonts\roboto\VvXUGKZXbHtX_S_VCTLpGhTbgVql8nDJpwnrE27mub0.woff2">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
- <Content Include="dashboard-ui\strings\html\ar.json">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\html\bg-BG.json">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\html\ca.json">
+ <Content Include="dashboard-ui\strings\ar.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\strings\html\cs.json">
+ <Content Include="dashboard-ui\strings\bg-BG.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\strings\html\da.json">
+ <Content Include="dashboard-ui\strings\ca.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\strings\html\de.json">
+ <Content Include="dashboard-ui\strings\cs.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\strings\html\el.json">
+ <Content Include="dashboard-ui\strings\da.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\strings\html\en-GB.json">
+ <Content Include="dashboard-ui\strings\de.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\strings\html\en-US.json">
+ <Content Include="dashboard-ui\strings\el.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\strings\html\es-AR.json">
+ <Content Include="dashboard-ui\strings\en-GB.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\strings\html\es-MX.json">
+ <Content Include="dashboard-ui\strings\en-US.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\strings\html\es.json">
+ <Content Include="dashboard-ui\strings\es-AR.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\strings\html\fi.json">
+ <Content Include="dashboard-ui\strings\es-MX.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\strings\html\fr.json">
+ <Content Include="dashboard-ui\strings\es.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\strings\html\gsw.json">
+ <Content Include="dashboard-ui\strings\fi.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\strings\html\he.json">
+ <Content Include="dashboard-ui\strings\fr.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\strings\html\hr.json">
+ <Content Include="dashboard-ui\strings\gsw.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\strings\html\it.json">
+ <Content Include="dashboard-ui\strings\he.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\strings\html\kk.json">
+ <Content Include="dashboard-ui\strings\hr.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\strings\html\ko.json">
+ <Content Include="dashboard-ui\strings\it.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\strings\html\ms.json">
+ <Content Include="dashboard-ui\strings\kk.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\strings\html\nb.json">
+ <Content Include="dashboard-ui\strings\ko.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\strings\html\nl.json">
+ <Content Include="dashboard-ui\strings\ms.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\strings\html\pl.json">
+ <Content Include="dashboard-ui\strings\nb.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\strings\html\pt-BR.json">
+ <Content Include="dashboard-ui\strings\nl.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\strings\html\pt-PT.json">
+ <Content Include="dashboard-ui\strings\pl.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\strings\html\ro.json">
+ <Content Include="dashboard-ui\strings\pt-BR.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\strings\html\ru.json">
+ <Content Include="dashboard-ui\strings\pt-PT.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\strings\html\server.json">
+ <Content Include="dashboard-ui\strings\ro.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\strings\html\sl-SI.json">
+ <Content Include="dashboard-ui\strings\ru.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\strings\html\sv.json">
+ <Content Include="dashboard-ui\strings\server.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\strings\html\tr.json">
+ <Content Include="dashboard-ui\strings\sl-SI.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\strings\html\uk.json">
+ <Content Include="dashboard-ui\strings\sv.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\strings\html\vi.json">
+ <Content Include="dashboard-ui\strings\tr.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\strings\html\zh-CN.json">
+ <Content Include="dashboard-ui\strings\uk.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\strings\html\zh-TW.json">
+ <Content Include="dashboard-ui\strings\vi.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\strings\javascript\ar.json">
+ <Content Include="dashboard-ui\strings\zh-CN.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\strings\javascript\be-BY.json">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\javascript\bg-BG.json">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\javascript\ca.json">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\javascript\cs.json">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\javascript\da.json">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\javascript\de.json">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\javascript\el.json">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\javascript\en-GB.json">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\javascript\en-US.json">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\javascript\es-AR.json">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\javascript\es-MX.json">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\javascript\es-VE.json">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\javascript\es.json">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\javascript\fi.json">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\javascript\fr.json">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\javascript\gsw.json">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\javascript\he.json">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\javascript\hr.json">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\javascript\hu.json">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\javascript\it.json">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\javascript\javascript.json">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\javascript\kk.json">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\javascript\ms.json">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\javascript\nb.json">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\javascript\nl.json">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\javascript\pl.json">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\javascript\pt-BR.json">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\javascript\pt-PT.json">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\javascript\ro.json">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\javascript\ru.json">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\javascript\sl-SI.json">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\javascript\sv.json">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\javascript\tr.json">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\javascript\uk.json">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\javascript\vi.json">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\javascript\zh-CN.json">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\javascript\zh-TW.json">
+ <Content Include="dashboard-ui\strings\zh-TW.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<None Include="dashboard-ui\manifest.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
- <None Include="dashboard-ui\strings\html\hu.json">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </None>
- <None Include="dashboard-ui\strings\html\id.json">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </None>
- <None Include="dashboard-ui\strings\html\zh-HK.json">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </None>
- <None Include="dashboard-ui\strings\javascript\id.json">
+ <None Include="dashboard-ui\strings\hu.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
- <None Include="dashboard-ui\strings\javascript\ko.json">
+ <None Include="dashboard-ui\strings\id.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
- <None Include="dashboard-ui\strings\javascript\zh-HK.json">
+ <None Include="dashboard-ui\strings\zh-HK.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\jqm.table.js">
@@ -2226,6 +1827,15 @@ </None>
<None Include="dashboard-ui\thirdparty\social-share-kit-1.0.4\LICENSE" />
<None Include="dashboard-ui\thirdparty\social-share-kit-1.0.4\README.md" />
+ <None Include="dashboard-ui\voice\grammar\en-US.json">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </None>
+ <None Include="dashboard-ui\voice\grammar\grammar.json">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </None>
+ <None Include="dashboard-ui\voice\Readme.md">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </None>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup />
diff --git a/MediaBrowser.WebDashboard/packages.config b/MediaBrowser.WebDashboard/packages.config index 128a2f162..f1f929f90 100644 --- a/MediaBrowser.WebDashboard/packages.config +++ b/MediaBrowser.WebDashboard/packages.config @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <packages> - <package id="CommonIO" version="1.0.0.7" targetFramework="net45" /> + <package id="CommonIO" version="1.0.0.8" targetFramework="net45" /> <package id="MediaBrowser.ApiClient.Javascript" version="3.0.249" targetFramework="net45" /> <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" /> <package id="WebMarkupMin.Core" version="1.0.1" targetFramework="net45" /> diff --git a/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj b/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj index 16b6ca995..73f4266fb 100644 --- a/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj +++ b/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj @@ -33,7 +33,7 @@ <ItemGroup> <Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\CommonIO.1.0.0.7\lib\net45\CommonIO.dll</HintPath> + <HintPath>..\packages\CommonIO.1.0.0.8\lib\net45\CommonIO.dll</HintPath> </Reference> <Reference Include="Patterns.Logging"> <HintPath>..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.dll</HintPath> diff --git a/MediaBrowser.XbmcMetadata/packages.config b/MediaBrowser.XbmcMetadata/packages.config index 28556744d..0639208dd 100644 --- a/MediaBrowser.XbmcMetadata/packages.config +++ b/MediaBrowser.XbmcMetadata/packages.config @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> <packages> - <package id="CommonIO" version="1.0.0.7" targetFramework="net45" /> + <package id="CommonIO" version="1.0.0.8" targetFramework="net45" /> <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" /> </packages>
\ No newline at end of file diff --git a/MediaBrowser.sln b/MediaBrowser.sln index a9b5020ec..a55ae200a 100644 --- a/MediaBrowser.sln +++ b/MediaBrowser.sln @@ -1,7 +1,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.31101.0 +# Visual Studio 14 +VisualStudioVersion = 14.0.24720.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{F0E0E64C-2A6F-4E35-9533-D53AC07C2CD1}" EndProject @@ -521,7 +521,4 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection - GlobalSection(Performance) = preSolution - HasPerformanceSessions = true - EndGlobalSection EndGlobal diff --git a/SharedVersion.cs b/SharedVersion.cs index 033efbbd7..22f7fffd1 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,4 +1,4 @@ using System.Reflection; //[assembly: AssemblyVersion("3.0.*")] -[assembly: AssemblyVersion("3.0.5871")] +[assembly: AssemblyVersion("3.0.5895")] |
