diff options
| author | Luke <luke.pulverenti@gmail.com> | 2016-09-09 15:42:40 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2016-09-09 15:42:40 -0400 |
| commit | 080a6511dc44edde960e6aefcab7e6cc068a3414 (patch) | |
| tree | 499c61fec48dcc080888d9cfc021baf734886fe7 | |
| parent | 51051c27cd4a30010eee98f4f99ebb895e166a91 (diff) | |
| parent | 5ad606a2232c6911e2cd8cf5d03da635c7c0c75d (diff) | |
Merge pull request #2151 from MediaBrowser/beta
Beta
148 files changed, 2951 insertions, 1108 deletions
diff --git a/MediaBrowser.Api/ItemUpdateService.cs b/MediaBrowser.Api/ItemUpdateService.cs index 2778cfe29..cda7ce0c6 100644 --- a/MediaBrowser.Api/ItemUpdateService.cs +++ b/MediaBrowser.Api/ItemUpdateService.cs @@ -70,12 +70,13 @@ namespace MediaBrowser.Api Cultures = _localizationManager.GetCultures().ToList() }; - if (!item.IsVirtualItem && !(item is ICollectionFolder) && !(item is UserView) && !(item is AggregateFolder) && !(item is LiveTvChannel) && !(item is IItemByName)) + if (!item.IsVirtualItem && !(item is ICollectionFolder) && !(item is UserView) && !(item is AggregateFolder) && !(item is LiveTvChannel) && !(item is IItemByName) && + item.SourceType == SourceType.Library) { var inheritedContentType = _libraryManager.GetInheritedContentType(item); var configuredContentType = _libraryManager.GetConfiguredContentType(item); - if (string.IsNullOrWhiteSpace(inheritedContentType) || string.Equals(inheritedContentType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase) || !string.IsNullOrWhiteSpace(configuredContentType)) + if (string.IsNullOrWhiteSpace(inheritedContentType) || !string.IsNullOrWhiteSpace(configuredContentType)) { info.ContentTypeOptions = GetContentTypeOptions(true); info.ContentType = configuredContentType; diff --git a/MediaBrowser.Api/Library/LibraryService.cs b/MediaBrowser.Api/Library/LibraryService.cs index 14a771db0..c6b637f01 100644 --- a/MediaBrowser.Api/Library/LibraryService.cs +++ b/MediaBrowser.Api/Library/LibraryService.cs @@ -25,6 +25,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using CommonIO; +using MediaBrowser.Controller.Configuration; namespace MediaBrowser.Api.Library { @@ -288,12 +289,13 @@ namespace MediaBrowser.Api.Library private readonly ITVSeriesManager _tvManager; private readonly ILibraryMonitor _libraryMonitor; private readonly IFileSystem _fileSystem; + private readonly IServerConfigurationManager _config; /// <summary> /// Initializes a new instance of the <see cref="LibraryService" /> class. /// </summary> public LibraryService(IItemRepository itemRepo, ILibraryManager libraryManager, IUserManager userManager, - IDtoService dtoService, IUserDataManager userDataManager, IAuthorizationContext authContext, IActivityManager activityManager, ILocalizationManager localization, ILiveTvManager liveTv, ITVSeriesManager tvManager, ILibraryMonitor libraryMonitor, IFileSystem fileSystem) + IDtoService dtoService, IUserDataManager userDataManager, IAuthorizationContext authContext, IActivityManager activityManager, ILocalizationManager localization, ILiveTvManager liveTv, ITVSeriesManager tvManager, ILibraryMonitor libraryMonitor, IFileSystem fileSystem, IServerConfigurationManager config) { _itemRepo = itemRepo; _libraryManager = libraryManager; @@ -307,6 +309,7 @@ namespace MediaBrowser.Api.Library _tvManager = tvManager; _libraryMonitor = libraryMonitor; _fileSystem = fileSystem; + _config = config; } public object Get(GetSimilarItems request) @@ -377,7 +380,7 @@ namespace MediaBrowser.Api.Library if (item is Movie || (program != null && program.IsMovie) || item is Trailer) { - return new MoviesService(_userManager, _userDataManager, _libraryManager, _itemRepo, _dtoService) + return new MoviesService(_userManager, _userDataManager, _libraryManager, _itemRepo, _dtoService, _config) { AuthorizationContext = AuthorizationContext, Logger = Logger, diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs index 545ac162f..3ad0ec1ba 100644 --- a/MediaBrowser.Api/LiveTv/LiveTvService.cs +++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs @@ -155,12 +155,72 @@ namespace MediaBrowser.Api.LiveTv [ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")] public bool? EnableUserData { get; set; } + public bool? IsMovie { get; set; } + public bool? IsSeries { get; set; } + public bool? IsKids { get; set; } + public bool? IsSports { get; set; } + public GetRecordings() { EnableTotalRecordCount = true; } } + [Route("/LiveTv/Recordings/Series", "GET", Summary = "Gets live tv recordings")] + [Authenticated] + public class GetRecordingSeries : IReturn<QueryResult<BaseItemDto>>, IHasDtoOptions + { + [ApiMember(Name = "ChannelId", Description = "Optional filter by channel id.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] + public string ChannelId { get; set; } + + [ApiMember(Name = "UserId", Description = "Optional filter by user and attach user data.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] + public string UserId { get; set; } + + [ApiMember(Name = "GroupId", Description = "Optional filter by recording group.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] + public string GroupId { get; set; } + + [ApiMember(Name = "StartIndex", Description = "Optional. The record index to start at. All items with a lower index will be dropped from the results.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] + public int? StartIndex { get; set; } + + [ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] + public int? Limit { get; set; } + + [ApiMember(Name = "Status", Description = "Optional filter by recording status.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] + public RecordingStatus? Status { get; set; } + + [ApiMember(Name = "Status", Description = "Optional filter by recordings that are in progress, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] + public bool? IsInProgress { get; set; } + + [ApiMember(Name = "SeriesTimerId", Description = "Optional filter by recordings belonging to a series timer", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] + public string SeriesTimerId { get; set; } + + [ApiMember(Name = "EnableImages", Description = "Optional, include image information in output", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")] + public bool? EnableImages { get; set; } + + [ApiMember(Name = "ImageTypeLimit", Description = "Optional, the max number of images to return, per image type", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] + public int? ImageTypeLimit { get; set; } + + [ApiMember(Name = "EnableImageTypes", Description = "Optional. The image types to include in the output.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] + public string EnableImageTypes { get; set; } + + /// <summary> + /// Fields to return within the items, in addition to basic information + /// </summary> + /// <value>The fields.</value> + [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, CriticRatingSummary, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] + public string Fields { get; set; } + + public bool EnableTotalRecordCount { get; set; } + + [ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")] + public bool? EnableUserData { get; set; } + + public GetRecordingSeries() + { + EnableTotalRecordCount = true; + } + } + [Route("/LiveTv/Recordings/Groups", "GET", Summary = "Gets live tv recording groups")] [Authenticated] public class GetRecordingGroups : IReturn<QueryResult<BaseItemDto>> @@ -535,12 +595,6 @@ namespace MediaBrowser.Api.LiveTv [Authenticated] public class GetLiveTvRegistrationInfo : IReturn<MBRegistrationRecord> { - [ApiMember(Name = "ChannelId", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string ChannelId { get; set; } - - [ApiMember(Name = "ProgramId", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string ProgramId { get; set; } - [ApiMember(Name = "Feature", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] public string Feature { get; set; } } @@ -592,7 +646,7 @@ namespace MediaBrowser.Api.LiveTv public async Task<object> Get(GetLiveTvRegistrationInfo request) { - var result = await _liveTvManager.GetRegistrationInfo(request.ChannelId, request.ProgramId, request.Feature).ConfigureAwait(false); + var result = await _liveTvManager.GetRegistrationInfo(request.Feature).ConfigureAwait(false); return ToOptimizedResult(result); } @@ -863,6 +917,32 @@ namespace MediaBrowser.Api.LiveTv Status = request.Status, SeriesTimerId = request.SeriesTimerId, IsInProgress = request.IsInProgress, + EnableTotalRecordCount = request.EnableTotalRecordCount, + IsMovie = request.IsMovie, + IsSeries = request.IsSeries, + IsKids = request.IsKids, + IsSports = request.IsSports + + }, options, CancellationToken.None).ConfigureAwait(false); + + return ToOptimizedResult(result); + } + + public async Task<object> Get(GetRecordingSeries request) + { + var options = GetDtoOptions(request); + options.DeviceId = AuthorizationContext.GetAuthorizationInfo(Request).DeviceId; + + var result = await _liveTvManager.GetRecordingSeries(new RecordingQuery + { + ChannelId = request.ChannelId, + UserId = request.UserId, + GroupId = request.GroupId, + StartIndex = request.StartIndex, + Limit = request.Limit, + Status = request.Status, + SeriesTimerId = request.SeriesTimerId, + IsInProgress = request.IsInProgress, EnableTotalRecordCount = request.EnableTotalRecordCount }, options, CancellationToken.None).ConfigureAwait(false); diff --git a/MediaBrowser.Api/Movies/MoviesService.cs b/MediaBrowser.Api/Movies/MoviesService.cs index 66f2fac81..f153a0475 100644 --- a/MediaBrowser.Api/Movies/MoviesService.cs +++ b/MediaBrowser.Api/Movies/MoviesService.cs @@ -14,6 +14,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.LiveTv; namespace MediaBrowser.Api.Movies @@ -88,6 +89,7 @@ namespace MediaBrowser.Api.Movies private readonly IItemRepository _itemRepo; private readonly IDtoService _dtoService; + private readonly IServerConfigurationManager _config; /// <summary> /// Initializes a new instance of the <see cref="MoviesService" /> class. @@ -97,13 +99,14 @@ namespace MediaBrowser.Api.Movies /// <param name="libraryManager">The library manager.</param> /// <param name="itemRepo">The item repo.</param> /// <param name="dtoService">The dto service.</param> - public MoviesService(IUserManager userManager, IUserDataManager userDataRepository, ILibraryManager libraryManager, IItemRepository itemRepo, IDtoService dtoService) + public MoviesService(IUserManager userManager, IUserDataManager userDataRepository, ILibraryManager libraryManager, IItemRepository itemRepo, IDtoService dtoService, IServerConfigurationManager config) { _userManager = userManager; _userDataRepository = userDataRepository; _libraryManager = libraryManager; _itemRepo = itemRepo; _dtoService = dtoService; + _config = config; } /// <summary> @@ -146,15 +149,17 @@ namespace MediaBrowser.Api.Movies (!string.IsNullOrWhiteSpace(request.UserId) ? user.RootFolder : _libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id); + var itemTypes = new List<string> { typeof(Movie).Name }; + if (_config.Configuration.EnableExternalContentInSuggestions) + { + itemTypes.Add(typeof(Trailer).Name); + itemTypes.Add(typeof(LiveTvProgram).Name); + } + var itemsResult = _libraryManager.GetItemList(new InternalItemsQuery(user) { Limit = request.Limit, - IncludeItemTypes = new[] - { - typeof(Movie).Name, - typeof(Trailer).Name, - typeof(LiveTvProgram).Name - }, + IncludeItemTypes = itemTypes.ToArray(), IsMovie = true, SimilarTo = item, EnableGroupByMetadataKey = true @@ -198,14 +203,16 @@ namespace MediaBrowser.Api.Movies var recentlyPlayedMovies = _libraryManager.GetItemList(query).ToList(); + var itemTypes = new List<string> { typeof(Movie).Name }; + if (_config.Configuration.EnableExternalContentInSuggestions) + { + itemTypes.Add(typeof(Trailer).Name); + itemTypes.Add(typeof(LiveTvProgram).Name); + } + var likedMovies = _libraryManager.GetItemList(new InternalItemsQuery(user) { - IncludeItemTypes = new[] - { - typeof(Movie).Name, - typeof(Trailer).Name, - typeof(LiveTvProgram).Name - }, + IncludeItemTypes = itemTypes.ToArray(), IsMovie = true, SortBy = new[] { ItemSortBy.Random }, SortOrder = SortOrder.Descending, @@ -278,6 +285,13 @@ namespace MediaBrowser.Api.Movies private IEnumerable<RecommendationDto> GetWithDirector(User user, IEnumerable<string> names, int itemLimit, DtoOptions dtoOptions, RecommendationType type) { + var itemTypes = new List<string> { typeof(Movie).Name }; + if (_config.Configuration.EnableExternalContentInSuggestions) + { + itemTypes.Add(typeof(Trailer).Name); + itemTypes.Add(typeof(LiveTvProgram).Name); + } + foreach (var name in names) { var items = _libraryManager.GetItemList(new InternalItemsQuery(user) @@ -286,12 +300,7 @@ namespace MediaBrowser.Api.Movies // Account for duplicates by imdb id, since the database doesn't support this yet Limit = itemLimit + 2, PersonTypes = new[] { PersonType.Director }, - IncludeItemTypes = new[] - { - typeof(Movie).Name, - typeof(Trailer).Name, - typeof(LiveTvProgram).Name - }, + IncludeItemTypes = itemTypes.ToArray(), IsMovie = true, EnableGroupByMetadataKey = true @@ -314,6 +323,13 @@ namespace MediaBrowser.Api.Movies private IEnumerable<RecommendationDto> GetWithActor(User user, IEnumerable<string> names, int itemLimit, DtoOptions dtoOptions, RecommendationType type) { + var itemTypes = new List<string> { typeof(Movie).Name }; + if (_config.Configuration.EnableExternalContentInSuggestions) + { + itemTypes.Add(typeof(Trailer).Name); + itemTypes.Add(typeof(LiveTvProgram).Name); + } + foreach (var name in names) { var items = _libraryManager.GetItemList(new InternalItemsQuery(user) @@ -321,12 +337,7 @@ namespace MediaBrowser.Api.Movies Person = name, // Account for duplicates by imdb id, since the database doesn't support this yet Limit = itemLimit + 2, - IncludeItemTypes = new[] - { - typeof(Movie).Name, - typeof(Trailer).Name, - typeof(LiveTvProgram).Name - }, + IncludeItemTypes = itemTypes.ToArray(), IsMovie = true, EnableGroupByMetadataKey = true @@ -349,17 +360,19 @@ namespace MediaBrowser.Api.Movies private IEnumerable<RecommendationDto> GetSimilarTo(User user, List<BaseItem> baselineItems, int itemLimit, DtoOptions dtoOptions, RecommendationType type) { + var itemTypes = new List<string> { typeof(Movie).Name }; + if (_config.Configuration.EnableExternalContentInSuggestions) + { + itemTypes.Add(typeof(Trailer).Name); + itemTypes.Add(typeof(LiveTvProgram).Name); + } + foreach (var item in baselineItems) { var similar = _libraryManager.GetItemList(new InternalItemsQuery(user) { Limit = itemLimit, - IncludeItemTypes = new[] - { - typeof(Movie).Name, - typeof(Trailer).Name, - typeof(LiveTvProgram).Name - }, + IncludeItemTypes = itemTypes.ToArray(), IsMovie = true, SimilarTo = item, EnableGroupByMetadataKey = true diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 4af564a5a..7a40d5bd1 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -337,44 +337,60 @@ namespace MediaBrowser.Api.Playback /// Gets the video bitrate to specify on the command line /// </summary> /// <param name="state">The state.</param> - /// <param name="videoCodec">The video codec.</param> + /// <param name="videoEncoder">The video codec.</param> /// <returns>System.String.</returns> - protected string GetVideoQualityParam(StreamState state, string videoCodec) + protected string GetVideoQualityParam(StreamState state, string videoEncoder) { var param = string.Empty; var isVc1 = state.VideoStream != null && string.Equals(state.VideoStream.Codec, "vc1", StringComparison.OrdinalIgnoreCase); - if (string.Equals(videoCodec, "libx264", StringComparison.OrdinalIgnoreCase)) + var encodingOptions = ApiEntryPoint.Instance.GetEncodingOptions(); + + if (string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase)) { - param = "-preset superfast"; + if (!string.IsNullOrWhiteSpace(encodingOptions.H264Preset)) + { + param += "-preset " + encodingOptions.H264Preset; + } + else + { + param += "-preset superfast"; + } - param += " -crf 23"; + if (encodingOptions.H264Crf >= 0 && encodingOptions.H264Crf <= 51) + { + param += " -crf " + encodingOptions.H264Crf.ToString(CultureInfo.InvariantCulture); + } + else + { + param += " -crf 23"; + } } - else if (string.Equals(videoCodec, "libx265", StringComparison.OrdinalIgnoreCase)) + else if (string.Equals(videoEncoder, "libx265", StringComparison.OrdinalIgnoreCase)) { - param = "-preset fast"; + param += "-preset fast"; param += " -crf 28"; } // h264 (h264_qsv) - else if (string.Equals(videoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase)) + else if (string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase)) { - param = "-preset 7 -look_ahead 0"; + param += "-preset 7 -look_ahead 0"; } // h264 (h264_nvenc) - else if (string.Equals(videoCodec, "h264_nvenc", StringComparison.OrdinalIgnoreCase)) + else if (string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase)) { - param = "-preset default"; + param += "-preset default"; } // webm - else if (string.Equals(videoCodec, "libvpx", StringComparison.OrdinalIgnoreCase)) + else if (string.Equals(videoEncoder, "libvpx", StringComparison.OrdinalIgnoreCase)) { // Values 0-3, 0 being highest quality but slower var profileScore = 0; @@ -394,30 +410,30 @@ namespace MediaBrowser.Api.Playback profileScore = Math.Min(profileScore, 2); // http://www.webmproject.org/docs/encoder-parameters/ - param = string.Format("-speed 16 -quality good -profile:v {0} -slices 8 -crf {1} -qmin {2} -qmax {3}", + param += string.Format("-speed 16 -quality good -profile:v {0} -slices 8 -crf {1} -qmin {2} -qmax {3}", profileScore.ToString(UsCulture), crf, qmin, qmax); } - else if (string.Equals(videoCodec, "mpeg4", StringComparison.OrdinalIgnoreCase)) + else if (string.Equals(videoEncoder, "mpeg4", StringComparison.OrdinalIgnoreCase)) { - param = "-mbd rd -flags +mv4+aic -trellis 2 -cmp 2 -subcmp 2 -bf 2"; + param += "-mbd rd -flags +mv4+aic -trellis 2 -cmp 2 -subcmp 2 -bf 2"; } // asf/wmv - else if (string.Equals(videoCodec, "wmv2", StringComparison.OrdinalIgnoreCase)) + else if (string.Equals(videoEncoder, "wmv2", StringComparison.OrdinalIgnoreCase)) { - param = "-qmin 2"; + param += "-qmin 2"; } - else if (string.Equals(videoCodec, "msmpeg4", StringComparison.OrdinalIgnoreCase)) + else if (string.Equals(videoEncoder, "msmpeg4", StringComparison.OrdinalIgnoreCase)) { - param = "-mbd 2"; + param += "-mbd 2"; } - param += GetVideoBitrateParam(state, videoCodec); + param += GetVideoBitrateParam(state, videoEncoder); var framerate = GetFramerateParam(state); if (framerate.HasValue) @@ -432,8 +448,8 @@ namespace MediaBrowser.Api.Playback if (!string.IsNullOrEmpty(state.VideoRequest.Profile)) { - if (!string.Equals(videoCodec, "h264_omx", StringComparison.OrdinalIgnoreCase) && - !string.Equals(videoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase)) + if (!string.Equals(videoEncoder, "h264_omx", StringComparison.OrdinalIgnoreCase) && + !string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase)) { // not supported by h264_omx param += " -profile:v " + state.VideoRequest.Profile; @@ -442,11 +458,13 @@ namespace MediaBrowser.Api.Playback if (!string.IsNullOrEmpty(state.VideoRequest.Level)) { + var level = NormalizeTranscodingLevel(state.OutputVideoCodec, state.VideoRequest.Level); + // h264_qsv and h264_nvenc expect levels to be expressed as a decimal. libx264 supports decimal and non-decimal format - if (string.Equals(videoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase) || - string.Equals(videoCodec, "h264_nvenc", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) || + string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase)) { - switch (state.VideoRequest.Level) + switch (level) { case "30": param += " -level 3"; @@ -476,20 +494,20 @@ namespace MediaBrowser.Api.Playback param += " -level 5.2"; break; default: - param += " -level " + state.VideoRequest.Level; + param += " -level " + level; break; } } - else if (!string.Equals(videoCodec, "h264_omx", StringComparison.OrdinalIgnoreCase)) + else if (!string.Equals(videoEncoder, "h264_omx", StringComparison.OrdinalIgnoreCase)) { - param += " -level " + state.VideoRequest.Level; + param += " -level " + level; } } - if (!string.Equals(videoCodec, "h264_omx", StringComparison.OrdinalIgnoreCase) && - !string.Equals(videoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase) && - !string.Equals(videoCodec, "h264_nvenc", StringComparison.OrdinalIgnoreCase) && - !string.Equals(videoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase)) + if (!string.Equals(videoEncoder, "h264_omx", StringComparison.OrdinalIgnoreCase) && + !string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) && + !string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase) && + !string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase)) { param = "-pix_fmt yuv420p " + param; } @@ -497,6 +515,25 @@ namespace MediaBrowser.Api.Playback return param; } + private string NormalizeTranscodingLevel(string videoCodec, string level) + { + double requestLevel; + + // Clients may direct play higher than level 41, but there's no reason to transcode higher + if (double.TryParse(level, NumberStyles.Any, UsCulture, out requestLevel)) + { + if (string.Equals(videoCodec, "h264", StringComparison.OrdinalIgnoreCase)) + { + if (requestLevel > 41) + { + return "41"; + } + } + } + + return level; + } + protected string GetAudioFilterParam(StreamState state, bool isHls) { var volParam = string.Empty; @@ -1160,17 +1197,21 @@ namespace MediaBrowser.Api.Playback await Task.Delay(100, cancellationTokenSource.Token).ConfigureAwait(false); } - if (state.IsInputVideo && transcodingJob.Type == TranscodingJobType.Progressive) + if (state.IsInputVideo && transcodingJob.Type == TranscodingJobType.Progressive && !transcodingJob.HasExited) { await Task.Delay(1000, cancellationTokenSource.Token).ConfigureAwait(false); - if (state.ReadInputAtNativeFramerate) + if (state.ReadInputAtNativeFramerate && !transcodingJob.HasExited) { await Task.Delay(1500, cancellationTokenSource.Token).ConfigureAwait(false); } } - StartThrottler(state, transcodingJob); + if (!transcodingJob.HasExited) + { + StartThrottler(state, transcodingJob); + } + ReportUsage(state); return transcodingJob; @@ -1770,6 +1811,15 @@ namespace MediaBrowser.Api.Playback // state.SegmentLength = 6; //} + if (state.VideoRequest != null) + { + if (!string.IsNullOrWhiteSpace(state.VideoRequest.VideoCodec)) + { + state.SupportedVideoCodecs = state.VideoRequest.VideoCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList(); + state.VideoRequest.VideoCodec = state.SupportedVideoCodecs.FirstOrDefault(); + } + } + if (!string.IsNullOrWhiteSpace(request.AudioCodec)) { state.SupportedAudioCodecs = request.AudioCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList(); @@ -2012,7 +2062,7 @@ namespace MediaBrowser.Api.Playback } // Source and target codecs must match - if (!string.Equals(request.VideoCodec, videoStream.Codec, StringComparison.OrdinalIgnoreCase)) + if (string.IsNullOrEmpty(videoStream.Codec) || !state.SupportedVideoCodecs.Contains(videoStream.Codec, StringComparer.OrdinalIgnoreCase)) { return false; } diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index 91e62b4e3..656b2ee08 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -17,6 +17,7 @@ using System.Threading; using System.Threading.Tasks; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.MediaEncoding; +using MediaBrowser.Model.Serialization; namespace MediaBrowser.Api.Playback { @@ -70,8 +71,9 @@ namespace MediaBrowser.Api.Playback private readonly INetworkManager _networkManager; private readonly IMediaEncoder _mediaEncoder; private readonly IUserManager _userManager; + private readonly IJsonSerializer _json; - public MediaInfoService(IMediaSourceManager mediaSourceManager, IDeviceManager deviceManager, ILibraryManager libraryManager, IServerConfigurationManager config, INetworkManager networkManager, IMediaEncoder mediaEncoder, IUserManager userManager) + public MediaInfoService(IMediaSourceManager mediaSourceManager, IDeviceManager deviceManager, ILibraryManager libraryManager, IServerConfigurationManager config, INetworkManager networkManager, IMediaEncoder mediaEncoder, IUserManager userManager, IJsonSerializer json) { _mediaSourceManager = mediaSourceManager; _deviceManager = deviceManager; @@ -80,6 +82,7 @@ namespace MediaBrowser.Api.Playback _networkManager = networkManager; _mediaEncoder = mediaEncoder; _userManager = userManager; + _json = json; } public object Get(GetBitrateTestBytes request) @@ -147,6 +150,8 @@ namespace MediaBrowser.Api.Playback var profile = request.DeviceProfile; + //Logger.Info("GetPostedPlaybackInfo profile: {0}", _json.SerializeToString(profile)); + if (profile == null) { var caps = _deviceManager.GetCapabilities(authInfo.DeviceId); diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs index d7d94c69b..2e92c4a49 100644 --- a/MediaBrowser.Api/Playback/StreamState.cs +++ b/MediaBrowser.Api/Playback/StreamState.cs @@ -112,6 +112,7 @@ namespace MediaBrowser.Api.Playback public string OutputVideoSync = "-1"; public List<string> SupportedAudioCodecs { get; set; } + public List<string> SupportedVideoCodecs { get; set; } public string UserAgent { get; set; } public StreamState(IMediaSourceManager mediaSourceManager, ILogger logger) @@ -119,6 +120,7 @@ namespace MediaBrowser.Api.Playback _mediaSourceManager = mediaSourceManager; _logger = logger; SupportedAudioCodecs = new List<string>(); + SupportedVideoCodecs = new List<string>(); PlayableStreamFileNames = new List<string>(); RemoteHttpHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); } diff --git a/MediaBrowser.Api/TvShowsService.cs b/MediaBrowser.Api/TvShowsService.cs index daaa6343d..a0d69317c 100644 --- a/MediaBrowser.Api/TvShowsService.cs +++ b/MediaBrowser.Api/TvShowsService.cs @@ -478,7 +478,7 @@ namespace MediaBrowser.Api } else { - episodes = series.GetSeasonEpisodes(user, season); + episodes = series.GetSeasonEpisodes(season, user); } } else diff --git a/MediaBrowser.Common.Implementations/Security/PluginSecurityManager.cs b/MediaBrowser.Common.Implementations/Security/PluginSecurityManager.cs index 4e01041bc..10c0f8fc9 100644 --- a/MediaBrowser.Common.Implementations/Security/PluginSecurityManager.cs +++ b/MediaBrowser.Common.Implementations/Security/PluginSecurityManager.cs @@ -142,9 +142,15 @@ namespace MediaBrowser.Common.Implementations.Security } set { - if (value != LicenseFile.RegKey) + var newValue = value; + if (newValue != null) { - LicenseFile.RegKey = value; + newValue = newValue.Trim(); + } + + if (newValue != LicenseFile.RegKey) + { + LicenseFile.RegKey = newValue; LicenseFile.Save(); // re-load registration info diff --git a/MediaBrowser.Common.Implementations/Updates/GithubUpdater.cs b/MediaBrowser.Common.Implementations/Updates/GithubUpdater.cs index 6281ab3ed..84c08439e 100644 --- a/MediaBrowser.Common.Implementations/Updates/GithubUpdater.cs +++ b/MediaBrowser.Common.Implementations/Updates/GithubUpdater.cs @@ -14,16 +14,14 @@ namespace MediaBrowser.Common.Implementations.Updates { private readonly IHttpClient _httpClient; private readonly IJsonSerializer _jsonSerializer; - private TimeSpan _cacheLength; - public GithubUpdater(IHttpClient httpClient, IJsonSerializer jsonSerializer, TimeSpan cacheLength) + public GithubUpdater(IHttpClient httpClient, IJsonSerializer jsonSerializer) { _httpClient = httpClient; _jsonSerializer = jsonSerializer; - _cacheLength = cacheLength; } - public async Task<CheckForUpdateResult> CheckForUpdateResult(string organzation, string repository, Version minVersion, PackageVersionClass updateLevel, string assetFilename, string packageName, string targetFilename, CancellationToken cancellationToken) + public async Task<CheckForUpdateResult> CheckForUpdateResult(string organzation, string repository, Version minVersion, PackageVersionClass updateLevel, string assetFilename, string packageName, string targetFilename, TimeSpan cacheLength, CancellationToken cancellationToken) { var url = string.Format("https://api.github.com/repos/{0}/{1}/releases", organzation, repository); @@ -35,10 +33,10 @@ namespace MediaBrowser.Common.Implementations.Updates UserAgent = "Emby/3.0" }; - if (_cacheLength.Ticks > 0) + if (cacheLength.Ticks > 0) { options.CacheMode = CacheMode.Unconditional; - options.CacheLength = _cacheLength; + options.CacheLength = cacheLength; } using (var stream = await _httpClient.Get(options).ConfigureAwait(false)) @@ -110,12 +108,6 @@ namespace MediaBrowser.Common.Implementations.Updates UserAgent = "Emby/3.0" }; - if (_cacheLength.Ticks > 0) - { - options.CacheMode = CacheMode.Unconditional; - options.CacheLength = _cacheLength; - } - using (var stream = await _httpClient.Get(options).ConfigureAwait(false)) { var obj = _jsonSerializer.DeserializeFromStream<RootObject[]>(stream); diff --git a/MediaBrowser.Controller/Channels/IChannelManager.cs b/MediaBrowser.Controller/Channels/IChannelManager.cs index e3d2d0440..3c46247a7 100644 --- a/MediaBrowser.Controller/Channels/IChannelManager.cs +++ b/MediaBrowser.Controller/Channels/IChannelManager.cs @@ -31,6 +31,8 @@ namespace MediaBrowser.Controller.Channels /// <returns>ChannelFeatures.</returns> ChannelFeatures GetChannelFeatures(string id); + bool SupportsSync(string channelId); + /// <summary> /// Gets all channel features. /// </summary> diff --git a/MediaBrowser.Server.Implementations/Collections/ManualCollectionsFolder.cs b/MediaBrowser.Controller/Collections/ManualCollectionsFolder.cs index 3e33066ae..d2d28e504 100644 --- a/MediaBrowser.Server.Implementations/Collections/ManualCollectionsFolder.cs +++ b/MediaBrowser.Controller/Collections/ManualCollectionsFolder.cs @@ -1,6 +1,6 @@ using MediaBrowser.Controller.Entities; -namespace MediaBrowser.Server.Implementations.Collections +namespace MediaBrowser.Controller.Collections { public class ManualCollectionsFolder : BasePluginFolder, IHiddenFromDisplay { diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs index 597ecf973..30ea26eb6 100644 --- a/MediaBrowser.Controller/Entities/CollectionFolder.cs +++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs @@ -106,7 +106,7 @@ namespace MediaBrowser.Controller.Entities { LibraryOptions[path] = options; - options.SchemaVersion = 1; + options.SchemaVersion = 2; XmlSerializer.SerializeToFile(options, GetLibraryOptionsPath(path)); } } diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index bf47ada0d..f1d8def4b 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -1057,11 +1057,21 @@ namespace MediaBrowser.Controller.Entities /// <returns>IList{BaseItem}.</returns> public IList<BaseItem> GetRecursiveChildren() { - return GetRecursiveChildren(i => true); + return GetRecursiveChildren(true); + } + + public IList<BaseItem> GetRecursiveChildren(bool includeLinkedChildren) + { + return GetRecursiveChildren(i => true, includeLinkedChildren); } public IList<BaseItem> GetRecursiveChildren(Func<BaseItem, bool> filter) { + return GetRecursiveChildren(filter, true); + } + + public IList<BaseItem> GetRecursiveChildren(Func<BaseItem, bool> filter, bool includeLinkedChildren) + { var result = new Dictionary<Guid, BaseItem>(); AddChildrenToList(result, true, true, filter); diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs index 65b7c9955..ff3e53b69 100644 --- a/MediaBrowser.Controller/Entities/TV/Season.cs +++ b/MediaBrowser.Controller/Entities/TV/Season.cs @@ -85,9 +85,7 @@ namespace MediaBrowser.Controller.Entities.TV public override int GetChildCount(User user) { - Logger.Debug("Season {0} getting child cound", (Path ?? Name)); var result = GetChildren(user, true).Count(); - Logger.Debug("Season {0} child cound: ", result); return result; } @@ -158,13 +156,10 @@ namespace MediaBrowser.Controller.Entities.TV var id = Guid.NewGuid().ToString("N"); - Logger.Debug("Season.GetItemsInternal entering GetEpisodes. Request id: " + id); var items = GetEpisodes(user).Where(filter); - Logger.Debug("Season.GetItemsInternal entering PostFilterAndSort. Request id: " + id); var result = PostFilterAndSort(items, query, false, false); - Logger.Debug("Season.GetItemsInternal complete. Request id: " + id); return Task.FromResult(result); } @@ -185,34 +180,12 @@ namespace MediaBrowser.Controller.Entities.TV public IEnumerable<Episode> GetEpisodes(Series series, User user, IEnumerable<Episode> allSeriesEpisodes) { - return series.GetSeasonEpisodes(user, this, allSeriesEpisodes); + return series.GetSeasonEpisodes(this, user, allSeriesEpisodes); } public IEnumerable<Episode> GetEpisodes() { - var episodes = GetRecursiveChildren().OfType<Episode>(); - var series = Series; - - if (series != null && series.ContainsEpisodesWithoutSeasonFolders) - { - var seasonNumber = IndexNumber; - var list = episodes.ToList(); - - if (seasonNumber.HasValue) - { - list.AddRange(series.GetRecursiveChildren().OfType<Episode>() - .Where(i => i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == seasonNumber.Value)); - } - else - { - list.AddRange(series.GetRecursiveChildren().OfType<Episode>() - .Where(i => !i.ParentIndexNumber.HasValue)); - } - - episodes = list.DistinctBy(i => i.Id); - } - - return episodes; + return Series.GetSeasonEpisodes(this, null, null); } public override IEnumerable<BaseItem> GetChildren(User user, bool includeLinkedChildren) diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs index 4915cfedc..7e8ba0516 100644 --- a/MediaBrowser.Controller/Entities/TV/Series.cs +++ b/MediaBrowser.Controller/Entities/TV/Series.cs @@ -209,7 +209,6 @@ namespace MediaBrowser.Controller.Entities.TV var seriesKey = GetUniqueSeriesKey(this); - Logger.Debug("GetSeasons SeriesKey: {0}", seriesKey); var query = new InternalItemsQuery(user) { AncestorWithPresentationUniqueKey = seriesKey, @@ -267,7 +266,6 @@ namespace MediaBrowser.Controller.Entities.TV public IEnumerable<Episode> GetEpisodes(User user) { var seriesKey = GetUniqueSeriesKey(this); - Logger.Debug("GetEpisodes seriesKey: {0}", seriesKey); var query = new InternalItemsQuery(user) { @@ -291,8 +289,6 @@ namespace MediaBrowser.Controller.Entities.TV var allItems = LibraryManager.GetItemList(query).ToList(); - Logger.Debug("GetEpisodes return {0} items from database", allItems.Count); - var allSeriesEpisodes = allItems.OfType<Episode>().ToList(); var allEpisodes = allItems.OfType<Season>() @@ -373,27 +369,9 @@ namespace MediaBrowser.Controller.Entities.TV progress.Report(100); } - private IEnumerable<Episode> GetAllEpisodes(User user) - { - Logger.Debug("Series.GetAllEpisodes entering GetItemList"); - - var result = LibraryManager.GetItemList(new InternalItemsQuery(user) - { - AncestorWithPresentationUniqueKey = GetUniqueSeriesKey(this), - IncludeItemTypes = new[] { typeof(Episode).Name }, - SortBy = new[] { ItemSortBy.SortName } - - }).Cast<Episode>().ToList(); - - Logger.Debug("Series.GetAllEpisodes returning {0} episodes", result.Count); - - return result; - } - - public IEnumerable<Episode> GetSeasonEpisodes(User user, Season parentSeason) + public IEnumerable<Episode> GetSeasonEpisodes(Season parentSeason, User user) { var seriesKey = GetUniqueSeriesKey(this); - Logger.Debug("GetSeasonEpisodes seriesKey: {0}", seriesKey); var query = new InternalItemsQuery(user) { @@ -401,34 +379,35 @@ namespace MediaBrowser.Controller.Entities.TV IncludeItemTypes = new[] { typeof(Episode).Name }, SortBy = new[] { ItemSortBy.SortName } }; - var config = user.Configuration; - if (!config.DisplayMissingEpisodes && !config.DisplayUnairedEpisodes) + if (user != null) { - query.IsVirtualItem = false; - } - else if (!config.DisplayMissingEpisodes) - { - query.IsMissing = false; - } - else if (!config.DisplayUnairedEpisodes) - { - query.IsVirtualUnaired = false; + var config = user.Configuration; + if (!config.DisplayMissingEpisodes && !config.DisplayUnairedEpisodes) + { + query.IsVirtualItem = false; + } + else if (!config.DisplayMissingEpisodes) + { + query.IsMissing = false; + } + else if (!config.DisplayUnairedEpisodes) + { + query.IsVirtualUnaired = false; + } } var allItems = LibraryManager.GetItemList(query).OfType<Episode>(); - return GetSeasonEpisodes(user, parentSeason, allItems); + return GetSeasonEpisodes(parentSeason, user, allItems); } - public IEnumerable<Episode> GetSeasonEpisodes(User user, Season parentSeason, IEnumerable<Episode> allSeriesEpisodes) + public IEnumerable<Episode> GetSeasonEpisodes(Season parentSeason, User user, IEnumerable<Episode> allSeriesEpisodes) { if (allSeriesEpisodes == null) { - Logger.Debug("GetSeasonEpisodes allSeriesEpisodes is null"); - return GetSeasonEpisodes(user, parentSeason); + return GetSeasonEpisodes(parentSeason, user); } - Logger.Debug("GetSeasonEpisodes FilterEpisodesBySeason"); var episodes = FilterEpisodesBySeason(allSeriesEpisodes, parentSeason, ConfigurationManager.Configuration.DisplaySpecialsWithinSeasons); var sortBy = (parentSeason.IndexNumber ?? -1) == 0 ? ItemSortBy.SortName : ItemSortBy.AiredEpisodeOrder; diff --git a/MediaBrowser.Controller/Entities/Trailer.cs b/MediaBrowser.Controller/Entities/Trailer.cs index 306ce35ec..7a987a68e 100644 --- a/MediaBrowser.Controller/Entities/Trailer.cs +++ b/MediaBrowser.Controller/Entities/Trailer.cs @@ -64,7 +64,7 @@ namespace MediaBrowser.Controller.Entities info.IsLocalTrailer = TrailerTypes.Contains(TrailerType.LocalTrailer); - if (!IsInMixedFolder) + if (!IsInMixedFolder && LocationType == LocationType.FileSystem) { info.Name = System.IO.Path.GetFileName(ContainingFolderPath); } diff --git a/MediaBrowser.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs index f4c0e7658..d6744e804 100644 --- a/MediaBrowser.Controller/IServerApplicationHost.cs +++ b/MediaBrowser.Controller/IServerApplicationHost.cs @@ -89,5 +89,7 @@ namespace MediaBrowser.Controller string GetLocalApiUrl(IPAddress ipAddress); void LaunchUrl(string url); + + void EnableLoopback(string appName); } } diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs index 0862e3eaf..04268bcea 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/MediaBrowser.Controller/Library/ILibraryManager.cs @@ -566,5 +566,8 @@ namespace MediaBrowser.Controller.Library QueryResult<Tuple<BaseItem, ItemCounts>> GetArtists(InternalItemsQuery query); QueryResult<Tuple<BaseItem, ItemCounts>> GetAlbumArtists(InternalItemsQuery query); QueryResult<Tuple<BaseItem, ItemCounts>> GetAllArtists(InternalItemsQuery query); + + void RegisterIgnoredPath(string path); + void UnRegisterIgnoredPath(string path); } }
\ No newline at end of file diff --git a/MediaBrowser.Controller/Library/NameExtensions.cs b/MediaBrowser.Controller/Library/NameExtensions.cs index 6973dce64..72f036b0a 100644 --- a/MediaBrowser.Controller/Library/NameExtensions.cs +++ b/MediaBrowser.Controller/Library/NameExtensions.cs @@ -54,7 +54,7 @@ namespace MediaBrowser.Controller.Library } } - class TextComparer : IComparer<string>, IEqualityComparer<string> + public class DistinctNameComparer : IComparer<string>, IEqualityComparer<string> { public int Compare(string x, string y) { @@ -63,7 +63,7 @@ namespace MediaBrowser.Controller.Library return 0; } - return string.Compare(x, y, CultureInfo.InvariantCulture, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace); + return string.Compare(x.RemoveDiacritics(), y.RemoveDiacritics(), StringComparison.OrdinalIgnoreCase); } public bool Equals(string x, string y) diff --git a/MediaBrowser.Controller/LiveTv/IHasRegistrationInfo.cs b/MediaBrowser.Controller/LiveTv/IHasRegistrationInfo.cs deleted file mode 100644 index 3626c18e5..000000000 --- a/MediaBrowser.Controller/LiveTv/IHasRegistrationInfo.cs +++ /dev/null @@ -1,15 +0,0 @@ -using MediaBrowser.Model.Entities; -using System.Threading.Tasks; - -namespace MediaBrowser.Controller.LiveTv -{ - public interface IHasRegistrationInfo - { - /// <summary> - /// Gets the registration information. - /// </summary> - /// <param name="feature">The feature.</param> - /// <returns>Task<MBRegistrationRecord>.</returns> - Task<MBRegistrationRecord> GetRegistrationInfo(string feature); - } -} diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs index ed64127c3..a8e42749b 100644 --- a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs +++ b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs @@ -108,6 +108,7 @@ namespace MediaBrowser.Controller.LiveTv /// <param name="cancellationToken">The cancellation token.</param> /// <returns>QueryResult{RecordingInfoDto}.</returns> Task<QueryResult<BaseItemDto>> GetRecordings(RecordingQuery query, DtoOptions options, CancellationToken cancellationToken); + Task<QueryResult<BaseItemDto>> GetRecordingSeries(RecordingQuery query, DtoOptions options, CancellationToken cancellationToken); /// <summary> /// Gets the timers. @@ -364,11 +365,9 @@ namespace MediaBrowser.Controller.LiveTv /// <summary> /// Gets the registration information. /// </summary> - /// <param name="channelId">The channel identifier.</param> - /// <param name="programId">The program identifier.</param> /// <param name="feature">The feature.</param> /// <returns>Task<MBRegistrationRecord>.</returns> - Task<MBRegistrationRecord> GetRegistrationInfo(string channelId, string programId, string feature); + Task<MBRegistrationRecord> GetRegistrationInfo(string feature); /// <summary> /// Adds the channel information. diff --git a/MediaBrowser.Controller/LiveTv/ProgramInfo.cs b/MediaBrowser.Controller/LiveTv/ProgramInfo.cs index a6a3e6108..ea5e6dbc6 100644 --- a/MediaBrowser.Controller/LiveTv/ProgramInfo.cs +++ b/MediaBrowser.Controller/LiveTv/ProgramInfo.cs @@ -1,6 +1,7 @@ using MediaBrowser.Model.LiveTv; using System; using System.Collections.Generic; +using MediaBrowser.Model.Entities; namespace MediaBrowser.Controller.LiveTv { @@ -66,6 +67,8 @@ namespace MediaBrowser.Controller.LiveTv /// <value><c>true</c> if this instance is hd; otherwise, <c>false</c>.</value> public bool? IsHD { get; set; } + public bool? Is3D { get; set; } + /// <summary> /// Gets or sets the audio. /// </summary> @@ -84,6 +87,8 @@ namespace MediaBrowser.Controller.LiveTv /// <value><c>true</c> if this instance is repeat; otherwise, <c>false</c>.</value> public bool IsRepeat { get; set; } + public bool IsSubjectToBlackout { get; set; } + /// <summary> /// Gets or sets the episode title. /// </summary> @@ -144,6 +149,8 @@ namespace MediaBrowser.Controller.LiveTv /// <value><c>true</c> if this instance is kids; otherwise, <c>false</c>.</value> public bool IsKids { get; set; } + public bool IsEducational { get; set; } + /// <summary> /// Gets or sets a value indicating whether this instance is premiere. /// </summary> diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index e7eaa1dc0..7cfd56c1e 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -98,6 +98,7 @@ <Compile Include="Collections\CollectionCreationOptions.cs" /> <Compile Include="Collections\CollectionEvents.cs" /> <Compile Include="Collections\ICollectionManager.cs" /> + <Compile Include="Collections\ManualCollectionsFolder.cs" /> <Compile Include="Connect\ConnectSupporterSummary.cs" /> <Compile Include="Connect\IConnectManager.cs" /> <Compile Include="Connect\UserLinkResult.cs" /> @@ -198,7 +199,6 @@ <Compile Include="Library\NameExtensions.cs" /> <Compile Include="Library\PlaybackStopEventArgs.cs" /> <Compile Include="Library\UserDataSaveEventArgs.cs" /> - <Compile Include="LiveTv\IHasRegistrationInfo.cs" /> <Compile Include="LiveTv\IListingsProvider.cs" /> <Compile Include="LiveTv\ITunerHost.cs" /> <Compile Include="LiveTv\RecordingGroup.cs" /> diff --git a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs index c0912708c..fccbd9211 100644 --- a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs +++ b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs @@ -23,14 +23,18 @@ namespace MediaBrowser.Controller.Providers /// The logger /// </summary> protected ILogger Logger { get; private set; } + protected IProviderManager ProviderManager { get; private set; } + + private Dictionary<string, string> _validProviderIds; /// <summary> /// Initializes a new instance of the <see cref="BaseItemXmlParser{T}" /> class. /// </summary> /// <param name="logger">The logger.</param> - public BaseItemXmlParser(ILogger logger) + public BaseItemXmlParser(ILogger logger, IProviderManager providerManager) { Logger = logger; + ProviderManager = providerManager; } /// <summary> @@ -60,6 +64,22 @@ namespace MediaBrowser.Controller.Providers ValidationType = ValidationType.None }; + _validProviderIds = _validProviderIds = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase); + + var idInfos = ProviderManager.GetExternalIdInfos(item.Item); + + foreach (var info in idInfos) + { + var id = info.Key + "Id"; + if (!_validProviderIds.ContainsKey(id)) + { + _validProviderIds.Add(id, info.Key); + } + } + + //Additional Mappings + _validProviderIds.Add("IMDB", "Imdb"); + //Fetch(item, metadataFile, settings, Encoding.GetEncoding("ISO-8859-1"), cancellationToken); Fetch(item, metadataFile, settings, Encoding.UTF8, cancellationToken); } @@ -657,14 +677,6 @@ namespace MediaBrowser.Controller.Providers break; } - case "TvDbId": - var tvdbId = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(tvdbId)) - { - item.SetProviderId(MetadataProviders.Tvdb, tvdbId); - } - break; - case "VoteCount": { var val = reader.ReadElementContentAsString(); @@ -679,95 +691,7 @@ namespace MediaBrowser.Controller.Providers } break; } - case "MusicBrainzAlbumId": - { - var mbz = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(mbz)) - { - item.SetProviderId(MetadataProviders.MusicBrainzAlbum, mbz); - } - break; - } - case "MusicBrainzAlbumArtistId": - { - var mbz = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(mbz)) - { - item.SetProviderId(MetadataProviders.MusicBrainzAlbumArtist, mbz); - } - break; - } - case "MusicBrainzArtistId": - { - var mbz = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(mbz)) - { - item.SetProviderId(MetadataProviders.MusicBrainzArtist, mbz); - } - break; - } - case "MusicBrainzReleaseGroupId": - { - var mbz = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(mbz)) - { - item.SetProviderId(MetadataProviders.MusicBrainzReleaseGroup, mbz); - } - break; - } - case "TVRageId": - { - var id = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(id)) - { - item.SetProviderId(MetadataProviders.TvRage, id); - } - break; - } - case "TvMazeId": - { - var id = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(id)) - { - item.SetProviderId(MetadataProviders.TvMaze, id); - } - break; - } - case "AudioDbArtistId": - { - var id = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(id)) - { - item.SetProviderId(MetadataProviders.AudioDbArtist, id); - } - break; - } - case "AudioDbAlbumId": - { - var id = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(id)) - { - item.SetProviderId(MetadataProviders.AudioDbAlbum, id); - } - break; - } - case "RottenTomatoesId": - var rtId = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(rtId)) - { - item.SetProviderId(MetadataProviders.RottenTomatoes, rtId); - } - break; - - case "TMDbId": - var tmdb = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(tmdb)) - { - item.SetProviderId(MetadataProviders.Tmdb, tmdb); - } - break; - - case "TMDbCollectionId": + case "CollectionNumber": var tmdbCollection = reader.ReadElementContentAsString(); if (!string.IsNullOrWhiteSpace(tmdbCollection)) { @@ -775,30 +699,6 @@ namespace MediaBrowser.Controller.Providers } break; - case "TVcomId": - var TVcomId = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(TVcomId)) - { - item.SetProviderId(MetadataProviders.Tvcom, TVcomId); - } - break; - - case "Zap2ItId": - var zap2ItId = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(zap2ItId)) - { - item.SetProviderId(MetadataProviders.Zap2It, zap2ItId); - } - break; - - case "IMDB": - var imDbId = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(imDbId)) - { - item.SetProviderId(MetadataProviders.Imdb, imDbId); - } - break; - case "Genres": { using (var subtree = reader.ReadSubtree()) @@ -890,8 +790,25 @@ namespace MediaBrowser.Controller.Providers } default: - reader.Skip(); - break; + { + string readerName = reader.Name; + string providerIdValue; + if (_validProviderIds.TryGetValue(readerName, out providerIdValue)) + { + var id = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(id)) + { + item.SetProviderId(providerIdValue, id); + } + } + else + { + reader.Skip(); + } + + break; + + } } } diff --git a/MediaBrowser.Dlna/PlayTo/Device.cs b/MediaBrowser.Dlna/PlayTo/Device.cs index 174ca871a..d1802b3ad 100644 --- a/MediaBrowser.Dlna/PlayTo/Device.cs +++ b/MediaBrowser.Dlna/PlayTo/Device.cs @@ -483,7 +483,9 @@ namespace MediaBrowser.Dlna.PlayTo { if (OnDeviceUnavailable != null) { + _logger.Debug("Disposing device due to loss of connection"); OnDeviceUnavailable(); + return; } } if (_successiveStopCount >= maxSuccessiveStopReturns) diff --git a/MediaBrowser.Dlna/Profiles/PanasonicVieraProfile.cs b/MediaBrowser.Dlna/Profiles/PanasonicVieraProfile.cs index 7b0f18fa6..5edf3afbf 100644 --- a/MediaBrowser.Dlna/Profiles/PanasonicVieraProfile.cs +++ b/MediaBrowser.Dlna/Profiles/PanasonicVieraProfile.cs @@ -66,7 +66,7 @@ namespace MediaBrowser.Dlna.Profiles { Container = "mkv", VideoCodec = "h264,mpeg2video", - AudioCodec = "aac,ac3,dca,mp3,mp2,pcm", + AudioCodec = "aac,ac3,dca,mp3,mp2,pcm,dts", Type = DlnaProfileType.Video }, diff --git a/MediaBrowser.Dlna/Profiles/SamsungSmartTvProfile.cs b/MediaBrowser.Dlna/Profiles/SamsungSmartTvProfile.cs index 63fcf60ec..aae520d6f 100644 --- a/MediaBrowser.Dlna/Profiles/SamsungSmartTvProfile.cs +++ b/MediaBrowser.Dlna/Profiles/SamsungSmartTvProfile.cs @@ -65,14 +65,14 @@ namespace MediaBrowser.Dlna.Profiles { Container = "avi", VideoCodec = "h264,mpeg4,mjpeg", - AudioCodec = "mp3,ac3,dca", + AudioCodec = "mp3,ac3,dca,dts", Type = DlnaProfileType.Video }, new DirectPlayProfile { Container = "mkv", VideoCodec = "h264,mpeg4,mjpeg4", - AudioCodec = "mp3,ac3,dca,aac", + AudioCodec = "mp3,ac3,dca,aac,dts", Type = DlnaProfileType.Video }, new DirectPlayProfile @@ -300,7 +300,7 @@ namespace MediaBrowser.Dlna.Profiles new CodecProfile { Type = CodecType.VideoAudio, - Codec = "ac3,wmav2,dca,aac,mp3", + Codec = "ac3,wmav2,dca,aac,mp3,dts", Conditions = new[] { diff --git a/MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2013.cs b/MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2013.cs index bbdf370b8..fefb96117 100644 --- a/MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2013.cs +++ b/MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2013.cs @@ -115,7 +115,7 @@ namespace MediaBrowser.Dlna.Profiles { Container = "mkv", VideoCodec = "mpeg4,h264", - AudioCodec = "ac3,dca,aac,mp3,pcm", + AudioCodec = "ac3,dca,aac,mp3,pcm,dts", Type = DlnaProfileType.Video }, new DirectPlayProfile diff --git a/MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2014.cs b/MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2014.cs index 1eed398ed..4f2ff3ad1 100644 --- a/MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2014.cs +++ b/MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2014.cs @@ -115,7 +115,7 @@ namespace MediaBrowser.Dlna.Profiles { Container = "mkv", VideoCodec = "mpeg4,h264", - AudioCodec = "ac3,dca,aac,mp3,pcm", + AudioCodec = "ac3,dca,aac,mp3,pcm,dts", Type = DlnaProfileType.Video }, new DirectPlayProfile diff --git a/MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2015.cs b/MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2015.cs index 563c2db7b..57cd5dad6 100644 --- a/MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2015.cs +++ b/MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2015.cs @@ -103,7 +103,7 @@ namespace MediaBrowser.Dlna.Profiles { Container = "mkv", VideoCodec = "mpeg4,h264", - AudioCodec = "ac3,dca,aac,mp3,pcm", + AudioCodec = "ac3,dca,aac,mp3,pcm,dts", Type = DlnaProfileType.Video }, new DirectPlayProfile diff --git a/MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2016.cs b/MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2016.cs index 21e0c092c..f504820d1 100644 --- a/MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2016.cs +++ b/MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2016.cs @@ -103,7 +103,7 @@ namespace MediaBrowser.Dlna.Profiles { Container = "mkv", VideoCodec = "mpeg4,h264", - AudioCodec = "ac3,dca,aac,mp3,pcm", + AudioCodec = "ac3,dca,aac,mp3,pcm,dts", Type = DlnaProfileType.Video }, new DirectPlayProfile diff --git a/MediaBrowser.Dlna/Profiles/WdtvLiveProfile.cs b/MediaBrowser.Dlna/Profiles/WdtvLiveProfile.cs index 1c0c7d297..5f9e30318 100644 --- a/MediaBrowser.Dlna/Profiles/WdtvLiveProfile.cs +++ b/MediaBrowser.Dlna/Profiles/WdtvLiveProfile.cs @@ -58,7 +58,7 @@ namespace MediaBrowser.Dlna.Profiles Container = "avi", Type = DlnaProfileType.Video, VideoCodec = "mpeg1video,mpeg2video,mpeg4,h264,vc1", - AudioCodec = "ac3,dca,mp2,mp3,pcm,dca" + AudioCodec = "ac3,dca,mp2,mp3,pcm,dts" }, new DirectPlayProfile @@ -66,7 +66,7 @@ namespace MediaBrowser.Dlna.Profiles Container = "mpeg", Type = DlnaProfileType.Video, VideoCodec = "mpeg1video,mpeg2video", - AudioCodec = "ac3,dca,mp2,mp3,pcm,dca" + AudioCodec = "ac3,dca,mp2,mp3,pcm,dts" }, new DirectPlayProfile @@ -74,7 +74,7 @@ namespace MediaBrowser.Dlna.Profiles Container = "mkv", Type = DlnaProfileType.Video, VideoCodec = "mpeg1video,mpeg2video,mpeg4,h264,vc1", - AudioCodec = "ac3,dca,aac,mp2,mp3,pcm,dca" + AudioCodec = "ac3,dca,aac,mp2,mp3,pcm,dts" }, new DirectPlayProfile @@ -82,7 +82,7 @@ namespace MediaBrowser.Dlna.Profiles Container = "ts,m2ts", Type = DlnaProfileType.Video, VideoCodec = "mpeg1video,mpeg2video,h264,vc1", - AudioCodec = "ac3,dca,mp2,mp3,aac,dca" + AudioCodec = "ac3,dca,mp2,mp3,aac,dts" }, new DirectPlayProfile @@ -90,7 +90,7 @@ namespace MediaBrowser.Dlna.Profiles Container = "mp4,mov", Type = DlnaProfileType.Video, VideoCodec = "h264,mpeg4", - AudioCodec = "ac3,aac,mp2,mp3,dca" + AudioCodec = "ac3,aac,mp2,mp3,dca,dts" }, new DirectPlayProfile diff --git a/MediaBrowser.Dlna/Profiles/Xml/Panasonic Viera.xml b/MediaBrowser.Dlna/Profiles/Xml/Panasonic Viera.xml index 865449cb6..d26346ff6 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Panasonic Viera.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Panasonic Viera.xml @@ -39,7 +39,7 @@ </XmlRootAttributes> <DirectPlayProfiles> <DirectPlayProfile container="mpeg,mpg" audioCodec="ac3,mp3,pcm_dvd" videoCodec="mpeg2video,mpeg4" type="Video" /> - <DirectPlayProfile container="mkv" audioCodec="aac,ac3,dca,mp3,mp2,pcm" videoCodec="h264,mpeg2video" type="Video" /> + <DirectPlayProfile container="mkv" audioCodec="aac,ac3,dca,mp3,mp2,pcm,dts" videoCodec="h264,mpeg2video" type="Video" /> <DirectPlayProfile container="ts" audioCodec="aac,mp3,mp2" videoCodec="h264,mpeg2video" type="Video" /> <DirectPlayProfile container="mp4" audioCodec="aac,ac3,mp3,pcm" videoCodec="h264" type="Video" /> <DirectPlayProfile container="mov" audioCodec="aac,pcm" videoCodec="h264" type="Video" /> diff --git a/MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml b/MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml index 77fa928cc..1918c0297 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml @@ -38,8 +38,8 @@ </XmlRootAttributes> <DirectPlayProfiles> <DirectPlayProfile container="asf" audioCodec="mp3,ac3,wmav2,wmapro,wmavoice" videoCodec="h264,mpeg4,mjpeg" type="Video" /> - <DirectPlayProfile container="avi" audioCodec="mp3,ac3,dca" videoCodec="h264,mpeg4,mjpeg" type="Video" /> - <DirectPlayProfile container="mkv" audioCodec="mp3,ac3,dca,aac" videoCodec="h264,mpeg4,mjpeg4" type="Video" /> + <DirectPlayProfile container="avi" audioCodec="mp3,ac3,dca,dts" videoCodec="h264,mpeg4,mjpeg" type="Video" /> + <DirectPlayProfile container="mkv" audioCodec="mp3,ac3,dca,aac,dts" videoCodec="h264,mpeg4,mjpeg4" type="Video" /> <DirectPlayProfile container="mp4" audioCodec="mp3,aac" videoCodec="h264,mpeg4" type="Video" /> <DirectPlayProfile container="3gp" audioCodec="aac,he-aac" videoCodec="h264,mpeg4" type="Video" /> <DirectPlayProfile container="mpg,mpeg" audioCodec="ac3,mp2,mp3,aac" videoCodec="mpeg1video,mpeg2video,h264" type="Video" /> @@ -100,7 +100,7 @@ </Conditions> <ApplyConditions /> </CodecProfile> - <CodecProfile type="VideoAudio" codec="ac3,wmav2,dca,aac,mp3"> + <CodecProfile type="VideoAudio" codec="ac3,wmav2,dca,aac,mp3,dts"> <Conditions> <ProfileCondition condition="LessThanEqual" property="AudioChannels" value="6" isRequired="true" /> </Conditions> 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 1c626d3d5..f8b583b50 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml @@ -45,7 +45,7 @@ <DirectPlayProfile container="mpeg,mpg" audioCodec="ac3,mp3,mp2,pcm" videoCodec="mpeg1video,mpeg2video" type="Video" /> <DirectPlayProfile container="mp4,m4v" audioCodec="ac3,aac,pcm,mp3" videoCodec="mpeg4,h264" type="Video" /> <DirectPlayProfile container="avi" audioCodec="ac3,aac,mp3,pcm" videoCodec="mpeg4,h264" type="Video" /> - <DirectPlayProfile container="mkv" audioCodec="ac3,dca,aac,mp3,pcm" videoCodec="mpeg4,h264" type="Video" /> + <DirectPlayProfile container="mkv" audioCodec="ac3,dca,aac,mp3,pcm,dts" videoCodec="mpeg4,h264" type="Video" /> <DirectPlayProfile container="m2ts,mts" audioCodec="aac,mp3,ac3,dca,dts" videoCodec="h264,mpeg4,vc1" type="Video" /> <DirectPlayProfile container="wmv,asf" type="Video" /> <DirectPlayProfile container="mp3,m4a,wma,wav" type="Audio" /> diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2014.xml b/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2014.xml index bb55a1a80..eaa37c620 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2014.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2014.xml @@ -45,7 +45,7 @@ <DirectPlayProfile container="mpeg,mpg" audioCodec="ac3,mp3,mp2,pcm" videoCodec="mpeg1video,mpeg2video" type="Video" /> <DirectPlayProfile container="mp4,m4v" audioCodec="ac3,aac,pcm,mp3" videoCodec="mpeg4,h264" type="Video" /> <DirectPlayProfile container="avi" audioCodec="ac3,aac,mp3,pcm" videoCodec="mpeg4,h264" type="Video" /> - <DirectPlayProfile container="mkv" audioCodec="ac3,dca,aac,mp3,pcm" videoCodec="mpeg4,h264" type="Video" /> + <DirectPlayProfile container="mkv" audioCodec="ac3,dca,aac,mp3,pcm,dts" videoCodec="mpeg4,h264" type="Video" /> <DirectPlayProfile container="m2ts,mts" audioCodec="aac,mp3,ac3,dca,dts" videoCodec="h264,mpeg4,vc1" type="Video" /> <DirectPlayProfile container="wmv,asf" type="Video" /> <DirectPlayProfile container="mp3,m4a,wma,wav" type="Audio" /> diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2015.xml b/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2015.xml index 804770a59..368e892ff 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2015.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2015.xml @@ -43,7 +43,7 @@ <DirectPlayProfile container="mpeg,mpg" audioCodec="ac3,mp3,mp2,pcm" videoCodec="mpeg1video,mpeg2video" type="Video" /> <DirectPlayProfile container="mp4,m4v" audioCodec="ac3,aac,pcm,mp3" videoCodec="mpeg4,h264" type="Video" /> <DirectPlayProfile container="avi" audioCodec="ac3,aac,mp3,pcm" videoCodec="mpeg4,h264" type="Video" /> - <DirectPlayProfile container="mkv" audioCodec="ac3,dca,aac,mp3,pcm" videoCodec="mpeg4,h264" type="Video" /> + <DirectPlayProfile container="mkv" audioCodec="ac3,dca,aac,mp3,pcm,dts" videoCodec="mpeg4,h264" type="Video" /> <DirectPlayProfile container="m2ts,mts" audioCodec="aac,mp3,ac3,dca,dts" videoCodec="h264,mpeg4,vc1" type="Video" /> <DirectPlayProfile container="wmv,asf" type="Video" /> <DirectPlayProfile container="mp3,m4a,wma,wav" type="Audio" /> diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2016.xml b/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2016.xml index 920b6ccfa..9ec096b7f 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2016.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2016.xml @@ -43,7 +43,7 @@ <DirectPlayProfile container="mpeg,mpg" audioCodec="ac3,mp3,mp2,pcm" videoCodec="mpeg1video,mpeg2video" type="Video" /> <DirectPlayProfile container="mp4,m4v" audioCodec="ac3,aac,pcm,mp3" videoCodec="mpeg4,h264" type="Video" /> <DirectPlayProfile container="avi" audioCodec="ac3,aac,mp3,pcm" videoCodec="mpeg4,h264" type="Video" /> - <DirectPlayProfile container="mkv" audioCodec="ac3,dca,aac,mp3,pcm" videoCodec="mpeg4,h264" type="Video" /> + <DirectPlayProfile container="mkv" audioCodec="ac3,dca,aac,mp3,pcm,dts" videoCodec="mpeg4,h264" type="Video" /> <DirectPlayProfile container="m2ts,mts" audioCodec="aac,mp3,ac3,dca,dts" videoCodec="h264,mpeg4,vc1" type="Video" /> <DirectPlayProfile container="wmv,asf" type="Video" /> <DirectPlayProfile container="mp3,m4a,wma,wav" type="Audio" /> diff --git a/MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml b/MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml index ef8fa5757..ebd4eb9b5 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml @@ -36,11 +36,11 @@ <IgnoreTranscodeByteRangeRequests>true</IgnoreTranscodeByteRangeRequests> <XmlRootAttributes /> <DirectPlayProfiles> - <DirectPlayProfile container="avi" audioCodec="ac3,dca,mp2,mp3,pcm,dca" videoCodec="mpeg1video,mpeg2video,mpeg4,h264,vc1" type="Video" /> - <DirectPlayProfile container="mpeg" audioCodec="ac3,dca,mp2,mp3,pcm,dca" videoCodec="mpeg1video,mpeg2video" type="Video" /> - <DirectPlayProfile container="mkv" audioCodec="ac3,dca,aac,mp2,mp3,pcm,dca" videoCodec="mpeg1video,mpeg2video,mpeg4,h264,vc1" type="Video" /> - <DirectPlayProfile container="ts,m2ts" audioCodec="ac3,dca,mp2,mp3,aac,dca" videoCodec="mpeg1video,mpeg2video,h264,vc1" type="Video" /> - <DirectPlayProfile container="mp4,mov" audioCodec="ac3,aac,mp2,mp3,dca" videoCodec="h264,mpeg4" type="Video" /> + <DirectPlayProfile container="avi" audioCodec="ac3,dca,mp2,mp3,pcm,dts" videoCodec="mpeg1video,mpeg2video,mpeg4,h264,vc1" type="Video" /> + <DirectPlayProfile container="mpeg" audioCodec="ac3,dca,mp2,mp3,pcm,dts" videoCodec="mpeg1video,mpeg2video" type="Video" /> + <DirectPlayProfile container="mkv" audioCodec="ac3,dca,aac,mp2,mp3,pcm,dts" videoCodec="mpeg1video,mpeg2video,mpeg4,h264,vc1" type="Video" /> + <DirectPlayProfile container="ts,m2ts" audioCodec="ac3,dca,mp2,mp3,aac,dts" videoCodec="mpeg1video,mpeg2video,h264,vc1" type="Video" /> + <DirectPlayProfile container="mp4,mov" audioCodec="ac3,aac,mp2,mp3,dca,dts" videoCodec="h264,mpeg4" type="Video" /> <DirectPlayProfile container="asf" audioCodec="wmav2,wmapro" videoCodec="vc1" type="Video" /> <DirectPlayProfile container="asf" audioCodec="mp2,ac3" videoCodec="mpeg2video" type="Video" /> <DirectPlayProfile container="mp3" audioCodec="mp2,mp3" type="Audio" /> diff --git a/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj b/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj index 134385765..ac127458e 100644 --- a/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj +++ b/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj @@ -63,7 +63,6 @@ <Compile Include="Parsers\MovieXmlParser.cs" /> <Compile Include="Parsers\MusicVideoXmlParser.cs" /> <Compile Include="Parsers\PlaylistXmlParser.cs" /> - <Compile Include="Parsers\SeasonXmlParser.cs" /> <Compile Include="Parsers\SeriesXmlParser.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Providers\BoxSetXmlProvider.cs" /> @@ -75,7 +74,6 @@ <Compile Include="Providers\MusicVideoXmlProvider.cs" /> <Compile Include="Providers\PersonXmlProvider.cs" /> <Compile Include="Providers\PlaylistXmlProvider.cs" /> - <Compile Include="Providers\SeasonXmlProvider.cs" /> <Compile Include="Providers\SeriesXmlProvider.cs" /> <Compile Include="Providers\VideoXmlProvider.cs" /> <Compile Include="Savers\BoxSetXmlSaver.cs" /> diff --git a/MediaBrowser.LocalMetadata/Parsers/BoxSetXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/BoxSetXmlParser.cs index 772af0673..9ebb357c6 100644 --- a/MediaBrowser.LocalMetadata/Parsers/BoxSetXmlParser.cs +++ b/MediaBrowser.LocalMetadata/Parsers/BoxSetXmlParser.cs @@ -9,8 +9,8 @@ namespace MediaBrowser.LocalMetadata.Parsers { public class BoxSetXmlParser : BaseItemXmlParser<BoxSet> { - public BoxSetXmlParser(ILogger logger) - : base(logger) + public BoxSetXmlParser(ILogger logger, IProviderManager providerManager) + : base(logger, providerManager) { } diff --git a/MediaBrowser.LocalMetadata/Parsers/EpisodeXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/EpisodeXmlParser.cs index d2ef01465..71f6d3fe9 100644 --- a/MediaBrowser.LocalMetadata/Parsers/EpisodeXmlParser.cs +++ b/MediaBrowser.LocalMetadata/Parsers/EpisodeXmlParser.cs @@ -20,8 +20,8 @@ namespace MediaBrowser.LocalMetadata.Parsers private List<LocalImageInfo> _imagesFound; private readonly IFileSystem _fileSystem; - public EpisodeXmlParser(ILogger logger, IFileSystem fileSystem) - : base(logger) + public EpisodeXmlParser(ILogger logger, IFileSystem fileSystem, IProviderManager providerManager) + : base(logger, providerManager) { _fileSystem = fileSystem; } diff --git a/MediaBrowser.LocalMetadata/Parsers/GameSystemXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/GameSystemXmlParser.cs index 09cc1fdd7..75df53958 100644 --- a/MediaBrowser.LocalMetadata/Parsers/GameSystemXmlParser.cs +++ b/MediaBrowser.LocalMetadata/Parsers/GameSystemXmlParser.cs @@ -10,8 +10,8 @@ namespace MediaBrowser.LocalMetadata.Parsers { public class GameSystemXmlParser : BaseItemXmlParser<GameSystem> { - public GameSystemXmlParser(ILogger logger) - : base(logger) + public GameSystemXmlParser(ILogger logger, IProviderManager providerManager) + : base(logger, providerManager) { } diff --git a/MediaBrowser.LocalMetadata/Parsers/GameXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/GameXmlParser.cs index 4bfcae44f..956b8baef 100644 --- a/MediaBrowser.LocalMetadata/Parsers/GameXmlParser.cs +++ b/MediaBrowser.LocalMetadata/Parsers/GameXmlParser.cs @@ -16,8 +16,8 @@ namespace MediaBrowser.LocalMetadata.Parsers { private readonly CultureInfo _usCulture = new CultureInfo("en-US"); - public GameXmlParser(ILogger logger) - : base(logger) + public GameXmlParser(ILogger logger, IProviderManager providerManager) + : base(logger, providerManager) { } diff --git a/MediaBrowser.LocalMetadata/Parsers/MovieXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/MovieXmlParser.cs index 1c1bbe71e..6e78d365e 100644 --- a/MediaBrowser.LocalMetadata/Parsers/MovieXmlParser.cs +++ b/MediaBrowser.LocalMetadata/Parsers/MovieXmlParser.cs @@ -12,8 +12,8 @@ namespace MediaBrowser.LocalMetadata.Parsers public class BaseVideoXmlParser<T> : BaseItemXmlParser<T> where T : Video { - public BaseVideoXmlParser(ILogger logger) - : base(logger) + public BaseVideoXmlParser(ILogger logger, IProviderManager providerManager) + : base(logger, providerManager) { } @@ -50,15 +50,15 @@ namespace MediaBrowser.LocalMetadata.Parsers public class MovieXmlParser : BaseVideoXmlParser<Movie> { - public MovieXmlParser(ILogger logger) : base(logger) + public MovieXmlParser(ILogger logger, IProviderManager providerManager) : base(logger, providerManager) { } } public class VideoXmlParser : BaseVideoXmlParser<Video> { - public VideoXmlParser(ILogger logger) - : base(logger) + public VideoXmlParser(ILogger logger, IProviderManager providerManager) + : base(logger, providerManager) { } } diff --git a/MediaBrowser.LocalMetadata/Parsers/MusicVideoXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/MusicVideoXmlParser.cs index d93746aa0..5f0b447e8 100644 --- a/MediaBrowser.LocalMetadata/Parsers/MusicVideoXmlParser.cs +++ b/MediaBrowser.LocalMetadata/Parsers/MusicVideoXmlParser.cs @@ -12,8 +12,8 @@ namespace MediaBrowser.LocalMetadata.Parsers /// Initializes a new instance of the <see cref="BaseItemXmlParser{T}" /> class. /// </summary> /// <param name="logger">The logger.</param> - public MusicVideoXmlParser(ILogger logger) - : base(logger) + public MusicVideoXmlParser(ILogger logger, IProviderManager providerManager) + : base(logger, providerManager) { } diff --git a/MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs index d4552fe12..de46c0a86 100644 --- a/MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs +++ b/MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs @@ -11,8 +11,8 @@ namespace MediaBrowser.LocalMetadata.Parsers { public class PlaylistXmlParser : BaseItemXmlParser<Playlist> { - public PlaylistXmlParser(ILogger logger) - : base(logger) + public PlaylistXmlParser(ILogger logger, IProviderManager providerManager) + : base(logger, providerManager) { } diff --git a/MediaBrowser.LocalMetadata/Parsers/SeasonXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/SeasonXmlParser.cs deleted file mode 100644 index 7fd60d3f7..000000000 --- a/MediaBrowser.LocalMetadata/Parsers/SeasonXmlParser.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System.Xml; -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Logging; - -namespace MediaBrowser.LocalMetadata.Parsers -{ - public class SeasonXmlParser : BaseItemXmlParser<Season> - { - public SeasonXmlParser(ILogger logger) - : base(logger) - { - } - - /// <summary> - /// Fetches the data from XML node. - /// </summary> - /// <param name="reader">The reader.</param> - /// <param name="result">The result.</param> - protected override void FetchDataFromXmlNode(XmlReader reader, MetadataResult<Season> result) - { - var item = result.Item; - - switch (reader.Name) - { - case "SeasonNumber": - { - var number = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(number)) - { - int num; - - if (int.TryParse(number, out num)) - { - item.IndexNumber = num; - } - } - break; - } - - default: - base.FetchDataFromXmlNode(reader, result); - break; - } - } - } -} diff --git a/MediaBrowser.LocalMetadata/Parsers/SeriesXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/SeriesXmlParser.cs index 8133bd9fe..7b7fb4751 100644 --- a/MediaBrowser.LocalMetadata/Parsers/SeriesXmlParser.cs +++ b/MediaBrowser.LocalMetadata/Parsers/SeriesXmlParser.cs @@ -17,8 +17,8 @@ namespace MediaBrowser.LocalMetadata.Parsers /// Initializes a new instance of the <see cref="BaseItemXmlParser{T}" /> class. /// </summary> /// <param name="logger">The logger.</param> - public SeriesXmlParser(ILogger logger) - : base(logger) + public SeriesXmlParser(ILogger logger, IProviderManager providerManager) + : base(logger, providerManager) { } diff --git a/MediaBrowser.LocalMetadata/Providers/BoxSetXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/BoxSetXmlProvider.cs index 217a5f355..3acb2b74c 100644 --- a/MediaBrowser.LocalMetadata/Providers/BoxSetXmlProvider.cs +++ b/MediaBrowser.LocalMetadata/Providers/BoxSetXmlProvider.cs @@ -14,16 +14,18 @@ namespace MediaBrowser.LocalMetadata.Providers public class BoxSetXmlProvider : BaseXmlProvider<BoxSet> { private readonly ILogger _logger; + private readonly IProviderManager _providerManager; - public BoxSetXmlProvider(IFileSystem fileSystem, ILogger logger) + public BoxSetXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager) : base(fileSystem) { _logger = logger; + _providerManager = providerManager; } protected override void Fetch(MetadataResult<BoxSet> result, string path, CancellationToken cancellationToken) { - new BoxSetXmlParser(_logger).Fetch(result, path, cancellationToken); + new BoxSetXmlParser(_logger, _providerManager).Fetch(result, path, cancellationToken); } protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService) diff --git a/MediaBrowser.LocalMetadata/Providers/EpisodeXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/EpisodeXmlProvider.cs index d3e365838..493df8c6a 100644 --- a/MediaBrowser.LocalMetadata/Providers/EpisodeXmlProvider.cs +++ b/MediaBrowser.LocalMetadata/Providers/EpisodeXmlProvider.cs @@ -13,11 +13,13 @@ namespace MediaBrowser.LocalMetadata.Providers public class EpisodeXmlProvider : BaseXmlProvider<Episode> { private readonly ILogger _logger; + private readonly IProviderManager _providerManager; - public EpisodeXmlProvider(IFileSystem fileSystem, ILogger logger) + public EpisodeXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager) : base(fileSystem) { _logger = logger; + _providerManager = providerManager; } protected override void Fetch(MetadataResult<Episode> result, string path, CancellationToken cancellationToken) @@ -25,7 +27,7 @@ namespace MediaBrowser.LocalMetadata.Providers var images = new List<LocalImageInfo>(); var chapters = new List<ChapterInfo>(); - new EpisodeXmlParser(_logger, FileSystem).Fetch(result, images, path, cancellationToken); + new EpisodeXmlParser(_logger, FileSystem, _providerManager).Fetch(result, images, path, cancellationToken); result.Images = images; } diff --git a/MediaBrowser.LocalMetadata/Providers/FolderXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/FolderXmlProvider.cs index 248fad363..7ac41e5cc 100644 --- a/MediaBrowser.LocalMetadata/Providers/FolderXmlProvider.cs +++ b/MediaBrowser.LocalMetadata/Providers/FolderXmlProvider.cs @@ -13,16 +13,18 @@ namespace MediaBrowser.LocalMetadata.Providers public class FolderXmlProvider : BaseXmlProvider<Folder> { private readonly ILogger _logger; + private readonly IProviderManager _providerManager; - public FolderXmlProvider(IFileSystem fileSystem, ILogger logger) + public FolderXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager) : base(fileSystem) { _logger = logger; + _providerManager = providerManager; } protected override void Fetch(MetadataResult<Folder> result, string path, CancellationToken cancellationToken) { - new BaseItemXmlParser<Folder>(_logger).Fetch(result, path, cancellationToken); + new BaseItemXmlParser<Folder>(_logger, _providerManager).Fetch(result, path, cancellationToken); } protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService) diff --git a/MediaBrowser.LocalMetadata/Providers/GameSystemXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/GameSystemXmlProvider.cs index 646fce805..942befb83 100644 --- a/MediaBrowser.LocalMetadata/Providers/GameSystemXmlProvider.cs +++ b/MediaBrowser.LocalMetadata/Providers/GameSystemXmlProvider.cs @@ -11,16 +11,18 @@ namespace MediaBrowser.LocalMetadata.Providers public class GameSystemXmlProvider : BaseXmlProvider<GameSystem> { private readonly ILogger _logger; + private readonly IProviderManager _providerManager; - public GameSystemXmlProvider(IFileSystem fileSystem, ILogger logger) + public GameSystemXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager) : base(fileSystem) { _logger = logger; + _providerManager = providerManager; } protected override void Fetch(MetadataResult<GameSystem> result, string path, CancellationToken cancellationToken) { - new GameSystemXmlParser(_logger).Fetch(result, path, cancellationToken); + new GameSystemXmlParser(_logger, _providerManager).Fetch(result, path, cancellationToken); } protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService) diff --git a/MediaBrowser.LocalMetadata/Providers/GameXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/GameXmlProvider.cs index 28736eddd..c562df7fb 100644 --- a/MediaBrowser.LocalMetadata/Providers/GameXmlProvider.cs +++ b/MediaBrowser.LocalMetadata/Providers/GameXmlProvider.cs @@ -11,16 +11,18 @@ namespace MediaBrowser.LocalMetadata.Providers public class GameXmlProvider : BaseXmlProvider<Game> { private readonly ILogger _logger; + private readonly IProviderManager _providerManager; - public GameXmlProvider(IFileSystem fileSystem, ILogger logger) + public GameXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager) : base(fileSystem) { _logger = logger; + _providerManager = providerManager; } protected override void Fetch(MetadataResult<Game> result, string path, CancellationToken cancellationToken) { - new GameXmlParser(_logger).Fetch(result, path, cancellationToken); + new GameXmlParser(_logger, _providerManager).Fetch(result, path, cancellationToken); } protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService) diff --git a/MediaBrowser.LocalMetadata/Providers/MovieXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/MovieXmlProvider.cs index e4f83dd1f..333ea2823 100644 --- a/MediaBrowser.LocalMetadata/Providers/MovieXmlProvider.cs +++ b/MediaBrowser.LocalMetadata/Providers/MovieXmlProvider.cs @@ -11,16 +11,18 @@ namespace MediaBrowser.LocalMetadata.Providers public class MovieXmlProvider : BaseXmlProvider<Movie> { private readonly ILogger _logger; + private readonly IProviderManager _providerManager; - public MovieXmlProvider(IFileSystem fileSystem, ILogger logger) + public MovieXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager) : base(fileSystem) { _logger = logger; + _providerManager = providerManager; } protected override void Fetch(MetadataResult<Movie> result, string path, CancellationToken cancellationToken) { - new MovieXmlParser(_logger).Fetch(result, path, cancellationToken); + new MovieXmlParser(_logger, _providerManager).Fetch(result, path, cancellationToken); } protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService) diff --git a/MediaBrowser.LocalMetadata/Providers/MusicVideoXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/MusicVideoXmlProvider.cs index 1060fe895..49d8c09cc 100644 --- a/MediaBrowser.LocalMetadata/Providers/MusicVideoXmlProvider.cs +++ b/MediaBrowser.LocalMetadata/Providers/MusicVideoXmlProvider.cs @@ -10,16 +10,18 @@ namespace MediaBrowser.LocalMetadata.Providers class MusicVideoXmlProvider : BaseXmlProvider<MusicVideo> { private readonly ILogger _logger; + private readonly IProviderManager _providerManager; - public MusicVideoXmlProvider(IFileSystem fileSystem, ILogger logger) + public MusicVideoXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager) : base(fileSystem) { _logger = logger; + _providerManager = providerManager; } protected override void Fetch(MetadataResult<MusicVideo> result, string path, CancellationToken cancellationToken) { - new MusicVideoXmlParser(_logger).Fetch(result, path, cancellationToken); + new MusicVideoXmlParser(_logger, _providerManager).Fetch(result, path, cancellationToken); } protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService) diff --git a/MediaBrowser.LocalMetadata/Providers/PersonXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/PersonXmlProvider.cs index b65977c8e..2ccb8968b 100644 --- a/MediaBrowser.LocalMetadata/Providers/PersonXmlProvider.cs +++ b/MediaBrowser.LocalMetadata/Providers/PersonXmlProvider.cs @@ -10,16 +10,18 @@ namespace MediaBrowser.LocalMetadata.Providers public class PersonXmlProvider : BaseXmlProvider<Person> { private readonly ILogger _logger; + private readonly IProviderManager _providerManager; - public PersonXmlProvider(IFileSystem fileSystem, ILogger logger) + public PersonXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager) : base(fileSystem) { _logger = logger; + _providerManager = providerManager; } protected override void Fetch(MetadataResult<Person> result, string path, CancellationToken cancellationToken) { - new BaseItemXmlParser<Person>(_logger).Fetch(result, path, cancellationToken); + new BaseItemXmlParser<Person>(_logger, _providerManager).Fetch(result, path, cancellationToken); } protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService) diff --git a/MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs index eb9e9a660..149a3142d 100644 --- a/MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs +++ b/MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs @@ -11,16 +11,18 @@ namespace MediaBrowser.LocalMetadata.Providers class PlaylistXmlProvider : BaseXmlProvider<Playlist> { private readonly ILogger _logger; + private readonly IProviderManager _providerManager; - public PlaylistXmlProvider(IFileSystem fileSystem, ILogger logger) + public PlaylistXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager) : base(fileSystem) { _logger = logger; + _providerManager = providerManager; } protected override void Fetch(MetadataResult<Playlist> result, string path, CancellationToken cancellationToken) { - new PlaylistXmlParser(_logger).Fetch(result, path, cancellationToken); + new PlaylistXmlParser(_logger, _providerManager).Fetch(result, path, cancellationToken); } protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService) diff --git a/MediaBrowser.LocalMetadata/Providers/SeasonXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/SeasonXmlProvider.cs deleted file mode 100644 index 7c82d9811..000000000 --- a/MediaBrowser.LocalMetadata/Providers/SeasonXmlProvider.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System.IO; -using System.Threading; -using CommonIO; -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.Providers; -using MediaBrowser.LocalMetadata.Parsers; -using MediaBrowser.Model.Logging; - -namespace MediaBrowser.LocalMetadata.Providers -{ - /// <summary> - /// Class SeriesProviderFromXml - /// </summary> - public class SeasonXmlProvider : BaseXmlProvider<Season>, IHasOrder - { - private readonly ILogger _logger; - - public SeasonXmlProvider(IFileSystem fileSystem, ILogger logger) - : base(fileSystem) - { - _logger = logger; - } - - protected override void Fetch(MetadataResult<Season> result, string path, CancellationToken cancellationToken) - { - new SeasonXmlParser(_logger).Fetch(result, path, cancellationToken); - } - - protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService) - { - return directoryService.GetFile(Path.Combine(info.Path, "season.xml")); - } - - public int Order - { - get - { - // After Xbmc - return 1; - } - } - } -} - diff --git a/MediaBrowser.LocalMetadata/Providers/SeriesXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/SeriesXmlProvider.cs index 0893f192f..26d3c7539 100644 --- a/MediaBrowser.LocalMetadata/Providers/SeriesXmlProvider.cs +++ b/MediaBrowser.LocalMetadata/Providers/SeriesXmlProvider.cs @@ -14,16 +14,18 @@ namespace MediaBrowser.LocalMetadata.Providers public class SeriesXmlProvider : BaseXmlProvider<Series>, IHasOrder { private readonly ILogger _logger; + private readonly IProviderManager _providerManager; - public SeriesXmlProvider(IFileSystem fileSystem, ILogger logger) + public SeriesXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager) : base(fileSystem) { _logger = logger; + _providerManager = providerManager; } protected override void Fetch(MetadataResult<Series> result, string path, CancellationToken cancellationToken) { - new SeriesXmlParser(_logger).Fetch(result, path, cancellationToken); + new SeriesXmlParser(_logger, _providerManager).Fetch(result, path, cancellationToken); } protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService) diff --git a/MediaBrowser.LocalMetadata/Providers/VideoXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/VideoXmlProvider.cs index c7bde4fa8..50f3bcda4 100644 --- a/MediaBrowser.LocalMetadata/Providers/VideoXmlProvider.cs +++ b/MediaBrowser.LocalMetadata/Providers/VideoXmlProvider.cs @@ -10,16 +10,18 @@ namespace MediaBrowser.LocalMetadata.Providers class VideoXmlProvider : BaseXmlProvider<Video> { private readonly ILogger _logger; + private readonly IProviderManager _providerManager; - public VideoXmlProvider(IFileSystem fileSystem, ILogger logger) + public VideoXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager) : base(fileSystem) { _logger = logger; + _providerManager = providerManager; } protected override void Fetch(MetadataResult<Video> result, string path, CancellationToken cancellationToken) { - new VideoXmlParser(_logger).Fetch(result, path, cancellationToken); + new VideoXmlParser(_logger, _providerManager).Fetch(result, path, cancellationToken); } protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService) diff --git a/MediaBrowser.LocalMetadata/Savers/XmlSaverHelpers.cs b/MediaBrowser.LocalMetadata/Savers/XmlSaverHelpers.cs index d472d4812..ac4ebbefe 100644 --- a/MediaBrowser.LocalMetadata/Savers/XmlSaverHelpers.cs +++ b/MediaBrowser.LocalMetadata/Savers/XmlSaverHelpers.cs @@ -133,7 +133,7 @@ namespace MediaBrowser.LocalMetadata.Savers /// <param name="xmlTagsUsed">The XML tags used.</param> public static void Save(StringBuilder xml, string path, List<string> xmlTagsUsed, IServerConfigurationManager config, IFileSystem fileSystem) { - if (fileSystem.FileExists(path)) + if (fileSystem.FileExists(path)) { var position = xml.ToString().LastIndexOf("</", StringComparison.OrdinalIgnoreCase); xml.Insert(position, GetCustomTags(path, xmlTagsUsed)); @@ -145,7 +145,7 @@ namespace MediaBrowser.LocalMetadata.Savers //Add the new node to the document. xmlDocument.InsertBefore(xmlDocument.CreateXmlDeclaration("1.0", "UTF-8", "yes"), xmlDocument.DocumentElement); - fileSystem.CreateDirectory(Path.GetDirectoryName(path)); + fileSystem.CreateDirectory(Path.GetDirectoryName(path)); var wasHidden = false; @@ -445,121 +445,18 @@ namespace MediaBrowser.LocalMetadata.Savers builder.Append("<RunningTime>" + Convert.ToInt32(timespan.TotalMinutes).ToString(UsCulture) + "</RunningTime>"); } - var imdb = item.GetProviderId(MetadataProviders.Imdb); - - if (!string.IsNullOrEmpty(imdb)) - { - builder.Append("<IMDB>" + SecurityElement.Escape(imdb) + "</IMDB>"); - } - - var tmdb = item.GetProviderId(MetadataProviders.Tmdb); - - if (!string.IsNullOrEmpty(tmdb)) - { - builder.Append("<TMDbId>" + SecurityElement.Escape(tmdb) + "</TMDbId>"); - } - - if (!(item is Series)) + if (item.ProviderIds != null) { - var tvdb = item.GetProviderId(MetadataProviders.Tvdb); - - if (!string.IsNullOrEmpty(tvdb)) + foreach (var providerKey in item.ProviderIds.Keys) { - builder.Append("<TvDbId>" + SecurityElement.Escape(tvdb) + "</TvDbId>"); + var providerId = item.ProviderIds[providerKey]; + if (!string.IsNullOrEmpty(providerId)) + { + builder.Append(string.Format("<{0}>{1}</{0}>", providerKey + "Id", SecurityElement.Escape(providerId))); + } } } - var externalId = item.GetProviderId(MetadataProviders.Tvcom); - - if (!string.IsNullOrEmpty(externalId)) - { - builder.Append("<TVcomId>" + SecurityElement.Escape(externalId) + "</TVcomId>"); - } - - externalId = item.GetProviderId(MetadataProviders.RottenTomatoes); - - if (!string.IsNullOrEmpty(externalId)) - { - builder.Append("<RottenTomatoesId>" + SecurityElement.Escape(externalId) + "</RottenTomatoesId>"); - } - - externalId = item.GetProviderId(MetadataProviders.Zap2It); - - if (!string.IsNullOrEmpty(externalId)) - { - builder.Append("<Zap2ItId>" + SecurityElement.Escape(externalId) + "</Zap2ItId>"); - } - - externalId = item.GetProviderId(MetadataProviders.MusicBrainzAlbum); - - if (!string.IsNullOrEmpty(externalId)) - { - builder.Append("<MusicBrainzAlbumId>" + SecurityElement.Escape(externalId) + "</MusicBrainzAlbumId>"); - } - - externalId = item.GetProviderId(MetadataProviders.MusicBrainzAlbumArtist); - - if (!string.IsNullOrEmpty(externalId)) - { - builder.Append("<MusicBrainzAlbumArtistId>" + SecurityElement.Escape(externalId) + "</MusicBrainzAlbumArtistId>"); - } - - externalId = item.GetProviderId(MetadataProviders.MusicBrainzArtist); - - if (!string.IsNullOrEmpty(externalId)) - { - builder.Append("<MusicBrainzArtistId>" + SecurityElement.Escape(externalId) + "</MusicBrainzArtistId>"); - } - - externalId = item.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup); - - if (!string.IsNullOrEmpty(externalId)) - { - builder.Append("<MusicBrainzReleaseGroupId>" + SecurityElement.Escape(externalId) + "</MusicBrainzReleaseGroupId>"); - } - - externalId = item.GetProviderId(MetadataProviders.Gamesdb); - - if (!string.IsNullOrEmpty(externalId)) - { - builder.Append("<GamesDbId>" + SecurityElement.Escape(externalId) + "</GamesDbId>"); - } - - externalId = item.GetProviderId(MetadataProviders.TmdbCollection); - - if (!string.IsNullOrEmpty(externalId)) - { - builder.Append("<TMDbCollectionId>" + SecurityElement.Escape(externalId) + "</TMDbCollectionId>"); - } - - externalId = item.GetProviderId(MetadataProviders.AudioDbArtist); - - if (!string.IsNullOrEmpty(externalId)) - { - builder.Append("<AudioDbArtistId>" + SecurityElement.Escape(externalId) + "</AudioDbArtistId>"); - } - - externalId = item.GetProviderId(MetadataProviders.AudioDbAlbum); - - if (!string.IsNullOrEmpty(externalId)) - { - builder.Append("<AudioDbAlbumId>" + SecurityElement.Escape(externalId) + "</AudioDbAlbumId>"); - } - - externalId = item.GetProviderId(MetadataProviders.TvRage); - - if (!string.IsNullOrEmpty(externalId)) - { - builder.Append("<TVRageId>" + SecurityElement.Escape(externalId) + "</TVRageId>"); - } - - externalId = item.GetProviderId(MetadataProviders.TvMaze); - - if (!string.IsNullOrEmpty(externalId)) - { - builder.Append("<TvMazeId>" + SecurityElement.Escape(externalId) + "</TvMazeId>"); - } - var hasTagline = item as IHasTaglines; if (hasTagline != null) { diff --git a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs index 6bf414dfa..2ded8a66f 100644 --- a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs @@ -481,6 +481,16 @@ namespace MediaBrowser.MediaEncoding.Encoder } } + if (state.IsVideoRequest) + { + var encodingOptions = GetEncodingOptions(); + var videoEncoder = EncodingJobFactory.GetVideoEncoder(MediaEncoder, state, encodingOptions); + if (videoEncoder.IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1) + { + arg = "-hwaccel vaapi -hwaccel_output_format vaapi -vaapi_device " + encodingOptions.VaapiDevice + " " + arg; + } + } + return arg.Trim(); } @@ -585,23 +595,23 @@ namespace MediaBrowser.MediaEncoding.Encoder /// Gets the video bitrate to specify on the command line /// </summary> /// <param name="state">The state.</param> - /// <param name="videoCodec">The video codec.</param> + /// <param name="videoEncoder">The video codec.</param> /// <returns>System.String.</returns> - protected string GetVideoQualityParam(EncodingJob state, string videoCodec) + protected string GetVideoQualityParam(EncodingJob state, string videoEncoder) { var param = string.Empty; var isVc1 = state.VideoStream != null && string.Equals(state.VideoStream.Codec, "vc1", StringComparison.OrdinalIgnoreCase); - if (string.Equals(videoCodec, "libx264", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase)) { param = "-preset superfast"; param += " -crf 23"; } - else if (string.Equals(videoCodec, "libx265", StringComparison.OrdinalIgnoreCase)) + else if (string.Equals(videoEncoder, "libx265", StringComparison.OrdinalIgnoreCase)) { param = "-preset fast"; @@ -609,20 +619,20 @@ namespace MediaBrowser.MediaEncoding.Encoder } // h264 (h264_qsv) - else if (string.Equals(videoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase)) + else if (string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase)) { param = "-preset 7 -look_ahead 0"; } // h264 (h264_nvenc) - else if (string.Equals(videoCodec, "h264_nvenc", StringComparison.OrdinalIgnoreCase)) + else if (string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase)) { param = "-preset llhq"; } // webm - else if (string.Equals(videoCodec, "libvpx", StringComparison.OrdinalIgnoreCase)) + else if (string.Equals(videoEncoder, "libvpx", StringComparison.OrdinalIgnoreCase)) { // Values 0-3, 0 being highest quality but slower var profileScore = 0; @@ -649,23 +659,23 @@ namespace MediaBrowser.MediaEncoding.Encoder qmax); } - else if (string.Equals(videoCodec, "mpeg4", StringComparison.OrdinalIgnoreCase)) + else if (string.Equals(videoEncoder, "mpeg4", StringComparison.OrdinalIgnoreCase)) { param = "-mbd rd -flags +mv4+aic -trellis 2 -cmp 2 -subcmp 2 -bf 2"; } // asf/wmv - else if (string.Equals(videoCodec, "wmv2", StringComparison.OrdinalIgnoreCase)) + else if (string.Equals(videoEncoder, "wmv2", StringComparison.OrdinalIgnoreCase)) { param = "-qmin 2"; } - else if (string.Equals(videoCodec, "msmpeg4", StringComparison.OrdinalIgnoreCase)) + else if (string.Equals(videoEncoder, "msmpeg4", StringComparison.OrdinalIgnoreCase)) { param = "-mbd 2"; } - param += GetVideoBitrateParam(state, videoCodec); + param += GetVideoBitrateParam(state, videoEncoder); var framerate = GetFramerateParam(state); if (framerate.HasValue) @@ -680,8 +690,8 @@ namespace MediaBrowser.MediaEncoding.Encoder if (!string.IsNullOrEmpty(state.Options.Profile)) { - if (!string.Equals(videoCodec, "h264_omx", StringComparison.OrdinalIgnoreCase) && - !string.Equals(videoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase)) + if (!string.Equals(videoEncoder, "h264_omx", StringComparison.OrdinalIgnoreCase) && + !string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase)) { // not supported by h264_omx param += " -profile:v " + state.Options.Profile; @@ -692,9 +702,11 @@ namespace MediaBrowser.MediaEncoding.Encoder if (!string.IsNullOrEmpty(levelString)) { + levelString = NormalizeTranscodingLevel(state.OutputVideoCodec, levelString); + // h264_qsv and h264_nvenc expect levels to be expressed as a decimal. libx264 supports decimal and non-decimal format - if (string.Equals(videoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase) || - string.Equals(videoCodec, "h264_nvenc", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) || + string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase)) { switch (levelString) { @@ -730,16 +742,16 @@ namespace MediaBrowser.MediaEncoding.Encoder break; } } - else if (!string.Equals(videoCodec, "h264_omx", StringComparison.OrdinalIgnoreCase)) + else if (!string.Equals(videoEncoder, "h264_omx", StringComparison.OrdinalIgnoreCase)) { param += " -level " + levelString; } } - if (!string.Equals(videoCodec, "h264_omx", StringComparison.OrdinalIgnoreCase) && - !string.Equals(videoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase) && - !string.Equals(videoCodec, "h264_nvenc", StringComparison.OrdinalIgnoreCase) && - !string.Equals(videoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase)) + if (!string.Equals(videoEncoder, "h264_omx", StringComparison.OrdinalIgnoreCase) && + !string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) && + !string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase) && + !string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase)) { param = "-pix_fmt yuv420p " + param; } @@ -747,6 +759,25 @@ namespace MediaBrowser.MediaEncoding.Encoder return param; } + private string NormalizeTranscodingLevel(string videoCodec, string level) + { + double requestLevel; + + // Clients may direct play higher than level 41, but there's no reason to transcode higher + if (double.TryParse(level, NumberStyles.Any, UsCulture, out requestLevel)) + { + if (string.Equals(videoCodec, "h264", StringComparison.OrdinalIgnoreCase)) + { + if (requestLevel > 41) + { + return "41"; + } + } + } + + return level; + } + protected string GetVideoBitrateParam(EncodingJob state, string videoCodec) { var bitrate = state.OutputVideoBitrate; diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index ad84ffee8..a2707aace 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -928,7 +928,13 @@ namespace MediaBrowser.MediaEncoding.Encoder { StartProcess(processWrapper); - ranToCompletion = process.WaitForExit(10000); + var timeoutMs = ConfigurationManager.Configuration.ImageExtractionTimeoutMs; + if (timeoutMs <= 0) + { + timeoutMs = Environment.Is64BitOperatingSystem ? (Environment.ProcessorCount > 2 ? 14000 : 20000) : 40000; + } + + ranToCompletion = process.WaitForExit(timeoutMs); if (!ranToCompletion) { diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs index a63aca11b..dc5e69283 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs @@ -818,14 +818,17 @@ namespace MediaBrowser.MediaEncoding.Subtitles public string GetSubtitleFileCharacterSetFromLanguage(string language) { + // https://developer.xamarin.com/api/type/System.Text.Encoding/ + switch (language.ToLower()) { + case "hun": + return "windows-1252"; case "pol": case "cze": case "ces": case "slo": case "slk": - case "hun": case "slv": case "srp": case "hrv": diff --git a/MediaBrowser.Model/Configuration/EncodingOptions.cs b/MediaBrowser.Model/Configuration/EncodingOptions.cs index 3c03dc12a..9a0b2d35b 100644 --- a/MediaBrowser.Model/Configuration/EncodingOptions.cs +++ b/MediaBrowser.Model/Configuration/EncodingOptions.cs @@ -11,6 +11,8 @@ namespace MediaBrowser.Model.Configuration public string HardwareAccelerationType { get; set; } public string EncoderAppPath { get; set; } public string VaapiDevice { get; set; } + public int H264Crf { get; set; } + public string H264Preset { get; set; } public EncodingOptions() { @@ -19,6 +21,7 @@ namespace MediaBrowser.Model.Configuration ThrottleDelaySeconds = 180; EncodingThreadCount = -1; VaapiDevice = "/dev/dri/card0"; + H264Crf = 23; } } } diff --git a/MediaBrowser.Model/Configuration/LibraryOptions.cs b/MediaBrowser.Model/Configuration/LibraryOptions.cs index 3fe694553..551363223 100644 --- a/MediaBrowser.Model/Configuration/LibraryOptions.cs +++ b/MediaBrowser.Model/Configuration/LibraryOptions.cs @@ -6,6 +6,8 @@ public bool EnablePhotos { get; set; } public bool EnableRealtimeMonitor { get; set; } public int SchemaVersion { get; set; } + public bool EnableChapterImageExtraction { get; set; } + public bool ExtractChapterImagesDuringLibraryScan { get; set; } public LibraryOptions() { diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index abe572cc8..40ac4be8a 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -204,7 +204,10 @@ namespace MediaBrowser.Model.Configuration public bool DisplayCollectionsView { get; set; } public string[] LocalNetworkAddresses { get; set; } public string[] CodecsUsed { get; set; } + public bool EnableChannelView { get; set; } + public bool EnableExternalContentInSuggestions { get; set; } + public int ImageExtractionTimeoutMs { get; set; } /// <summary> /// Initializes a new instance of the <see cref="ServerConfiguration" /> class. /// </summary> @@ -214,9 +217,11 @@ namespace MediaBrowser.Model.Configuration Migrations = new string[] { }; CodecsUsed = new string[] { }; SqliteCacheSize = 0; + ImageExtractionTimeoutMs = 0; EnableLocalizedGuids = true; DisplaySpecialsWithinSeasons = true; + EnableExternalContentInSuggestions = true; ImageSavingConvention = ImageSavingConvention.Compatible; PublicPort = 8096; @@ -229,6 +234,7 @@ namespace MediaBrowser.Model.Configuration EnableAnonymousUsageReporting = true; EnableAutomaticRestart = true; + EnableFolderView = true; EnableUPnP = true; SharingExpirationDays = 30; diff --git a/MediaBrowser.Model/Configuration/UserConfiguration.cs b/MediaBrowser.Model/Configuration/UserConfiguration.cs index 313c5243c..a6785a06a 100644 --- a/MediaBrowser.Model/Configuration/UserConfiguration.cs +++ b/MediaBrowser.Model/Configuration/UserConfiguration.cs @@ -41,7 +41,6 @@ namespace MediaBrowser.Model.Configuration public string[] PlainFolderViews { get; set; } public bool HidePlayedInLatest { get; set; } - public bool EnableChannelView { get; set; } public bool RememberAudioSelections { get; set; } public bool RememberSubtitleSelections { get; set; } diff --git a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs index 242a2d24e..ee7dd8b98 100644 --- a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs +++ b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs @@ -13,6 +13,7 @@ namespace MediaBrowser.Model.LiveTv public string SeriesRecordingPath { get; set; } public bool EnableAutoOrganize { get; set; } public bool EnableRecordingEncoding { get; set; } + public string RecordingEncodingFormat { get; set; } public bool EnableRecordingSubfolders { get; set; } public bool EnableOriginalAudioWithEncodedRecordings { get; set; } @@ -31,6 +32,7 @@ namespace MediaBrowser.Model.LiveTv TunerHosts = new List<TunerHostInfo>(); ListingProviders = new List<ListingsProviderInfo>(); MediaLocationsCreated = new string[] { }; + RecordingEncodingFormat = "mp4"; } } diff --git a/MediaBrowser.Model/LiveTv/ProgramAudio.cs b/MediaBrowser.Model/LiveTv/ProgramAudio.cs index 902079b9a..9a272492c 100644 --- a/MediaBrowser.Model/LiveTv/ProgramAudio.cs +++ b/MediaBrowser.Model/LiveTv/ProgramAudio.cs @@ -6,6 +6,7 @@ Stereo, Dolby, DolbyDigital, - Thx + Thx, + Atmos } }
\ No newline at end of file diff --git a/MediaBrowser.Model/LiveTv/RecordingQuery.cs b/MediaBrowser.Model/LiveTv/RecordingQuery.cs index 923d303f8..0ba5f1779 100644 --- a/MediaBrowser.Model/LiveTv/RecordingQuery.cs +++ b/MediaBrowser.Model/LiveTv/RecordingQuery.cs @@ -68,6 +68,10 @@ namespace MediaBrowser.Model.LiveTv /// <value>The fields.</value> public ItemFields[] Fields { get; set; } public bool? EnableImages { get; set; } + public bool? IsMovie { get; set; } + public bool? IsSeries { get; set; } + public bool? IsKids { get; set; } + public bool? IsSports { get; set; } public int? ImageTypeLimit { get; set; } public ImageType[] EnableImageTypes { get; set; } diff --git a/MediaBrowser.Model/System/SystemInfo.cs b/MediaBrowser.Model/System/SystemInfo.cs index 3d1de5b37..c4d056a61 100644 --- a/MediaBrowser.Model/System/SystemInfo.cs +++ b/MediaBrowser.Model/System/SystemInfo.cs @@ -8,6 +8,8 @@ namespace MediaBrowser.Model.System /// </summary> public class SystemInfo : PublicSystemInfo { + public PackageVersionClass SystemUpdateLevel { get; set; } + /// <summary> /// Gets or sets the display name of the operating system. /// </summary> diff --git a/MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs b/MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs index cdaa38366..2f534c12e 100644 --- a/MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs +++ b/MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using CommonIO; +using MediaBrowser.Controller.Collections; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; @@ -21,4 +22,17 @@ namespace MediaBrowser.Providers.Folders { } } + + public class ManualCollectionsFolderMetadataService : MetadataService<ManualCollectionsFolder, ItemLookupInfo> + { + protected override void MergeData(MetadataResult<ManualCollectionsFolder> source, MetadataResult<ManualCollectionsFolder> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings) + { + ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings); + } + + public ManualCollectionsFolderMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, fileSystem, userDataManager, libraryManager) + { + } + } + } diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs index c20823535..0f8cf93fb 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs @@ -261,11 +261,18 @@ namespace MediaBrowser.Providers.MediaInfo NormalizeChapterNames(chapters); + var libraryOptions = _libraryManager.GetLibraryOptions(video); + var extractDuringScan = chapterOptions.ExtractDuringLibraryScan; + if (libraryOptions != null && libraryOptions.SchemaVersion >= 2) + { + extractDuringScan = libraryOptions.ExtractChapterImagesDuringLibraryScan; + } + await _encodingManager.RefreshChapterImages(new ChapterImageRefreshOptions { Chapters = chapters, Video = video, - ExtractImages = chapterOptions.ExtractDuringLibraryScan, + ExtractImages = extractDuringScan, SaveChapters = false }, cancellationToken).ConfigureAwait(false); diff --git a/MediaBrowser.Providers/Music/AudioMetadataService.cs b/MediaBrowser.Providers/Music/AudioMetadataService.cs index 532128186..67ddd8981 100644 --- a/MediaBrowser.Providers/Music/AudioMetadataService.cs +++ b/MediaBrowser.Providers/Music/AudioMetadataService.cs @@ -6,6 +6,7 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Providers.Manager; using System.Collections.Generic; +using System.Linq; using CommonIO; namespace MediaBrowser.Providers.Music @@ -21,7 +22,7 @@ namespace MediaBrowser.Providers.Music if (replaceData || targetItem.Artists.Count == 0) { - targetItem.Artists = sourceItem.Artists; + targetItem.Artists = sourceItem.Artists.ToList(); } if (replaceData || string.IsNullOrEmpty(targetItem.Album)) diff --git a/MediaBrowser.Providers/TV/DummySeasonProvider.cs b/MediaBrowser.Providers/TV/DummySeasonProvider.cs index fe0ad78be..b74eac219 100644 --- a/MediaBrowser.Providers/TV/DummySeasonProvider.cs +++ b/MediaBrowser.Providers/TV/DummySeasonProvider.cs @@ -138,8 +138,6 @@ namespace MediaBrowser.Providers.TV .Where(i => i.LocationType == LocationType.Virtual) .ToList(); - var episodes = series.GetRecursiveChildren().OfType<Episode>().ToList(); - var seasonsToRemove = virtualSeasons .Where(i => { @@ -152,19 +150,15 @@ namespace MediaBrowser.Providers.TV { return true; } + } - // If there are no episodes with this season number, delete it - if (episodes.All(e => !e.ParentIndexNumber.HasValue || e.ParentIndexNumber.Value != seasonNumber)) - { - return true; - } - - return false; + // If there are no episodes with this season number, delete it + if (!i.GetEpisodes().Any()) + { + return true; } - // Season does not have a number - // Remove if there are no episodes directly in series without a season number - return episodes.All(s => s.ParentIndexNumber.HasValue || s.IsInSeasonFolder); + return false; }) .ToList(); diff --git a/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs b/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs index bb7e142b6..b76cf46b0 100644 --- a/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs +++ b/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs @@ -530,6 +530,19 @@ namespace MediaBrowser.Server.Implementations.Channels return GetChannelFeaturesDto(channel, channelProvider, channelProvider.GetChannelFeatures()); } + public bool SupportsSync(string channelId) + { + if (string.IsNullOrWhiteSpace(channelId)) + { + throw new ArgumentNullException("channelId"); + } + + //var channel = GetChannel(channelId); + var channelProvider = GetChannelProvider(channelId); + + return channelProvider.GetChannelFeatures().SupportsContentDownloading; + } + public ChannelFeatures GetChannelFeaturesDto(Channel channel, IChannel provider, InternalChannelFeatures features) @@ -1450,6 +1463,24 @@ namespace MediaBrowser.Server.Implementations.Channels return result; } + internal IChannel GetChannelProvider(string internalChannelId) + { + if (internalChannelId == null) + { + throw new ArgumentNullException("internalChannelId"); + } + + var result = GetAllChannels() + .FirstOrDefault(i => string.Equals(GetInternalChannelId(i.Name).ToString("N"), internalChannelId, StringComparison.OrdinalIgnoreCase)); + + if (result == null) + { + throw new ResourceNotFoundException("No channel provider found for channel id " + internalChannelId); + } + + return result; + } + private IEnumerable<BaseItem> ApplyFilters(IEnumerable<BaseItem> items, IEnumerable<ItemFilter> filters, User user) { foreach (var filter in filters.OrderByDescending(f => (int)f)) diff --git a/MediaBrowser.Server.Implementations/Collections/CollectionsDynamicFolder.cs b/MediaBrowser.Server.Implementations/Collections/CollectionsDynamicFolder.cs index 6cd9e9620..50bb6c559 100644 --- a/MediaBrowser.Server.Implementations/Collections/CollectionsDynamicFolder.cs +++ b/MediaBrowser.Server.Implementations/Collections/CollectionsDynamicFolder.cs @@ -2,6 +2,7 @@ using MediaBrowser.Controller.Entities; using System.IO; using CommonIO; +using MediaBrowser.Controller.Collections; namespace MediaBrowser.Server.Implementations.Collections { diff --git a/MediaBrowser.Server.Implementations/Connect/ConnectEntryPoint.cs b/MediaBrowser.Server.Implementations/Connect/ConnectEntryPoint.cs index 28a62c012..2b2373a47 100644 --- a/MediaBrowser.Server.Implementations/Connect/ConnectEntryPoint.cs +++ b/MediaBrowser.Server.Implementations/Connect/ConnectEntryPoint.cs @@ -43,7 +43,7 @@ namespace MediaBrowser.Server.Implementations.Connect { LoadCachedAddress(); - _timer = new PeriodicTimer(TimerCallback, null, TimeSpan.FromSeconds(5), TimeSpan.FromHours(3)); + _timer = new PeriodicTimer(TimerCallback, null, TimeSpan.FromSeconds(5), TimeSpan.FromHours(1)); ((ConnectManager)_connectManager).Start(); } diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs index be68162ca..9284f4fc7 100644 --- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs +++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs @@ -1168,6 +1168,26 @@ namespace MediaBrowser.Server.Implementations.Dto }; }) .ToList(); + + // Include artists that are not in the database yet, e.g., just added via metadata editor + var foundArtists = artistItems.Items.Select(i => i.Item1.Name).ToList(); + dto.ArtistItems.AddRange(hasArtist.Artists + .Except(foundArtists, new DistinctNameComparer()) + .Select(i => + { + var artist = _libraryManager.GetArtist(i); + if (artist != null) + { + return new NameIdPair + { + Name = artist.Name, + Id = artist.Id.ToString("N") + }; + } + + return null; + + }).Where(i => i != null)); } var hasAlbumArtist = item as IHasAlbumArtist; diff --git a/MediaBrowser.Server.Implementations/EntryPoints/UsageEntryPoint.cs b/MediaBrowser.Server.Implementations/EntryPoints/UsageEntryPoint.cs index f82bb01bb..d14bd4368 100644 --- a/MediaBrowser.Server.Implementations/EntryPoints/UsageEntryPoint.cs +++ b/MediaBrowser.Server.Implementations/EntryPoints/UsageEntryPoint.cs @@ -92,11 +92,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints DeviceId = session.DeviceId }; - // Report usage to remote server, except for web client, since we already have data on that - if (!string.Equals(info.AppName, "Dashboard", StringComparison.OrdinalIgnoreCase)) - { - ReportNewSession(info); - } + ReportNewSession(info); return info; } diff --git a/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs b/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs index 51a53fe21..8e46f8f03 100644 --- a/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -71,14 +71,17 @@ namespace MediaBrowser.Server.Implementations.HttpServer HostConfig.Instance.MapExceptionToStatusCode = new Dictionary<Type, int> { - {typeof (InvalidOperationException), 422}, + {typeof (InvalidOperationException), 500}, + {typeof (NotImplementedException), 500}, {typeof (ResourceNotFoundException), 404}, {typeof (FileNotFoundException), 404}, {typeof (DirectoryNotFoundException), 404}, {typeof (SecurityException), 401}, {typeof (PaymentRequiredException), 402}, {typeof (UnauthorizedAccessException), 500}, - {typeof (ApplicationException), 500} + {typeof (ApplicationException), 500}, + {typeof (PlatformNotSupportedException), 500}, + {typeof (NotSupportedException), 500} }; HostConfig.Instance.GlobalResponseHeaders = new Dictionary<string, string>(); @@ -99,14 +102,14 @@ namespace MediaBrowser.Server.Implementations.HttpServer // new SessionAuthProvider(_containerAdapter.Resolve<ISessionContext>()), //})); - PreRequestFilters.Add((httpReq, httpRes) => - { - //Handles Request and closes Responses after emitting global HTTP Headers - if (string.Equals(httpReq.Verb, "OPTIONS", StringComparison.OrdinalIgnoreCase)) - { - httpRes.EndRequest(); //add a 'using ServiceStack;' - } - }); + //PreRequestFilters.Add((httpReq, httpRes) => + //{ + // //Handles Request and closes Responses after emitting global HTTP Headers + // if (string.Equals(httpReq.Verb, "OPTIONS", StringComparison.OrdinalIgnoreCase)) + // { + // httpRes.EndRequest(); //add a 'using ServiceStack;' + // } + //}); HostContext.GlobalResponseFilters.Add(new ResponseFilter(_logger).FilterResponse); } @@ -400,6 +403,17 @@ namespace MediaBrowser.Server.Implementations.HttpServer return; } + if (string.Equals(httpReq.Verb, "OPTIONS", StringComparison.OrdinalIgnoreCase)) + { + httpRes.StatusCode = 200; + httpRes.AddHeader("Access-Control-Allow-Origin", "*"); + httpRes.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS"); + httpRes.AddHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, Range, X-MediaBrowser-Token, X-Emby-Authorization"); + httpRes.ContentType = "text/html"; + + httpRes.Close(); + } + var operationName = httpReq.OperationName; var localPath = url.LocalPath; diff --git a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs index 7ed4dc71e..c87d10ef4 100644 --- a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs +++ b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs @@ -46,6 +46,14 @@ namespace MediaBrowser.Server.Implementations.IO "TempSBE" }; + private readonly IReadOnlyList<string> _alwaysIgnoreSubstrings = new List<string> + { + // Synology + "@eaDir", + ".wd_tv", + ".actors" + }; + private readonly IReadOnlyList<string> _alwaysIgnoreExtensions = new List<string> { // thumbs.db @@ -421,10 +429,11 @@ namespace MediaBrowser.Server.Implementations.IO } var filename = Path.GetFileName(path); - + var monitorPath = !string.IsNullOrEmpty(filename) && !_alwaysIgnoreFiles.Contains(filename, StringComparer.OrdinalIgnoreCase) && - !_alwaysIgnoreExtensions.Contains(Path.GetExtension(path) ?? string.Empty, StringComparer.OrdinalIgnoreCase); + !_alwaysIgnoreExtensions.Contains(Path.GetExtension(path) ?? string.Empty, StringComparer.OrdinalIgnoreCase) && + _alwaysIgnoreSubstrings.All(i => path.IndexOf(i, StringComparison.OrdinalIgnoreCase) == -1); // Ignore certain files var tempIgnorePaths = _tempIgnoredPaths.Keys.ToList(); diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index 5d556e3a6..442e2ebe5 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -401,7 +401,7 @@ namespace MediaBrowser.Server.Implementations.Library var locationType = item.LocationType; var children = item.IsFolder - ? ((Folder)item).GetRecursiveChildren().ToList() + ? ((Folder)item).GetRecursiveChildren(false).ToList() : new List<BaseItem>(); foreach (var metadataPath in GetMetadataPaths(item, children)) @@ -621,9 +621,38 @@ namespace MediaBrowser.Server.Implementations.Library return ResolveItem(args, resolvers); } + private readonly List<string> _ignoredPaths = new List<string>(); + + public void RegisterIgnoredPath(string path) + { + lock (_ignoredPaths) + { + _ignoredPaths.Add(path); + } + } + public void UnRegisterIgnoredPath(string path) + { + lock (_ignoredPaths) + { + _ignoredPaths.Remove(path); + } + } + public bool IgnoreFile(FileSystemMetadata file, BaseItem parent) { - return EntityResolutionIgnoreRules.Any(r => r.ShouldIgnore(file, parent)); + if (EntityResolutionIgnoreRules.Any(r => r.ShouldIgnore(file, parent))) + { + return true; + } + + //lock (_ignoredPaths) + { + if (_ignoredPaths.Contains(file.FullName, StringComparer.OrdinalIgnoreCase)) + { + return true; + } + } + return false; } public IEnumerable<FileSystemMetadata> NormalizeRootPathList(IEnumerable<FileSystemMetadata> paths) diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs index aefb29f1a..4c6254330 100644 --- a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs +++ b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs @@ -54,14 +54,14 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV { if (args.IsDirectory) { + if (args.HasParent<Series>()) + { + return null; + } + var collectionType = args.GetCollectionType(); if (string.Equals(collectionType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase)) { - if (args.HasParent<Series>()) - { - return null; - } - var configuredContentType = _libraryManager.GetConfiguredContentType(args.Path); if (!string.Equals(configuredContentType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase)) { @@ -76,11 +76,6 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV { if (string.IsNullOrWhiteSpace(collectionType)) { - if (args.HasParent<Series>()) - { - return null; - } - if (args.Parent.IsRoot) { return null; diff --git a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs b/MediaBrowser.Server.Implementations/Library/UserViewManager.cs index 5fffa3d1f..2cbee7c97 100644 --- a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs +++ b/MediaBrowser.Server.Implementations/Library/UserViewManager.cs @@ -120,8 +120,8 @@ namespace MediaBrowser.Server.Implementations.Library }, cancellationToken).ConfigureAwait(false); var channels = channelResult.Items; - - if (user.Configuration.EnableChannelView && channels.Length > 0) + + if (_config.Configuration.EnableChannelView && channels.Length > 0) { list.Add(await _channelManager.GetInternalChannelFolder(cancellationToken).ConfigureAwait(false)); } diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 6acb0783e..8fa34109d 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -33,7 +33,7 @@ using Microsoft.Win32; namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV { - public class EmbyTV : ILiveTvService, ISupportsNewTimerIds, IHasRegistrationInfo, IDisposable + public class EmbyTV : ILiveTvService, ISupportsNewTimerIds, IDisposable { private readonly IApplicationHost _appHpst; private readonly ILogger _logger; @@ -46,7 +46,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV private readonly LiveTvManager _liveTvManager; private readonly IFileSystem _fileSystem; - private readonly ISecurityManager _security; private readonly ILibraryMonitor _libraryMonitor; private readonly ILibraryManager _libraryManager; @@ -62,7 +61,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV private readonly ConcurrentDictionary<string, ActiveRecordingInfo> _activeRecordings = new ConcurrentDictionary<string, ActiveRecordingInfo>(StringComparer.OrdinalIgnoreCase); - public EmbyTV(IApplicationHost appHost, ILogger logger, IJsonSerializer jsonSerializer, IHttpClient httpClient, IServerConfigurationManager config, ILiveTvManager liveTvManager, IFileSystem fileSystem, ISecurityManager security, ILibraryManager libraryManager, ILibraryMonitor libraryMonitor, IProviderManager providerManager, IFileOrganizationService organizationService, IMediaEncoder mediaEncoder, IPowerManagement powerManagement) + public EmbyTV(IApplicationHost appHost, ILogger logger, IJsonSerializer jsonSerializer, IHttpClient httpClient, IServerConfigurationManager config, ILiveTvManager liveTvManager, IFileSystem fileSystem, ILibraryManager libraryManager, ILibraryMonitor libraryMonitor, IProviderManager providerManager, IFileOrganizationService organizationService, IMediaEncoder mediaEncoder, IPowerManagement powerManagement) { Current = this; @@ -71,7 +70,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV _httpClient = httpClient; _config = config; _fileSystem = fileSystem; - _security = security; _libraryManager = libraryManager; _libraryMonitor = libraryMonitor; _providerManager = providerManager; @@ -851,29 +849,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV var recordPath = RecordingPath; var config = GetConfiguration(); - if (info.IsMovie) - { - var customRecordingPath = config.MovieRecordingPath; - var allowSubfolder = true; - if (!string.IsNullOrWhiteSpace(customRecordingPath)) - { - allowSubfolder = string.Equals(customRecordingPath, recordPath, StringComparison.OrdinalIgnoreCase); - recordPath = customRecordingPath; - } - - if (allowSubfolder && config.EnableRecordingSubfolders) - { - recordPath = Path.Combine(recordPath, "Movies"); - } - - var folderName = _fileSystem.GetValidFilename(info.Name).Trim(); - if (info.ProductionYear.HasValue) - { - folderName += " (" + info.ProductionYear.Value.ToString(CultureInfo.InvariantCulture) + ")"; - } - recordPath = Path.Combine(recordPath, folderName); - } - else if (info.IsSeries) + if (info.IsSeries) { var customRecordingPath = config.SeriesRecordingPath; var allowSubfolder = true; @@ -910,6 +886,28 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV recordPath = Path.Combine(recordPath, folderName); } } + else if (info.IsMovie) + { + var customRecordingPath = config.MovieRecordingPath; + var allowSubfolder = true; + if (!string.IsNullOrWhiteSpace(customRecordingPath)) + { + allowSubfolder = string.Equals(customRecordingPath, recordPath, StringComparison.OrdinalIgnoreCase); + recordPath = customRecordingPath; + } + + if (allowSubfolder && config.EnableRecordingSubfolders) + { + recordPath = Path.Combine(recordPath, "Movies"); + } + + var folderName = _fileSystem.GetValidFilename(info.Name).Trim(); + if (info.ProductionYear.HasValue) + { + folderName += " (" + info.ProductionYear.Value.ToString(CultureInfo.InvariantCulture) + ")"; + } + recordPath = Path.Combine(recordPath, folderName); + } else if (info.IsKids) { if (config.EnableRecordingSubfolders) @@ -995,6 +993,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV recordPath = recorder.GetOutputPath(mediaStreamInfo, recordPath); recordPath = EnsureFileUnique(recordPath, timer.Id); + _libraryManager.RegisterIgnoredPath(recordPath); _libraryMonitor.ReportFileSystemChangeBeginning(recordPath); _fileSystem.CreateDirectory(Path.GetDirectoryName(recordPath)); activeRecordingInfo.Path = recordPath; @@ -1046,6 +1045,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV semaphore.Release(); } + _libraryManager.UnRegisterIgnoredPath(recordPath); _libraryMonitor.ReportFileSystemChangeComplete(recordPath, true); ActiveRecordingInfo removed; @@ -1114,7 +1114,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV if (config.EnableRecordingEncoding) { - var regInfo = await _security.GetRegistrationStatus("embytvrecordingconversion").ConfigureAwait(false); + var regInfo = await _liveTvManager.GetRegistrationInfo("embytvrecordingconversion").ConfigureAwait(false); if (regInfo.IsValid) { @@ -1171,8 +1171,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV private async Task UpdateTimersForSeriesTimer(List<ProgramInfo> epgData, SeriesTimerInfo seriesTimer, bool deleteInvalidTimers) { var newTimers = GetTimersForSeries(seriesTimer, epgData, true).ToList(); - - var registration = await GetRegistrationInfo("seriesrecordings").ConfigureAwait(false); + + var registration = await _liveTvManager.GetRegistrationInfo("seriesrecordings").ConfigureAwait(false); if (registration.IsValid) { @@ -1349,20 +1349,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV } } - public Task<MBRegistrationRecord> GetRegistrationInfo(string feature) - { - if (string.Equals(feature, "seriesrecordings", StringComparison.OrdinalIgnoreCase)) - { - return _security.GetRegistrationStatus("embytvseriesrecordings"); - } - - return Task.FromResult(new MBRegistrationRecord - { - IsValid = true, - IsRegistered = true - }); - } - public List<VirtualFolderInfo> GetRecordingFolders() { var list = new List<VirtualFolderInfo>(); diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs index fc3a507d1..75ad3de59 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs @@ -46,18 +46,41 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV _httpClient = httpClient; } + private string OutputFormat + { + get + { + var format = _liveTvOptions.RecordingEncodingFormat; + + if (string.Equals(format, "mkv", StringComparison.OrdinalIgnoreCase)) + { + return "mkv"; + } + + return "mp4"; + } + } + public string GetOutputPath(MediaSourceInfo mediaSource, string targetFile) { - return Path.ChangeExtension(targetFile, ".mp4"); + return Path.ChangeExtension(targetFile, "." + OutputFormat); } public async Task Record(MediaSourceInfo mediaSource, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken) { + if (mediaSource.Path.IndexOf("m3u8", StringComparison.OrdinalIgnoreCase) != -1) + { + await RecordWithoutTempFile(mediaSource, targetFile, duration, onStarted, cancellationToken) + .ConfigureAwait(false); + + return; + } + var tempfile = Path.Combine(_appPaths.TranscodingTempPath, Guid.NewGuid().ToString("N") + ".ts"); try { - await RecordInternal(mediaSource, tempfile, targetFile, duration, onStarted, cancellationToken) + await RecordWithTempFile(mediaSource, tempfile, targetFile, duration, onStarted, cancellationToken) .ConfigureAwait(false); } finally @@ -73,7 +96,17 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV } } - public async Task RecordInternal(MediaSourceInfo mediaSource, string tempFile, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken) + private async Task RecordWithoutTempFile(MediaSourceInfo mediaSource, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken) + { + var durationToken = new CancellationTokenSource(duration); + cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token; + + await RecordFromFile(mediaSource, mediaSource.Path, targetFile, duration, onStarted, cancellationToken).ConfigureAwait(false); + + _logger.Info("Recording completed to file {0}", targetFile); + } + + private async Task RecordWithTempFile(MediaSourceInfo mediaSource, string tempFile, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken) { var httpRequestOptions = new HttpRequestOptions() { @@ -215,15 +248,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV private string GetAudioArgs(MediaSourceInfo mediaSource) { - // do not copy aac because many players have difficulty with aac_latm - var copyAudio = new[] { "mp3" }; var mediaStreams = mediaSource.MediaStreams ?? new List<MediaStream>(); var inputAudioCodec = mediaStreams.Where(i => i.Type == MediaStreamType.Audio).Select(i => i.Codec).FirstOrDefault() ?? string.Empty; - if (copyAudio.Contains(inputAudioCodec, StringComparer.OrdinalIgnoreCase)) - { - return "-codec:a:0 copy"; - } + // do not copy aac because many players have difficulty with aac_latm if (_liveTvOptions.EnableOriginalAudioWithEncodedRecordings && !string.Equals(inputAudioCodec, "aac", StringComparison.OrdinalIgnoreCase)) { return "-codec:a:0 copy"; diff --git a/MediaBrowser.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/MediaBrowser.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs index e37109c14..1a5ebedc2 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs @@ -194,14 +194,22 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings return station; } - if (string.IsNullOrWhiteSpace(channelName)) + if (!string.IsNullOrWhiteSpace(channelName)) { - return null; - } + channelName = NormalizeName(channelName); + + var result = channelPair.Values.FirstOrDefault(i => string.Equals(NormalizeName(i.callsign ?? string.Empty), channelName, StringComparison.OrdinalIgnoreCase)); - channelName = NormalizeName(channelName); + if (result != null) + { + return result; + } + } - return channelPair.Values.FirstOrDefault(i => string.Equals(NormalizeName(i.callsign ?? string.Empty), channelName, StringComparison.OrdinalIgnoreCase)); + if (!string.IsNullOrWhiteSpace(channelNumber)) + { + return channelPair.Values.FirstOrDefault(i => string.Equals(NormalizeName(i.stationID ?? string.Empty), channelNumber, StringComparison.OrdinalIgnoreCase)); + } } return null; @@ -348,7 +356,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings if (programInfo.audioProperties != null) { - if (programInfo.audioProperties.Exists(item => string.Equals(item, "dd 5.1", StringComparison.OrdinalIgnoreCase))) + if (programInfo.audioProperties.Exists(item => string.Equals(item, "atmos", StringComparison.OrdinalIgnoreCase))) + { + audioType = ProgramAudio.Atmos; + } + else if (programInfo.audioProperties.Exists(item => string.Equals(item, "dd 5.1", StringComparison.OrdinalIgnoreCase))) { audioType = ProgramAudio.DolbyDigital; } @@ -405,6 +417,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings if (programInfo.videoProperties != null) { info.IsHD = programInfo.videoProperties.Contains("hdtv", StringComparer.OrdinalIgnoreCase); + info.Is3D = programInfo.videoProperties.Contains("3d", StringComparer.OrdinalIgnoreCase); } if (details.contentRating != null && details.contentRating.Count > 0) @@ -785,9 +798,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings get { return "Schedules Direct"; } } + public static string TypeName = "SchedulesDirect"; public string Type { - get { return "SchedulesDirect"; } + get { return TypeName; } } private async Task<bool> HasLineup(ListingsProviderInfo info, CancellationToken cancellationToken) diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index 88017aa59..b3ced55a5 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -31,7 +31,11 @@ using CommonIO; using IniParser; using IniParser.Model; using MediaBrowser.Common.Events; +using MediaBrowser.Common.Security; +using MediaBrowser.Controller.Entities.Movies; +using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Model.Events; +using MediaBrowser.Server.Implementations.LiveTv.Listings; namespace MediaBrowser.Server.Implementations.LiveTv { @@ -49,6 +53,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv private readonly ITaskManager _taskManager; private readonly IJsonSerializer _jsonSerializer; private readonly IProviderManager _providerManager; + private readonly ISecurityManager _security; private readonly IDtoService _dtoService; private readonly ILocalizationManager _localization; @@ -71,7 +76,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv public event EventHandler<GenericEventArgs<TimerEventInfo>> TimerCreated; public event EventHandler<GenericEventArgs<TimerEventInfo>> SeriesTimerCreated; - public LiveTvManager(IApplicationHost appHost, IServerConfigurationManager config, ILogger logger, IItemRepository itemRepo, IImageProcessor imageProcessor, IUserDataManager userDataManager, IDtoService dtoService, IUserManager userManager, ILibraryManager libraryManager, ITaskManager taskManager, ILocalizationManager localization, IJsonSerializer jsonSerializer, IProviderManager providerManager, IFileSystem fileSystem) + public LiveTvManager(IApplicationHost appHost, IServerConfigurationManager config, ILogger logger, IItemRepository itemRepo, IImageProcessor imageProcessor, IUserDataManager userDataManager, IDtoService dtoService, IUserManager userManager, ILibraryManager libraryManager, ITaskManager taskManager, ILocalizationManager localization, IJsonSerializer jsonSerializer, IProviderManager providerManager, IFileSystem fileSystem, ISecurityManager security) { _config = config; _logger = logger; @@ -83,6 +88,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv _jsonSerializer = jsonSerializer; _providerManager = providerManager; _fileSystem = fileSystem; + _security = security; _dtoService = dtoService; _userDataManager = userDataManager; @@ -1423,6 +1429,49 @@ namespace MediaBrowser.Server.Implementations.LiveTv return new QueryResult<BaseItem>(); } + var includeItemTypes = new List<string>(); + var excludeItemTypes = new List<string>(); + var genres = new List<string>(); + + if (query.IsMovie.HasValue) + { + if (query.IsMovie.Value) + { + includeItemTypes.Add(typeof(Movie).Name); + } + else + { + excludeItemTypes.Add(typeof(Movie).Name); + } + } + if (query.IsSeries.HasValue) + { + if (query.IsSeries.Value) + { + includeItemTypes.Add(typeof(Episode).Name); + } + else + { + excludeItemTypes.Add(typeof(Episode).Name); + } + } + if (query.IsSports.HasValue) + { + if (query.IsSports.Value) + { + genres.Add("Sports"); + } + } + if (query.IsKids.HasValue) + { + if (query.IsKids.Value) + { + genres.Add("Kids"); + genres.Add("Children"); + genres.Add("Family"); + } + } + return _libraryManager.GetItemsResult(new InternalItemsQuery(user) { MediaTypes = new[] { MediaType.Video }, @@ -1430,13 +1479,75 @@ namespace MediaBrowser.Server.Implementations.LiveTv AncestorIds = folders.Select(i => i.Id.ToString("N")).ToArray(), IsFolder = false, ExcludeLocationTypes = new[] { LocationType.Virtual }, - Limit = Math.Min(200, query.Limit ?? int.MaxValue), + Limit = query.Limit, SortBy = new[] { ItemSortBy.DateCreated }, SortOrder = SortOrder.Descending, - EnableTotalRecordCount = query.EnableTotalRecordCount + EnableTotalRecordCount = query.EnableTotalRecordCount, + IncludeItemTypes = includeItemTypes.ToArray(), + ExcludeItemTypes = excludeItemTypes.ToArray(), + Genres = genres.ToArray() }); } + public async Task<QueryResult<BaseItemDto>> GetRecordingSeries(RecordingQuery query, DtoOptions options, CancellationToken cancellationToken) + { + var user = string.IsNullOrEmpty(query.UserId) ? null : _userManager.GetUserById(query.UserId); + if (user != null && !IsLiveTvEnabled(user)) + { + return new QueryResult<BaseItemDto>(); + } + + if (_services.Count > 1) + { + return new QueryResult<BaseItemDto>(); + } + + if (user == null || (query.IsInProgress ?? false)) + { + return new QueryResult<BaseItemDto>(); + } + + var folders = EmbyTV.EmbyTV.Current.GetRecordingFolders() + .SelectMany(i => i.Locations) + .Distinct(StringComparer.OrdinalIgnoreCase) + .Select(i => _libraryManager.FindByPath(i, true)) + .Where(i => i != null) + .Where(i => i.IsVisibleStandalone(user)) + .ToList(); + + if (folders.Count == 0) + { + return new QueryResult<BaseItemDto>(); + } + + var includeItemTypes = new List<string>(); + var excludeItemTypes = new List<string>(); + + includeItemTypes.Add(typeof(Series).Name); + + var internalResult = _libraryManager.GetItemsResult(new InternalItemsQuery(user) + { + Recursive = true, + AncestorIds = folders.Select(i => i.Id.ToString("N")).ToArray(), + Limit = query.Limit, + SortBy = new[] { ItemSortBy.DateCreated }, + SortOrder = SortOrder.Descending, + EnableTotalRecordCount = query.EnableTotalRecordCount, + IncludeItemTypes = includeItemTypes.ToArray(), + ExcludeItemTypes = excludeItemTypes.ToArray() + }); + + RemoveFields(options); + + var returnArray = (await _dtoService.GetBaseItemDtos(internalResult.Items, options, user).ConfigureAwait(false)).ToArray(); + + return new QueryResult<BaseItemDto> + { + Items = returnArray, + TotalRecordCount = internalResult.TotalRecordCount + }; + } + public async Task<QueryResult<BaseItem>> GetInternalRecordings(RecordingQuery query, CancellationToken cancellationToken) { var user = string.IsNullOrEmpty(query.UserId) ? null : _userManager.GetUserById(query.UserId); @@ -1492,6 +1603,30 @@ namespace MediaBrowser.Server.Implementations.LiveTv recordings = recordings.Where(i => i.Status == val); } + if (query.IsMovie.HasValue) + { + var val = query.IsMovie.Value; + recordings = recordings.Where(i => i.IsMovie == val); + } + + if (query.IsSeries.HasValue) + { + var val = query.IsSeries.Value; + recordings = recordings.Where(i => i.IsSeries == val); + } + + if (query.IsKids.HasValue) + { + var val = query.IsKids.Value; + recordings = recordings.Where(i => i.IsKids == val); + } + + if (query.IsSports.HasValue) + { + var val = query.IsSports.Value; + recordings = recordings.Where(i => i.IsSports == val); + } + if (!string.IsNullOrEmpty(query.SeriesTimerId)) { var guid = new Guid(query.SeriesTimerId); @@ -1950,16 +2085,16 @@ namespace MediaBrowser.Server.Implementations.LiveTv dto.Number = channel.Number; dto.ChannelNumber = channel.Number; dto.ChannelType = channel.ChannelType; - dto.ServiceName = GetService(channel).Name; + dto.ServiceName = channel.ServiceName; if (options.Fields.Contains(ItemFields.MediaSources)) { dto.MediaSources = channel.GetMediaSources(true).ToList(); } - var channelIdString = channel.Id.ToString("N"); if (options.AddCurrentProgram) { + var channelIdString = channel.Id.ToString("N"); var currentProgram = programs.FirstOrDefault(i => string.Equals(i.ChannelId, channelIdString)); if (currentProgram != null) @@ -2091,6 +2226,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv public async Task CreateSeriesTimer(SeriesTimerInfoDto timer, CancellationToken cancellationToken) { + var registration = await GetRegistrationInfo("seriesrecordings").ConfigureAwait(false); + + if (!registration.IsValid) + { + _logger.Info("Creating series recordings requires an active Emby Premiere subscription."); + return; + } + var service = GetService(timer.ServiceName); var info = await _tvDtoService.GetSeriesTimerInfo(timer, true, this, cancellationToken).ConfigureAwait(false); @@ -2653,33 +2796,28 @@ namespace MediaBrowser.Server.Implementations.LiveTv } } - public Task<MBRegistrationRecord> GetRegistrationInfo(string channelId, string programId, string feature) + public Task<MBRegistrationRecord> GetRegistrationInfo(string feature) { - ILiveTvService service; - - if (string.IsNullOrWhiteSpace(programId)) - { - var channel = GetInternalChannel(channelId); - service = GetService(channel); - } - else + if (string.Equals(feature, "seriesrecordings", StringComparison.OrdinalIgnoreCase)) { - var program = GetInternalProgram(programId); - service = GetService(program); + feature = "embytvseriesrecordings"; } - var hasRegistration = service as IHasRegistrationInfo; - - if (hasRegistration != null) + if (string.Equals(feature, "dvr", StringComparison.OrdinalIgnoreCase)) { - return hasRegistration.GetRegistrationInfo(feature); + var config = GetConfiguration(); + if (config.TunerHosts.Count(i => i.IsEnabled) > 0 && + config.ListingProviders.Count(i => (i.EnableAllTuners || i.EnabledTuners.Length > 0) && string.Equals(i.Type, SchedulesDirect.TypeName, StringComparison.OrdinalIgnoreCase)) > 0) + { + return Task.FromResult(new MBRegistrationRecord + { + IsRegistered = true, + IsValid = true + }); + } } - return Task.FromResult(new MBRegistrationRecord - { - IsValid = true, - IsRegistered = true - }); + return _security.GetRegistrationStatus(feature); } public List<NameValuePair> GetSatIniMappings() diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs index ffe95c862..8095a6989 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; @@ -70,7 +71,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts } else if (!string.IsNullOrWhiteSpace(extInf) && !line.StartsWith("#", StringComparison.OrdinalIgnoreCase)) { - var channel = GetChannelnfo(extInf, tunerHostId); + var channel = GetChannelnfo(extInf, tunerHostId, line); channel.Id = channelIdPrefix + urlHash + line.GetMD5().ToString("N"); channel.Path = line; channels.Add(channel); @@ -79,7 +80,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts } return channels; } - private M3UChannel GetChannelnfo(string extInf, string tunerHostId) + private M3UChannel GetChannelnfo(string extInf, string tunerHostId, string mediaUrl) { var titleIndex = extInf.LastIndexOf(','); var channel = new M3UChannel(); @@ -87,8 +88,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts 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; @@ -101,6 +100,17 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts channel.Name = channel.Name.Substring(numberIndex + 1); } } + + if (string.Equals(channel.Number, "-1", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrWhiteSpace(mediaUrl)) + { + channel.Number = Path.GetFileNameWithoutExtension(mediaUrl.Split('/').Last()); + } + + if (string.Equals(channel.Number, "-1", StringComparison.OrdinalIgnoreCase)) + { + channel.Number = "0"; + } + channel.ImageUrl = FindProperty("tvg-logo", extInf, null); channel.Number = FindProperty("tvg-id", extInf, channel.Number); channel.Number = FindProperty("channel-id", extInf, channel.Number); diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/ReportBlock.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/ReportBlock.cs new file mode 100644 index 000000000..dddd77179 --- /dev/null +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/ReportBlock.cs @@ -0,0 +1,79 @@ +/* + Copyright (C) <2007-2016> <Kay Diefenthal> + + SatIp.RtspSample is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + SatIp.RtspSample is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with SatIp.RtspSample. If not, see <http://www.gnu.org/licenses/>. +*/ + +namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp.Rtcp +{ + public class ReportBlock + { + /// <summary> + /// Get the length of the block. + /// </summary> + public int BlockLength { get { return (24); } } + /// <summary> + /// Get the synchronization source. + /// </summary> + public string SynchronizationSource { get; private set; } + /// <summary> + /// Get the fraction lost. + /// </summary> + public int FractionLost { get; private set; } + /// <summary> + /// Get the cumulative packets lost. + /// </summary> + public int CumulativePacketsLost { get; private set; } + /// <summary> + /// Get the highest number received. + /// </summary> + public int HighestNumberReceived { get; private set; } + /// <summary> + /// Get the inter arrival jitter. + /// </summary> + public int InterArrivalJitter { get; private set; } + /// <summary> + /// Get the timestamp of the last report. + /// </summary> + public int LastReportTimeStamp { get; private set; } + /// <summary> + /// Get the delay since the last report. + /// </summary> + public int DelaySinceLastReport { get; private set; } + + /// <summary> + /// Initialize a new instance of the ReportBlock class. + /// </summary> + public ReportBlock() { } + + /// <summary> + /// Unpack the data in a packet. + /// </summary> + /// <param name="buffer">The buffer containing the packet.</param> + /// <param name="offset">The offset to the first byte of the packet within the buffer.</param> + /// <returns>An ErrorSpec instance if an error occurs; null otherwise.</returns> + public void Process(byte[] buffer, int offset) + { + SynchronizationSource = Utils.ConvertBytesToString(buffer, offset, 4); + FractionLost = buffer[offset + 4]; + CumulativePacketsLost = Utils.Convert3BytesToInt(buffer, offset + 5); + HighestNumberReceived = Utils.Convert4BytesToInt(buffer, offset + 8); + InterArrivalJitter = Utils.Convert4BytesToInt(buffer, offset + 12); + LastReportTimeStamp = Utils.Convert4BytesToInt(buffer, offset + 16); + DelaySinceLastReport = Utils.Convert4BytesToInt(buffer, offset + 20); + + + } + } +} diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpAppPacket.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpAppPacket.cs new file mode 100644 index 000000000..990b6dd94 --- /dev/null +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpAppPacket.cs @@ -0,0 +1,68 @@ +/* + Copyright (C) <2007-2016> <Kay Diefenthal> + + SatIp.RtspSample is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + SatIp.RtspSample is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with SatIp.RtspSample. If not, see <http://www.gnu.org/licenses/>. +*/ +using System.Text; + +namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp.Rtcp +{ + class RtcpAppPacket : RtcpPacket + { + /// <summary> + /// Get the synchronization source. + /// </summary> + public int SynchronizationSource { get; private set; } + /// <summary> + /// Get the name. + /// </summary> + public string Name { get; private set; } + /// <summary> + /// Get the identity. + /// </summary> + public int Identity { get; private set; } + /// <summary> + /// Get the variable data portion. + /// </summary> + public string Data { get; private set; } + + public override void Parse(byte[] buffer, int offset) + { + base.Parse(buffer, offset); + SynchronizationSource = Utils.Convert4BytesToInt(buffer, offset + 4); + Name = Utils.ConvertBytesToString(buffer, offset + 8, 4); + Identity = Utils.Convert2BytesToInt(buffer, offset + 12); + + int dataLength = Utils.Convert2BytesToInt(buffer, offset + 14); + if (dataLength != 0) + Data = Utils.ConvertBytesToString(buffer, offset + 16, dataLength); + } + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.AppendFormat("Application Specific.\n"); + sb.AppendFormat("Version : {0} .\n", Version); + sb.AppendFormat("Padding : {0} .\n", Padding); + sb.AppendFormat("Report Count : {0} .\n", ReportCount); + sb.AppendFormat("PacketType: {0} .\n", Type); + sb.AppendFormat("Length : {0} .\n", Length); + sb.AppendFormat("SynchronizationSource : {0} .\n", SynchronizationSource); + sb.AppendFormat("Name : {0} .\n", Name); + sb.AppendFormat("Identity : {0} .\n", Identity); + sb.AppendFormat("Data : {0} .\n", Data); + sb.AppendFormat(".\n"); + return sb.ToString(); + } + } +} diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpByePacket.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpByePacket.cs new file mode 100644 index 000000000..c79ea31a8 --- /dev/null +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpByePacket.cs @@ -0,0 +1,59 @@ +using System.Collections.ObjectModel; +/* + Copyright (C) <2007-2016> <Kay Diefenthal> + + SatIp.RtspSample is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + SatIp.RtspSample is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with SatIp.RtspSample. If not, see <http://www.gnu.org/licenses/>. +*/ +using System.Text; + +namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp.Rtcp +{ + public class RtcpByePacket :RtcpPacket + { + public Collection<string> SynchronizationSources { get; private set; } + public string ReasonForLeaving { get; private set; } + public override void Parse(byte[] buffer, int offset) + { + base.Parse(buffer, offset); + SynchronizationSources = new Collection<string>(); + int index = 4; + + while (SynchronizationSources.Count < ReportCount) + { + SynchronizationSources.Add(Utils.ConvertBytesToString(buffer, offset + index, 4)); + index += 4; + } + + if (index < Length) + { + int reasonLength = buffer[offset + index]; + ReasonForLeaving = Utils.ConvertBytesToString(buffer, offset + index + 1, reasonLength); + } + } + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.AppendFormat("ByeBye .\n"); + sb.AppendFormat("Version : {0} .\n", Version); + sb.AppendFormat("Padding : {0} .\n", Padding); + sb.AppendFormat("Report Count : {0} .\n", ReportCount); + sb.AppendFormat("PacketType: {0} .\n", Type); + sb.AppendFormat("Length : {0} .\n", Length); + sb.AppendFormat("SynchronizationSources : {0} .\n", SynchronizationSources); + sb.AppendFormat("ReasonForLeaving : {0} .\n", ReasonForLeaving); + sb.AppendFormat(".\n"); + return sb.ToString(); + } + } +} diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpListener.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpListener.cs new file mode 100644 index 000000000..2c54f0665 --- /dev/null +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpListener.cs @@ -0,0 +1,203 @@ +/* + Copyright (C) <2007-2016> <Kay Diefenthal> + + SatIp.RtspSample is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + SatIp.RtspSample is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with SatIp.RtspSample. If not, see <http://www.gnu.org/licenses/>. +*/ +using System; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using MediaBrowser.Model.Logging; + +namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp.Rtcp +{ + public class RtcpListener + { + private readonly ILogger _logger; + private Thread _rtcpListenerThread; + private AutoResetEvent _rtcpListenerThreadStopEvent = null; + private UdpClient _udpClient; + private IPEndPoint _multicastEndPoint; + private IPEndPoint _serverEndPoint; + private TransmissionMode _transmissionMode; + + public RtcpListener(String address, int port, TransmissionMode mode,ILogger logger) + { + _logger = logger; + _transmissionMode = mode; + switch (mode) + { + case TransmissionMode.Unicast: + _udpClient = new UdpClient(new IPEndPoint(IPAddress.Parse(address), port)); + _serverEndPoint = new IPEndPoint(IPAddress.Any, 0); + break; + case TransmissionMode.Multicast: + _multicastEndPoint = new IPEndPoint(IPAddress.Parse(address), port); + _serverEndPoint = new IPEndPoint(IPAddress.Any, 0); + _udpClient = new UdpClient(); + _udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 1); + _udpClient.ExclusiveAddressUse = false; + _udpClient.Client.Bind(new IPEndPoint(IPAddress.Any, port)); + _udpClient.JoinMulticastGroup(_multicastEndPoint.Address); + break; + } + //StartRtcpListenerThread(); + } + + public void StartRtcpListenerThread() + { + // Kill the existing thread if it is in "zombie" state. + if (_rtcpListenerThread != null && !_rtcpListenerThread.IsAlive) + { + StopRtcpListenerThread(); + } + + if (_rtcpListenerThread == null) + { + _logger.Info("SAT>IP : starting new RTCP listener thread"); + _rtcpListenerThreadStopEvent = new AutoResetEvent(false); + _rtcpListenerThread = new Thread(new ThreadStart(RtcpListenerThread)); + _rtcpListenerThread.Name = string.Format("SAT>IP tuner RTCP listener"); + _rtcpListenerThread.IsBackground = true; + _rtcpListenerThread.Priority = ThreadPriority.Lowest; + _rtcpListenerThread.Start(); + } + } + + public void StopRtcpListenerThread() + { + if (_rtcpListenerThread != null) + { + if (!_rtcpListenerThread.IsAlive) + { + _logger.Info("SAT>IP : aborting old RTCP listener thread"); + _rtcpListenerThread.Abort(); + } + else + { + _rtcpListenerThreadStopEvent.Set(); + if (!_rtcpListenerThread.Join(400 * 2)) + { + _logger.Info("SAT>IP : failed to join RTCP listener thread, aborting thread"); + _rtcpListenerThread.Abort(); + } + } + _rtcpListenerThread = null; + if (_rtcpListenerThreadStopEvent != null) + { + _rtcpListenerThreadStopEvent.Close(); + _rtcpListenerThreadStopEvent = null; + } + } + } + + private void RtcpListenerThread() + { + try + { + bool receivedGoodBye = false; + try + { + _udpClient.Client.ReceiveTimeout = 400; + IPEndPoint serverEndPoint = new IPEndPoint(IPAddress.Any, 0); + while (!receivedGoodBye && !_rtcpListenerThreadStopEvent.WaitOne(1)) + { + byte[] packets = _udpClient.Receive(ref serverEndPoint); + if (packets == null) + { + continue; + } + + int offset = 0; + while (offset < packets.Length) + { + switch (packets[offset + 1]) + { + case 200: //sr + var sr = new RtcpSenderReportPacket(); + sr.Parse(packets, offset); + offset += sr.Length; + break; + case 201: //rr + var rr = new RtcpReceiverReportPacket(); + rr.Parse(packets, offset); + offset += rr.Length; + break; + case 202: //sd + var sd = new RtcpSourceDescriptionPacket(); + sd.Parse(packets, offset); + offset += sd.Length; + break; + case 203: // bye + var bye = new RtcpByePacket(); + bye.Parse(packets, offset); + receivedGoodBye = true; + OnPacketReceived(new RtcpPacketReceivedArgs(bye)); + offset += bye.Length; + break; + case 204: // app + var app = new RtcpAppPacket(); + app.Parse(packets, offset); + OnPacketReceived(new RtcpPacketReceivedArgs(app)); + offset += app.Length; + break; + } + } + + } + } + finally + { + switch (_transmissionMode) + { + case TransmissionMode.Multicast: + _udpClient.DropMulticastGroup(_multicastEndPoint.Address); + _udpClient.Close(); + break; + case TransmissionMode.Unicast: + _udpClient.Close(); + break; + } + } + } + catch (ThreadAbortException) + { + } + catch (Exception ex) + { + _logger.Info(string.Format("SAT>IP : RTCP listener thread exception"), ex); + return; + } + _logger.Info("SAT>IP : RTCP listener thread stopping"); + } + public delegate void PacketReceivedHandler(object sender, RtcpPacketReceivedArgs e); + public event PacketReceivedHandler PacketReceived; + public class RtcpPacketReceivedArgs : EventArgs + { + public Object Packet { get; private set; } + + public RtcpPacketReceivedArgs(Object packet) + { + Packet = packet; + } + } + protected void OnPacketReceived(RtcpPacketReceivedArgs args) + { + if (PacketReceived != null) + { + PacketReceived(this, args); + } + } + } +} diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpPacket.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpPacket.cs new file mode 100644 index 000000000..0a949eb7e --- /dev/null +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpPacket.cs @@ -0,0 +1,37 @@ +/* + Copyright (C) <2007-2016> <Kay Diefenthal> + + SatIp.RtspSample is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + SatIp.RtspSample is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with SatIp.RtspSample. If not, see <http://www.gnu.org/licenses/>. +*/ + +namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp.Rtcp +{ + public abstract class RtcpPacket + { + public int Version { get; private set; } + public bool Padding { get; private set; } + public int ReportCount { get; private set; } + public int Type { get; private set; } + public int Length { get; private set; } + + public virtual void Parse(byte[] buffer, int offset) + { + Version = buffer[offset] >> 6; + Padding = (buffer[offset] & 0x20) != 0; + ReportCount = buffer[offset] & 0x1f; + Type = buffer[offset + 1]; + Length = (Utils.Convert2BytesToInt(buffer, offset + 2) * 4) + 4; + } + } +} diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpReceiverReportPacket.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpReceiverReportPacket.cs new file mode 100644 index 000000000..abb863652 --- /dev/null +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpReceiverReportPacket.cs @@ -0,0 +1,68 @@ +using System.Collections.ObjectModel; +/* + Copyright (C) <2007-2016> <Kay Diefenthal> + + SatIp.RtspSample is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + SatIp.RtspSample is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with SatIp.RtspSample. If not, see <http://www.gnu.org/licenses/>. +*/ +using System.Text; + +namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp.Rtcp +{ + public class RtcpReceiverReportPacket :RtcpPacket + { + public string SynchronizationSource { get; private set; } + public Collection<ReportBlock> ReportBlocks { get; private set; } + public byte[] ProfileExtension { get; private set; } + public override void Parse(byte[] buffer, int offset) + { + base.Parse(buffer, offset); + SynchronizationSource = Utils.ConvertBytesToString(buffer, offset + 4, 4); + + ReportBlocks = new Collection<ReportBlock>(); + int index = 8; + + while (ReportBlocks.Count < ReportCount) + { + ReportBlock reportBlock = new ReportBlock(); + reportBlock.Process(buffer, offset + index); + ReportBlocks.Add(reportBlock); + index += reportBlock.BlockLength; + } + + if (index < Length) + { + ProfileExtension = new byte[Length - index]; + + for (int extensionIndex = 0; index < Length; index++) + { + ProfileExtension[extensionIndex] = buffer[offset + index]; + extensionIndex++; + } + } + } + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.AppendFormat("Receiver Report.\n"); + sb.AppendFormat("Version : {0} .\n", Version); + sb.AppendFormat("Padding : {0} .\n", Padding); + sb.AppendFormat("Report Count : {0} .\n", ReportCount); + sb.AppendFormat("PacketType: {0} .\n", Type); + sb.AppendFormat("Length : {0} .\n", Length); + sb.AppendFormat("SynchronizationSource : {0} .\n", SynchronizationSource); + sb.AppendFormat(".\n"); + return sb.ToString(); + } + } +} diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpSenderReportPacket.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpSenderReportPacket.cs new file mode 100644 index 000000000..dda5d6a03 --- /dev/null +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpSenderReportPacket.cs @@ -0,0 +1,105 @@ +using System.Collections.ObjectModel; +/* + Copyright (C) <2007-2016> <Kay Diefenthal> + + SatIp.RtspSample is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + SatIp.RtspSample is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with SatIp.RtspSample. If not, see <http://www.gnu.org/licenses/>. +*/ +using System.Text; + +namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp.Rtcp +{ + public class RtcpSenderReportPacket : RtcpPacket + { + #region Properties + /// <summary> + /// Get the synchronization source. + /// </summary> + public int SynchronizationSource { get; private set; } + /// <summary> + /// Get the NPT timestamp. + /// </summary> + public long NPTTimeStamp { get; private set; } + /// <summary> + /// Get the RTP timestamp. + /// </summary> + public int RTPTimeStamp { get; private set; } + /// <summary> + /// Get the packet count. + /// </summary> + public int SenderPacketCount { get; private set; } + /// <summary> + /// Get the octet count. + /// </summary> + public int SenderOctetCount { get; private set; } + /// <summary> + /// Get the list of report blocks. + /// </summary> + public Collection<ReportBlock> ReportBlocks { get; private set; } + /// <summary> + /// Get the profile extension data. + /// </summary> + public byte[] ProfileExtension { get; private set; } + #endregion + + public override void Parse(byte[] buffer, int offset) + { + base.Parse(buffer, offset); + SynchronizationSource = Utils.Convert4BytesToInt(buffer, offset + 4); + NPTTimeStamp = Utils.Convert8BytesToLong(buffer, offset + 8); + RTPTimeStamp = Utils.Convert4BytesToInt(buffer, offset + 16); + SenderPacketCount = Utils.Convert4BytesToInt(buffer, offset + 20); + SenderOctetCount = Utils.Convert4BytesToInt(buffer, offset + 24); + + ReportBlocks = new Collection<ReportBlock>(); + int index = 28; + + while (ReportBlocks.Count < ReportCount) + { + ReportBlock reportBlock = new ReportBlock(); + reportBlock.Process(buffer, offset + index); + ReportBlocks.Add(reportBlock); + index += reportBlock.BlockLength; + } + + if (index < Length) + { + ProfileExtension = new byte[Length - index]; + + for (int extensionIndex = 0; index < Length; index++) + { + ProfileExtension[extensionIndex] = buffer[offset + index]; + extensionIndex++; + } + } + } + + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.AppendFormat("Sender Report.\n"); + sb.AppendFormat("Version : {0} .\n", Version); + sb.AppendFormat("Padding : {0} .\n", Padding); + sb.AppendFormat("Report Count : {0} .\n", ReportCount); + sb.AppendFormat("PacketType: {0} .\n", Type); + sb.AppendFormat("Length : {0} .\n", Length); + sb.AppendFormat("SynchronizationSource : {0} .\n", SynchronizationSource); + sb.AppendFormat("NTP Timestamp : {0} .\n", Utils.NptTimestampToDateTime(NPTTimeStamp)); + sb.AppendFormat("RTP Timestamp : {0} .\n", RTPTimeStamp); + sb.AppendFormat("Sender PacketCount : {0} .\n", SenderPacketCount); + sb.AppendFormat("Sender Octet Count : {0} .\n", SenderOctetCount); + sb.AppendFormat(".\n"); + return sb.ToString(); + } + } +} diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpSourceDescriptionPacket.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpSourceDescriptionPacket.cs new file mode 100644 index 000000000..0a95a4413 --- /dev/null +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpSourceDescriptionPacket.cs @@ -0,0 +1,57 @@ +using System.Collections.ObjectModel; +/* + Copyright (C) <2007-2016> <Kay Diefenthal> + + SatIp.RtspSample is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + SatIp.RtspSample is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with SatIp.RtspSample. If not, see <http://www.gnu.org/licenses/>. +*/ +using System.Text; + +namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp.Rtcp +{ + class RtcpSourceDescriptionPacket :RtcpPacket + { /// <summary> + /// Get the list of source descriptions. + /// </summary> + public Collection<SourceDescriptionBlock> Descriptions; + public override void Parse(byte[] buffer, int offset) + { + base.Parse(buffer, offset); + Descriptions = new Collection<SourceDescriptionBlock>(); + + int index = 4; + + while (Descriptions.Count < ReportCount) + { + SourceDescriptionBlock descriptionBlock = new SourceDescriptionBlock(); + descriptionBlock.Process(buffer, offset + index); + Descriptions.Add(descriptionBlock); + index += descriptionBlock.BlockLength; + } + } + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.AppendFormat("Source Description.\n"); + sb.AppendFormat("Version : {0} .\n", Version); + sb.AppendFormat("Padding : {0} .\n", Padding); + sb.AppendFormat("Report Count : {0} .\n", ReportCount); + sb.AppendFormat("PacketType: {0} .\n", Type); + sb.AppendFormat("Length : {0} .\n", Length); + sb.AppendFormat("Descriptions : {0} .\n", Descriptions); + + sb.AppendFormat(".\n"); + return sb.ToString(); + } + } +} diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/SourceDescriptionBlock.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/SourceDescriptionBlock.cs new file mode 100644 index 000000000..bf56087cd --- /dev/null +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/SourceDescriptionBlock.cs @@ -0,0 +1,65 @@ +/* + Copyright (C) <2007-2016> <Kay Diefenthal> + + SatIp.RtspSample is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + SatIp.RtspSample is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with SatIp.RtspSample. If not, see <http://www.gnu.org/licenses/>. +*/ +using System.Collections.ObjectModel; + +namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp.Rtcp +{ + class SourceDescriptionBlock + { + /// <summary> + /// Get the length of the block. + /// </summary> + public int BlockLength { get { return (blockLength + (blockLength % 4)); } } + + /// <summary> + /// Get the synchronization source. + /// </summary> + public string SynchronizationSource { get; private set; } + /// <summary> + /// Get the list of source descriptioni items. + /// </summary> + public Collection<SourceDescriptionItem> Items; + + private int blockLength; + + public void Process(byte[] buffer, int offset) + { + SynchronizationSource = Utils.ConvertBytesToString(buffer, offset, 4); + Items = new Collection<SourceDescriptionItem>(); + int index = 4; + bool done = false; + do + { + SourceDescriptionItem item = new SourceDescriptionItem(); + item.Process(buffer, offset + index); + + if (item.Type != 0) + { + Items.Add(item); + index += item.ItemLength; + blockLength += item.ItemLength; + } + else + { + blockLength++; + done = true; + } + } + while (!done); + } + } +} diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/SourceDescriptionItem.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/SourceDescriptionItem.cs new file mode 100644 index 000000000..5dd033642 --- /dev/null +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/SourceDescriptionItem.cs @@ -0,0 +1,60 @@ +/* + Copyright (C) <2007-2016> <Kay Diefenthal> + + SatIp.RtspSample is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + SatIp.RtspSample is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with SatIp.RtspSample. If not, see <http://www.gnu.org/licenses/>. +*/ + +namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp.Rtcp +{ + /// <summary> + /// The class that describes a source description item. + /// </summary> + public class SourceDescriptionItem + { + /// <summary> + /// Get the type. + /// </summary> + public int Type { get; private set; } + /// <summary> + /// Get the text. + /// </summary> + public string Text { get; private set; } + + /// <summary> + /// Get the length of the item. + /// </summary> + public int ItemLength { get { return (Text.Length + 2); } } + + /// <summary> + /// Initialize a new instance of the SourceDescriptionItem class. + /// </summary> + public SourceDescriptionItem() { } + + /// <summary> + /// Unpack the data in a packet. + /// </summary> + /// <param name="buffer">The buffer containing the packet.</param> + /// <param name="offset">The offset to the first byte of the packet within the buffer.</param> + /// <returns>An ErrorSpec instance if an error occurs; null otherwise.</returns> + public void Process(byte[] buffer, int offset) + { + Type = buffer[offset]; + if (Type != 0) + { + int length = buffer[offset + 1]; + Text = Utils.ConvertBytesToString(buffer, offset + 2, length); + } + } + } +} diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtp/RtpListener.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtp/RtpListener.cs new file mode 100644 index 000000000..ea6a9ba6a --- /dev/null +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtp/RtpListener.cs @@ -0,0 +1,160 @@ +/* + Copyright (C) <2007-2016> <Kay Diefenthal> + + SatIp.RtspSample is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + SatIp.RtspSample is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with SatIp.RtspSample. If not, see <http://www.gnu.org/licenses/>. +*/ +using System; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using MediaBrowser.Model.Logging; + +namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp.Rtp +{ + public class RtpListener + { + private readonly ILogger _logger; + private AutoResetEvent _rtpListenerThreadStopEvent; + private Thread _rtpListenerThread; + private UdpClient _udpClient; + private IPEndPoint _multicastEndPoint; + private IPEndPoint _serverEndPoint; + private TransmissionMode _transmissionMode; + public RtpListener(String address, int port,TransmissionMode mode,ILogger logger) + { + _logger = logger; + _transmissionMode = mode; + switch (mode) + { + case TransmissionMode.Unicast: + _udpClient = new UdpClient(new IPEndPoint(IPAddress.Parse(address), port)); + _serverEndPoint = new IPEndPoint(IPAddress.Any, 0); + break; + case TransmissionMode.Multicast: + _multicastEndPoint = new IPEndPoint(IPAddress.Parse(address), port); + _serverEndPoint = null; + _udpClient = new UdpClient(); + _udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 1); + _udpClient.ExclusiveAddressUse = false; + _udpClient.Client.Bind(new IPEndPoint(IPAddress.Any, _multicastEndPoint.Port)); + _udpClient.JoinMulticastGroup(_multicastEndPoint.Address); + break; + } + //StartRtpListenerThread(); + } + public void StartRtpListenerThread() + { + // Kill the existing thread if it is in "zombie" state. + if (_rtpListenerThread != null && !_rtpListenerThread.IsAlive) + { + StopRtpListenerThread(); + } + + if (_rtpListenerThread == null) + { + _logger.Info("SAT>IP : starting new RTP listener thread"); + _rtpListenerThreadStopEvent = new AutoResetEvent(false); + _rtpListenerThread = new Thread(new ThreadStart(RtpListenerThread)); + _rtpListenerThread.Name = string.Format("SAT>IP tuner RTP listener"); + _rtpListenerThread.IsBackground = true; + _rtpListenerThread.Priority = ThreadPriority.Lowest; + _rtpListenerThread.Start(); + } + } + + public void StopRtpListenerThread() + { + if (_rtpListenerThread != null) + { + if (!_rtpListenerThread.IsAlive) + { + _logger.Info("SAT>IP : aborting old RTP listener thread"); + _rtpListenerThread.Abort(); + } + else + { + _rtpListenerThreadStopEvent.Set(); + if (!_rtpListenerThread.Join(400 * 2)) + { + _logger.Info("SAT>IP : failed to join RTP listener thread, aborting thread"); + _rtpListenerThread.Abort(); + } + } + _rtpListenerThread = null; + if (_rtpListenerThreadStopEvent != null) + { + _rtpListenerThreadStopEvent.Close(); + _rtpListenerThreadStopEvent = null; + } + } + } + + private void RtpListenerThread() + { + try + { + try + { + + while (!_rtpListenerThreadStopEvent.WaitOne(1)) + { + byte[] receivedbytes = _udpClient.Receive(ref _serverEndPoint); + RtpPacket packet = RtpPacket.Decode(receivedbytes); + OnPacketReceived(new RtpPacketReceivedArgs(packet)); + } + } + finally + { + switch (_transmissionMode) + { + case TransmissionMode.Multicast: + _udpClient.DropMulticastGroup(_multicastEndPoint.Address); + _udpClient.Close(); + break; + case TransmissionMode.Unicast: + _udpClient.Close(); + break; + } + } + } + catch (ThreadAbortException) + { + } + catch (Exception ex) + { + _logger.Info(string.Format("SAT>IP : RTP listener thread exception"), ex); + return; + } + _logger.Info("SAT>IP : RTP listener thread stopping"); + } + public delegate void PacketReceivedHandler(object sender, RtpPacketReceivedArgs e); + public event PacketReceivedHandler PacketReceived; + public class RtpPacketReceivedArgs : EventArgs + { + public RtpPacket Packet { get; private set; } + + public RtpPacketReceivedArgs(RtpPacket packet) + { + Packet = packet; + } + } + protected void OnPacketReceived(RtpPacketReceivedArgs args) + { + if (PacketReceived != null) + { + PacketReceived(this, args); + } + } + } +} diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtp/RtpPacket.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtp/RtpPacket.cs new file mode 100644 index 000000000..489d7f087 --- /dev/null +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtp/RtpPacket.cs @@ -0,0 +1,116 @@ +/* + Copyright (C) <2007-2016> <Kay Diefenthal> + + SatIp.RtspSample is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + SatIp.RtspSample is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with SatIp.RtspSample. If not, see <http://www.gnu.org/licenses/>. +*/ + +using System; +using System.Collections.ObjectModel; +using System.Text; + +namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp.Rtp +{ + public class RtpPacket + { + private static int MinHeaderLength = 12; + public int HeaderSize = MinHeaderLength; + public int Version { get; set; } + public Boolean Padding { get; set; } + public Boolean Extension { get; set; } + public int ContributingSourceCount { get; set; } + public Boolean Marker { get; set; } + public int PayloadType { get; set; } + public int SequenceNumber { get; set; } + public long TimeStamp { get; set; } + public long SynchronizationSource { get; set; } + public Collection<string> ContributingSources { get; private set; } + public int ExtensionHeaderId = 0; + public int ExtensionHeaderLength = 0; + public bool HasPayload { get; set; } + public byte[] Payload { get; set; } + public RtpPacket() + { + + } + public static RtpPacket Decode(byte[] buffer) + { + var packet = new RtpPacket(); + packet.Version = buffer[0] >> 6; + packet.Padding = (buffer[0] & 0x20) != 0; + packet.Extension = (buffer[0] & 0x10) != 0; + packet.ContributingSourceCount = buffer[0] & 0x0f; + + packet.Marker = (buffer[1] & 0x80) != 0; + packet.PayloadType = buffer[1] & 0x7f; + + packet.SequenceNumber = Utils.Convert2BytesToInt(buffer, 2); + packet.TimeStamp = Utils.Convert4BytesToLong(buffer, 4); + packet.SynchronizationSource = Utils.Convert4BytesToLong(buffer, 8); + + int index = 12; + + if (packet.ContributingSourceCount != 0) + { + packet.ContributingSources = new Collection<string>(); + + while (packet.ContributingSources.Count < packet.ContributingSourceCount) + { + packet.ContributingSources.Add(Utils.ConvertBytesToString(buffer, index, 4)); + index += 4; + } + } + var dataoffset = 0; + if (!packet.Extension) + dataoffset = index; + else + { + packet.ExtensionHeaderId = Utils.Convert2BytesToInt(buffer, index); + packet.ExtensionHeaderLength = Utils.Convert2BytesToInt(buffer, index + 2); + dataoffset = index + packet.ExtensionHeaderLength + 4; + } + + var dataLength = buffer.Length - dataoffset; + if (dataLength > dataoffset) + { + packet.HasPayload = true; + packet.Payload = new byte[dataLength]; + Array.Copy(buffer, dataoffset, packet.Payload, 0, dataLength); + } + else + { + packet.HasPayload = false; + } + return packet; + } + + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.AppendFormat("RTP Packet"); + sb.AppendFormat("Version: {0} \n", Version); + sb.AppendFormat("Padding: {0} \n", Padding); + sb.AppendFormat("Extension: {0} \n", Extension); + sb.AppendFormat("Contributing Source Identifiers Count: {0} \n", ContributingSourceCount); + sb.AppendFormat("Marker: {0} \n", Marker); + sb.AppendFormat("Payload Type: {0} \n", PayloadType); + sb.AppendFormat("Sequence Number: {0} \n", SequenceNumber); + sb.AppendFormat("Timestamp: {0} .\n", TimeStamp); + sb.AppendFormat("Synchronization Source Identifier: {0} \n", SynchronizationSource); + sb.AppendFormat("\n"); + return sb.ToString(); + } + + } + +} diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpDiscovery.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpDiscovery.cs index ba5c604ed..cb0e573da 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpDiscovery.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpDiscovery.cs @@ -237,7 +237,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp string modelurl = ""; string serialnumber = ""; string presentationurl = ""; - string capabilities = ""; + //string capabilities = ""; string m3u = ""; var document = XDocument.Load(locationUri.AbsoluteUri); var xnm = new XmlNamespaceManager(new NameTable()); @@ -284,22 +284,22 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp var capabilitiesElement = deviceElement.Element(n1 + "X_SATIPCAP"); if (capabilitiesElement != null) { - //_capabilities = capabilitiesElement.Value; - //if (capabilitiesElement.Value.Contains(',')) - //{ - // string[] capabilities = capabilitiesElement.Value.Split(','); - // foreach (var capability in capabilities) - // { - // ReadCapability(capability); - // } - //} - //else - //{ - // ReadCapability(capabilitiesElement.Value); - //} + //_capabilities = capabilitiesElement.Value; + if (capabilitiesElement.Value.Contains(',')) + { + string[] capabilities = capabilitiesElement.Value.Split(','); + foreach (var capability in capabilities) + { + ReadCapability(capability); + } } else { + ReadCapability(capabilitiesElement.Value); + } + } + else + { _supportsDVBS = true; _tunerCountDVBS =1; } @@ -314,8 +314,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp Id = uniquedevicename, IsEnabled = true, Type = SatIpHost.DeviceType, - Tuners = 1, - TunersAvailable = 1, + Tuners = _tunerCountDVBS, + TunersAvailable = _tunerCountDVBS, M3UUrl = m3u }; diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/TransmissionMode.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/TransmissionMode.cs new file mode 100644 index 000000000..71d7656d9 --- /dev/null +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/TransmissionMode.cs @@ -0,0 +1,25 @@ +/* + Copyright (C) <2007-2016> <Kay Diefenthal> + + SatIp.RtspSample is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + SatIp.RtspSample is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with SatIp.RtspSample. If not, see <http://www.gnu.org/licenses/>. +*/ + +namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp +{ + public enum TransmissionMode + { + Unicast, + Multicast + } +} diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Utils.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Utils.cs new file mode 100644 index 000000000..3595e4b0a --- /dev/null +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Utils.cs @@ -0,0 +1,90 @@ +/* + Copyright (C) <2007-2016> <Kay Diefenthal> + + SatIp.RtspSample is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + SatIp.RtspSample is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with SatIp.RtspSample. If not, see <http://www.gnu.org/licenses/>. +*/ +using System; +using System.Text; + +namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp +{ + public class Utils + { + public static int Convert2BytesToInt(byte[] buffer, int offset) + { + int temp = (int)buffer[offset]; + temp = (temp * 256) + buffer[offset + 1]; + + return (temp); + } + public static int Convert3BytesToInt(byte[] buffer, int offset) + { + int temp = (int)buffer[offset]; + temp = (temp * 256) + buffer[offset + 1]; + temp = (temp * 256) + buffer[offset + 2]; + + return (temp); + } + public static int Convert4BytesToInt(byte[] buffer, int offset) + { + int temp =(int)buffer[offset]; + temp = (temp * 256) + buffer[offset + 1]; + temp = (temp * 256) + buffer[offset + 2]; + temp = (temp * 256) + buffer[offset + 3]; + + return (temp); + } + public static long Convert4BytesToLong(byte[] buffer, int offset) + { + long temp = 0; + + for (int index = 0; index < 4; index++) + temp = (temp * 256) + buffer[offset + index]; + + return (temp); + } + public static long Convert8BytesToLong(byte[] buffer, int offset) + { + long temp = 0; + + for (int index = 0; index < 8; index++) + temp = (temp * 256) + buffer[offset + index]; + + return (temp); + } + public static string ConvertBytesToString(byte[] buffer, int offset, int length) + { + StringBuilder reply = new StringBuilder(4); + for (int index = 0; index < length; index++) + reply.Append((char)buffer[offset + index]); + return (reply.ToString()); + } + public static DateTime NptTimestampToDateTime(long nptTimestamp) { return NptTimestampToDateTime((uint)((nptTimestamp >> 32) & 0xFFFFFFFF), (uint)(nptTimestamp & 0xFFFFFFFF),null); } + + public static DateTime NptTimestampToDateTime(uint seconds, uint fractions, DateTime? epoch ) + { + ulong ticks =(ulong)((seconds * TimeSpan.TicksPerSecond) + ((fractions * TimeSpan.TicksPerSecond) / 0x100000000L)); + if (epoch.HasValue) return epoch.Value + TimeSpan.FromTicks((Int64)ticks); + return (seconds & 0x80000000L) == 0 ? UtcEpoch2036 + TimeSpan.FromTicks((Int64)ticks) : UtcEpoch1900 + TimeSpan.FromTicks((Int64)ticks); + } + + //When the First Epoch will wrap (The real Y2k) + public static DateTime UtcEpoch2036 = new DateTime(2036, 2, 7, 6, 28, 16, DateTimeKind.Utc); + + public static DateTime UtcEpoch1900 = new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc); + + public static DateTime UtcEpoch1970 = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + + } +} diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index 6879c3f40..8850f3d35 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -124,7 +124,6 @@ <Compile Include="Channels\RefreshChannelsScheduledTask.cs" /> <Compile Include="Collections\CollectionManager.cs" /> <Compile Include="Collections\CollectionsDynamicFolder.cs" /> - <Compile Include="Collections\ManualCollectionsFolder.cs" /> <Compile Include="Collections\CollectionImageProvider.cs" /> <Compile Include="Configuration\ServerConfigurationManager.cs" /> <Compile Include="Connect\ConnectData.cs" /> @@ -251,6 +250,18 @@ <Compile Include="LiveTv\RecordingImageProvider.cs" /> <Compile Include="LiveTv\RefreshChannelsScheduledTask.cs" /> <Compile Include="LiveTv\TunerHosts\SatIp\ChannelScan.cs" /> + <Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\ReportBlock.cs" /> + <Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\RtcpAppPacket.cs" /> + <Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\RtcpByePacket.cs" /> + <Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\RtcpListener.cs" /> + <Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\RtcpPacket.cs" /> + <Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\RtcpReceiverReportPacket.cs" /> + <Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\RtcpSenderReportPacket.cs" /> + <Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\RtcpSourceDescriptionPacket.cs" /> + <Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\SourceDescriptionBlock.cs" /> + <Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\SourceDescriptionItem.cs" /> + <Compile Include="LiveTv\TunerHosts\SatIp\Rtp\RtpListener.cs" /> + <Compile Include="LiveTv\TunerHosts\SatIp\Rtp\RtpPacket.cs" /> <Compile Include="LiveTv\TunerHosts\SatIp\Rtsp\RtspMethod.cs" /> <Compile Include="LiveTv\TunerHosts\SatIp\Rtsp\RtspRequest.cs" /> <Compile Include="LiveTv\TunerHosts\SatIp\Rtsp\RtspResponse.cs" /> @@ -258,6 +269,8 @@ <Compile Include="LiveTv\TunerHosts\SatIp\Rtsp\RtspStatusCode.cs" /> <Compile Include="LiveTv\TunerHosts\SatIp\SatIpHost.cs" /> <Compile Include="LiveTv\TunerHosts\SatIp\SatIpDiscovery.cs" /> + <Compile Include="LiveTv\TunerHosts\SatIp\TransmissionMode.cs" /> + <Compile Include="LiveTv\TunerHosts\SatIp\Utils.cs" /> <Compile Include="Localization\LocalizationManager.cs" /> <Compile Include="Logging\PatternsLogger.cs" /> <Compile Include="MediaEncoder\EncodingManager.cs" /> diff --git a/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs b/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs index 67ddcc5cc..7d0841fa6 100644 --- a/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs +++ b/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs @@ -14,6 +14,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using CommonIO; +using MediaBrowser.Controller.Library; namespace MediaBrowser.Server.Implementations.MediaEncoder { @@ -24,16 +25,18 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder private readonly ILogger _logger; private readonly IMediaEncoder _encoder; private readonly IChapterManager _chapterManager; + private readonly ILibraryManager _libraryManager; public EncodingManager(IFileSystem fileSystem, ILogger logger, IMediaEncoder encoder, - IChapterManager chapterManager) + IChapterManager chapterManager, ILibraryManager libraryManager) { _fileSystem = fileSystem; _logger = logger; _encoder = encoder; _chapterManager = chapterManager; + _libraryManager = libraryManager; } /// <summary> @@ -57,27 +60,38 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder return false; } - var options = _chapterManager.GetConfiguration(); - - if (video is Movie) + var libraryOptions = _libraryManager.GetLibraryOptions(video); + if (libraryOptions != null && libraryOptions.SchemaVersion >= 2) { - if (!options.EnableMovieChapterImageExtraction) + if (!libraryOptions.EnableChapterImageExtraction) { return false; } } - else if (video is Episode) + else { - if (!options.EnableEpisodeChapterImageExtraction) + var options = _chapterManager.GetConfiguration(); + + if (video is Movie) { - return false; + if (!options.EnableMovieChapterImageExtraction) + { + return false; + } } - } - else - { - if (!options.EnableOtherVideoChapterImageExtraction) + else if (video is Episode) { - return false; + if (!options.EnableEpisodeChapterImageExtraction) + { + return false; + } + } + else + { + if (!options.EnableOtherVideoChapterImageExtraction) + { + return false; + } } } @@ -135,16 +149,16 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder } } - // Add some time for the first chapter to make sure we don't end up with a black image - var time = chapter.StartPositionTicks == 0 ? TimeSpan.FromTicks(Math.Min(FirstChapterTicks, video.RunTimeTicks ?? 0)) : TimeSpan.FromTicks(chapter.StartPositionTicks); + try + { + // Add some time for the first chapter to make sure we don't end up with a black image + var time = chapter.StartPositionTicks == 0 ? TimeSpan.FromTicks(Math.Min(FirstChapterTicks, video.RunTimeTicks ?? 0)) : TimeSpan.FromTicks(chapter.StartPositionTicks); - var protocol = MediaProtocol.File; + var protocol = MediaProtocol.File; - var inputPath = MediaEncoderHelpers.GetInputArgument(_fileSystem, video.Path, protocol, null, video.PlayableStreamFileNames); + var inputPath = MediaEncoderHelpers.GetInputArgument(_fileSystem, video.Path, protocol, null, video.PlayableStreamFileNames); - try - { - _fileSystem.CreateDirectory(Path.GetDirectoryName(path)); + _fileSystem.CreateDirectory(Path.GetDirectoryName(path)); var tempFile = await _encoder.ExtractVideoImage(inputPath, protocol, video.Video3DFormat, time, cancellationToken).ConfigureAwait(false); File.Copy(tempFile, path, true); @@ -164,7 +178,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder } catch (Exception ex) { - _logger.ErrorException("Error extracting chapter images for {0}", ex, string.Join(",", inputPath)); + _logger.ErrorException("Error extracting chapter images for {0}", ex, string.Join(",", video.Path)); success = false; break; } diff --git a/MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs b/MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs index 175dbbc01..f40b64498 100644 --- a/MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs +++ b/MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs @@ -23,7 +23,7 @@ namespace MediaBrowser.Server.Implementations.Sync if (supportsDca) { - mkvAudio += ",dca"; + mkvAudio += ",dca,dts"; } var videoProfile = "high|main|baseline|constrained baseline"; diff --git a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs b/MediaBrowser.Server.Implementations/Sync/SyncManager.cs index ffba60af8..1278a40a4 100644 --- a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs +++ b/MediaBrowser.Server.Implementations/Sync/SyncManager.cs @@ -541,6 +541,11 @@ namespace MediaBrowser.Server.Implementations.Sync return true; } + if (item.SourceType == SourceType.Channel) + { + return BaseItem.ChannelManager.SupportsSync(item.ChannelId); + } + return item.LocationType == LocationType.FileSystem || item is Season; } diff --git a/MediaBrowser.Server.Implementations/Udp/UdpServer.cs b/MediaBrowser.Server.Implementations/Udp/UdpServer.cs index 32992b9b2..6dd5de548 100644 --- a/MediaBrowser.Server.Implementations/Udp/UdpServer.cs +++ b/MediaBrowser.Server.Implementations/Udp/UdpServer.cs @@ -30,7 +30,7 @@ namespace MediaBrowser.Server.Implementations.Udp private bool _isDisposed; - private readonly List<Tuple<string, byte[], Action<string, Encoding>>> _responders = new List<Tuple<string, byte[], Action<string, Encoding>>>(); + private readonly List<Tuple<string, bool, Func<string, string, Encoding, Task>>> _responders = new List<Tuple<string, bool, Func<string, string, Encoding, Task>>>(); private readonly IServerApplicationHost _appHost; private readonly IJsonSerializer _json; @@ -49,43 +49,35 @@ namespace MediaBrowser.Server.Implementations.Udp _appHost = appHost; _json = json; - AddMessageResponder("who is EmbyServer?", RespondToV2Message); - AddMessageResponder("who is MediaBrowserServer_v2?", RespondToV2Message); - AddMessageResponder("who is MediaBrowserServer?", RespondToV1Message); + AddMessageResponder("who is EmbyServer?", true, RespondToV2Message); + AddMessageResponder("who is MediaBrowserServer_v2?", false, RespondToV2Message); } - private void AddMessageResponder(string message, Action<string, Encoding> responder) + private void AddMessageResponder(string message, bool isSubstring, Func<string, string, Encoding, Task> responder) { - var expectedMessageBytes = Encoding.UTF8.GetBytes(message); - - _responders.Add(new Tuple<string, byte[], Action<string, Encoding>>(message, expectedMessageBytes, responder)); + _responders.Add(new Tuple<string, bool, Func<string, string, Encoding, Task>>(message, isSubstring, responder)); } /// <summary> /// Raises the <see cref="E:MessageReceived" /> event. /// </summary> /// <param name="e">The <see cref="UdpMessageReceivedEventArgs"/> instance containing the event data.</param> - private void OnMessageReceived(UdpMessageReceivedEventArgs e) + private async void OnMessageReceived(UdpMessageReceivedEventArgs e) { - var responder = _responders.FirstOrDefault(i => i.Item2.SequenceEqual(e.Bytes)); var encoding = Encoding.UTF8; + var responder = GetResponder(e.Bytes, encoding); if (responder == null) { - var text = Encoding.Unicode.GetString(e.Bytes); - responder = _responders.FirstOrDefault(i => string.Equals(i.Item1, text, StringComparison.OrdinalIgnoreCase)); - - if (responder != null) - { - encoding = Encoding.Unicode; - } + encoding = Encoding.Unicode; + responder = GetResponder(e.Bytes, encoding); } if (responder != null) { try { - responder.Item3(e.RemoteEndPoint, encoding); + await responder.Item2.Item3(responder.Item1, e.RemoteEndPoint, encoding).ConfigureAwait(false); } catch (Exception ex) { @@ -94,33 +86,29 @@ namespace MediaBrowser.Server.Implementations.Udp } } - private async void RespondToV1Message(string endpoint, Encoding encoding) + private Tuple<string, Tuple<string, bool, Func<string, string, Encoding, Task>>> GetResponder(byte[] bytes, Encoding encoding) { - var localUrl = await _appHost.GetLocalApiUrl().ConfigureAwait(false); - - if (!string.IsNullOrEmpty(localUrl)) + var text = encoding.GetString(bytes); + var responder = _responders.FirstOrDefault(i => { - // This is how we did the old v1 search, so need to strip off the protocol - var index = localUrl.IndexOf("://", StringComparison.OrdinalIgnoreCase); - - if (index != -1) + if (i.Item2) { - localUrl = localUrl.Substring(index + 3); + return text.IndexOf(i.Item1, StringComparison.OrdinalIgnoreCase) != -1; } + return string.Equals(i.Item1, text, StringComparison.OrdinalIgnoreCase); + }); - // Send a response back with our ip address and port - var response = String.Format("MediaBrowserServer|{0}", localUrl); - - await SendAsync(Encoding.UTF8.GetBytes(response), endpoint); - } - else + if (responder == null) { - _logger.Warn("Unable to respond to udp request because the local ip address could not be determined."); + return null; } + return new Tuple<string, Tuple<string, bool, Func<string, string, Encoding, Task>>>(text, responder); } - private async void RespondToV2Message(string endpoint, Encoding encoding) + private async Task RespondToV2Message(string messageText, string endpoint, Encoding encoding) { + var parts = messageText.Split('|'); + var localUrl = await _appHost.GetLocalApiUrl().ConfigureAwait(false); if (!string.IsNullOrEmpty(localUrl)) @@ -132,7 +120,12 @@ namespace MediaBrowser.Server.Implementations.Udp Name = _appHost.FriendlyName }; - await SendAsync(encoding.GetBytes(_json.SerializeToString(response)), endpoint); + await SendAsync(encoding.GetBytes(_json.SerializeToString(response)), endpoint).ConfigureAwait(false); + + if (parts.Length > 1) + { + _appHost.EnableLoopback(parts[1]); + } } else { diff --git a/MediaBrowser.Server.Implementations/UserViews/CollectionFolderImageProvider.cs b/MediaBrowser.Server.Implementations/UserViews/CollectionFolderImageProvider.cs index 29716d33e..2cff4a14f 100644 --- a/MediaBrowser.Server.Implementations/UserViews/CollectionFolderImageProvider.cs +++ b/MediaBrowser.Server.Implementations/UserViews/CollectionFolderImageProvider.cs @@ -13,6 +13,10 @@ using System.IO; using System.Linq; using System.Threading.Tasks; using CommonIO; +using MediaBrowser.Controller.Collections; +using MediaBrowser.Controller.Entities.Movies; +using MediaBrowser.Controller.Library; +using MediaBrowser.Model.Querying; namespace MediaBrowser.Server.Implementations.UserViews { @@ -109,4 +113,62 @@ namespace MediaBrowser.Server.Implementations.UserViews return await base.CreateImage(item, itemsWithImages, outputPath, imageType, imageIndex).ConfigureAwait(false); } } + + public class ManualCollectionFolderImageProvider : BaseDynamicImageProvider<ManualCollectionsFolder> + { + private readonly ILibraryManager _libraryManager; + + public ManualCollectionFolderImageProvider(IFileSystem fileSystem, IProviderManager providerManager, IApplicationPaths applicationPaths, IImageProcessor imageProcessor, ILibraryManager libraryManager) : base(fileSystem, providerManager, applicationPaths, imageProcessor) + { + _libraryManager = libraryManager; + } + + public override IEnumerable<ImageType> GetSupportedImages(IHasImages item) + { + return new List<ImageType> + { + ImageType.Primary + }; + } + + protected override async Task<List<BaseItem>> GetItemsWithImages(IHasImages item) + { + var view = (ManualCollectionsFolder)item; + + var recursive = !new[] { CollectionType.Playlists, CollectionType.Channels }.Contains(view.CollectionType ?? string.Empty, StringComparer.OrdinalIgnoreCase); + + var items = _libraryManager.GetItemList(new InternalItemsQuery + { + Recursive = recursive, + IncludeItemTypes = new[] { typeof(BoxSet).Name }, + Limit = 20, + SortBy = new[] { ItemSortBy.Random } + }); + + return GetFinalItems(items.Where(i => i.HasImage(ImageType.Primary) || i.HasImage(ImageType.Thumb)).ToList(), 8); + } + + protected override bool Supports(IHasImages item) + { + return item is ManualCollectionsFolder; + } + + protected override async Task<string> CreateImage(IHasImages item, List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex) + { + var outputPath = Path.ChangeExtension(outputPathWithoutExtension, ".png"); + + if (imageType == ImageType.Primary) + { + if (itemsWithImages.Count == 0) + { + return null; + } + + return await CreateThumbCollage(item, itemsWithImages, outputPath, 960, 540).ConfigureAwait(false); + } + + return await base.CreateImage(item, itemsWithImages, outputPath, imageType, imageIndex).ConfigureAwait(false); + } + } + } diff --git a/MediaBrowser.Server.Implementations/UserViews/DynamicImageProvider.cs b/MediaBrowser.Server.Implementations/UserViews/DynamicImageProvider.cs index 3c75c8a48..f40072897 100644 --- a/MediaBrowser.Server.Implementations/UserViews/DynamicImageProvider.cs +++ b/MediaBrowser.Server.Implementations/UserViews/DynamicImageProvider.cs @@ -14,17 +14,20 @@ using System.IO; using System.Linq; using System.Threading.Tasks; using CommonIO; +using MediaBrowser.Controller.LiveTv; namespace MediaBrowser.Server.Implementations.UserViews { public class DynamicImageProvider : BaseDynamicImageProvider<UserView> { private readonly IUserManager _userManager; + private readonly ILibraryManager _libraryManager; - public DynamicImageProvider(IFileSystem fileSystem, IProviderManager providerManager, IApplicationPaths applicationPaths, IImageProcessor imageProcessor, IUserManager userManager) + public DynamicImageProvider(IFileSystem fileSystem, IProviderManager providerManager, IApplicationPaths applicationPaths, IImageProcessor imageProcessor, IUserManager userManager, ILibraryManager libraryManager) : base(fileSystem, providerManager, applicationPaths, imageProcessor) { _userManager = userManager; + _libraryManager = libraryManager; } public override IEnumerable<ImageType> GetSupportedImages(IHasImages item) @@ -50,7 +53,15 @@ namespace MediaBrowser.Server.Implementations.UserViews if (string.Equals(view.ViewType, CollectionType.LiveTv, StringComparison.OrdinalIgnoreCase)) { - return new List<BaseItem>(); + var programs = _libraryManager.GetItemList(new InternalItemsQuery + { + IncludeItemTypes = new[] { typeof(LiveTvProgram).Name }, + ImageTypes = new[] { ImageType.Primary }, + Limit = 30, + IsMovie = true + }).ToList(); + + return GetFinalItems(programs).ToList(); } if (string.Equals(view.ViewType, SpecialFolder.MovieGenre, StringComparison.OrdinalIgnoreCase) || @@ -147,6 +158,7 @@ namespace MediaBrowser.Server.Implementations.UserViews CollectionType.MusicVideos, CollectionType.HomeVideos, CollectionType.BoxSets, + CollectionType.LiveTv, CollectionType.Playlists, CollectionType.Photos, string.Empty diff --git a/MediaBrowser.Server.Mono/Native/BaseMonoApp.cs b/MediaBrowser.Server.Mono/Native/BaseMonoApp.cs index faf3ba37e..48f6a2a48 100644 --- a/MediaBrowser.Server.Mono/Native/BaseMonoApp.cs +++ b/MediaBrowser.Server.Mono/Native/BaseMonoApp.cs @@ -278,6 +278,16 @@ namespace MediaBrowser.Server.Mono.Native return info; } + + public void EnableLoopback(string appName) + { + + } + + public bool PortsRequireAuthorization(string applicationPath) + { + return false; + } } public class NullPowerManagement : IPowerManagement diff --git a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs index ac2422b08..433855ea0 100644 --- a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs +++ b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs @@ -325,6 +325,15 @@ namespace MediaBrowser.Server.Startup.Common await MediaEncoder.Init().ConfigureAwait(false); + if (string.IsNullOrWhiteSpace(MediaEncoder.EncoderPath)) + { + if (ServerConfigurationManager.Configuration.IsStartupWizardCompleted) + { + ServerConfigurationManager.Configuration.IsStartupWizardCompleted = false; + ServerConfigurationManager.SaveConfiguration(); + } + } + Logger.Info("ServerId: {0}", SystemId); Logger.Info("Core startup complete"); HttpServer.GlobalResponse = null; @@ -363,7 +372,10 @@ namespace MediaBrowser.Server.Startup.Common private void PerformPreInitMigrations() { - var migrations = new List<IVersionMigration>(); + var migrations = new List<IVersionMigration> + { + new UpdateLevelMigration(ServerConfigurationManager, this, HttpClient, JsonSerializer, _releaseAssetFilename) + }; foreach (var task in migrations) { @@ -382,10 +394,8 @@ namespace MediaBrowser.Server.Startup.Common { var migrations = new List<IVersionMigration> { - new OmdbEpisodeProviderMigration(ServerConfigurationManager), new MovieDbEpisodeProviderMigration(ServerConfigurationManager), - new DbMigration(ServerConfigurationManager, TaskManager), - new UpdateLevelMigration(ServerConfigurationManager, this, HttpClient, JsonSerializer, _releaseAssetFilename) + new DbMigration(ServerConfigurationManager, TaskManager) }; foreach (var task in migrations) @@ -521,7 +531,7 @@ namespace MediaBrowser.Server.Startup.Common PlaylistManager = new PlaylistManager(LibraryManager, FileSystemManager, LibraryMonitor, LogManager.GetLogger("PlaylistManager"), UserManager, ProviderManager); RegisterSingleInstance<IPlaylistManager>(PlaylistManager); - LiveTvManager = new LiveTvManager(this, ServerConfigurationManager, Logger, ItemRepository, ImageProcessor, UserDataManager, DtoService, UserManager, LibraryManager, TaskManager, LocalizationManager, JsonSerializer, ProviderManager, FileSystemManager); + LiveTvManager = new LiveTvManager(this, ServerConfigurationManager, Logger, ItemRepository, ImageProcessor, UserDataManager, DtoService, UserManager, LibraryManager, TaskManager, LocalizationManager, JsonSerializer, ProviderManager, FileSystemManager, SecurityManager); RegisterSingleInstance(LiveTvManager); UserViewManager = new UserViewManager(LibraryManager, LocalizationManager, UserManager, ChannelManager, LiveTvManager, ServerConfigurationManager); @@ -547,7 +557,7 @@ namespace MediaBrowser.Server.Startup.Common await RegisterMediaEncoder(innerProgress).ConfigureAwait(false); progress.Report(90); - EncodingManager = new EncodingManager(FileSystemManager, Logger, MediaEncoder, ChapterManager); + EncodingManager = new EncodingManager(FileSystemManager, Logger, MediaEncoder, ChapterManager, LibraryManager); RegisterSingleInstance(EncodingManager); RegisterSingleInstance(NativeApp.GetPowerManagement()); @@ -774,7 +784,19 @@ namespace MediaBrowser.Server.Startup.Common /// </summary> protected override void FindParts() { - if (!ServerConfigurationManager.Configuration.IsPortAuthorized) + var isAuthorized = ServerConfigurationManager.Configuration.IsPortAuthorized; + if (isAuthorized) + { + try + { + isAuthorized = !NativeApp.PortsRequireAuthorization(ConfigurationManager.CommonApplicationPaths.ApplicationPath); + } + catch + { + + } + } + if (!isAuthorized) { RegisterServerWithAdministratorAccess(); ServerConfigurationManager.Configuration.IsPortAuthorized = true; @@ -947,7 +969,7 @@ namespace MediaBrowser.Server.Startup.Common { if (!CanSelfRestart) { - throw new InvalidOperationException("The server is unable to self-restart. Please restart manually."); + throw new PlatformNotSupportedException("The server is unable to self-restart. Please restart manually."); } try @@ -1097,7 +1119,8 @@ namespace MediaBrowser.Server.Startup.Common LocalAddress = localAddress, SupportsLibraryMonitor = SupportsLibraryMonitor, EncoderLocationType = MediaEncoder.EncoderLocationType, - SystemArchitecture = NativeApp.Environment.SystemArchitecture + SystemArchitecture = NativeApp.Environment.SystemArchitecture, + SystemUpdateLevel = ConfigurationManager.CommonConfiguration.SystemUpdateLevel }; } @@ -1331,8 +1354,8 @@ namespace MediaBrowser.Server.Startup.Common cacheLength = TimeSpan.FromMinutes(5); } - var result = await new GithubUpdater(HttpClient, JsonSerializer, cacheLength).CheckForUpdateResult("MediaBrowser", "Emby", ApplicationVersion, updateLevel, _releaseAssetFilename, - "MBServer", "Mbserver.zip", cancellationToken).ConfigureAwait(false); + var result = await new GithubUpdater(HttpClient, JsonSerializer).CheckForUpdateResult("MediaBrowser", "Emby", ApplicationVersion, updateLevel, _releaseAssetFilename, + "MBServer", "Mbserver.zip", cacheLength, cancellationToken).ConfigureAwait(false); HasUpdateAvailable = result.IsUpdateAvailable; @@ -1393,5 +1416,10 @@ namespace MediaBrowser.Server.Startup.Common { NativeApp.LaunchUrl(url); } + + public void EnableLoopback(string appName) + { + NativeApp.EnableLoopback(appName); + } } } diff --git a/MediaBrowser.Server.Startup.Common/Browser/BrowserLauncher.cs b/MediaBrowser.Server.Startup.Common/Browser/BrowserLauncher.cs index 6b3602a73..1a0e2d973 100644 --- a/MediaBrowser.Server.Startup.Common/Browser/BrowserLauncher.cs +++ b/MediaBrowser.Server.Startup.Common/Browser/BrowserLauncher.cs @@ -28,6 +28,11 @@ namespace MediaBrowser.Server.Startup.Common.Browser OpenUrl(appHost, "http://emby.media/community"); } + public static void OpenEmbyPremiere(IServerApplicationHost appHost) + { + OpenDashboardPage("supporterkey.html", appHost); + } + /// <summary> /// Opens the web client. /// </summary> diff --git a/MediaBrowser.Server.Startup.Common/INativeApp.cs b/MediaBrowser.Server.Startup.Common/INativeApp.cs index c13d3624e..9297a6d37 100644 --- a/MediaBrowser.Server.Startup.Common/INativeApp.cs +++ b/MediaBrowser.Server.Startup.Common/INativeApp.cs @@ -25,6 +25,8 @@ namespace MediaBrowser.Server.Startup.Common /// <param name="tempDirectory">The temporary directory.</param> void AuthorizeServer(int udpPort, int httpServerPort, int httpsServerPort, string applicationPath, string tempDirectory); + bool PortsRequireAuthorization(string applicationPath); + /// <summary> /// Gets the environment. /// </summary> @@ -107,5 +109,7 @@ namespace MediaBrowser.Server.Startup.Common void LaunchUrl(string url); IDbConnector GetDbConnector(); + + void EnableLoopback(string appName); } } diff --git a/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj b/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj index c415dec8a..778002e50 100644 --- a/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj +++ b/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj @@ -73,7 +73,6 @@ <Compile Include="Migrations\IVersionMigration.cs" /> <Compile Include="Migrations\DbMigration.cs" /> <Compile Include="Migrations\MovieDbEpisodeProviderMigration.cs" /> - <Compile Include="Migrations\OmdbEpisodeProviderMigration.cs" /> <Compile Include="Migrations\UpdateLevelMigration.cs" /> <Compile Include="NativeEnvironment.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> diff --git a/MediaBrowser.Server.Startup.Common/Migrations/OmdbEpisodeProviderMigration.cs b/MediaBrowser.Server.Startup.Common/Migrations/OmdbEpisodeProviderMigration.cs deleted file mode 100644 index ebc0e67de..000000000 --- a/MediaBrowser.Server.Startup.Common/Migrations/OmdbEpisodeProviderMigration.cs +++ /dev/null @@ -1,43 +0,0 @@ -using MediaBrowser.Controller.Configuration; -using System.Linq; - -namespace MediaBrowser.Server.Startup.Common.Migrations -{ - class OmdbEpisodeProviderMigration : IVersionMigration - { - private readonly IServerConfigurationManager _config; - private const string _providerName = "The Open Movie Database"; - - public OmdbEpisodeProviderMigration(IServerConfigurationManager config) - { - _config = config; - } - - public void Run() - { - var migrationKey = this.GetType().FullName; - var migrationKeyList = _config.Configuration.Migrations.ToList(); - - if (!migrationKeyList.Contains(migrationKey)) - { - foreach (var metaDataOption in _config.Configuration.MetadataOptions) - { - if (metaDataOption.ItemType == "Episode") - { - var disabledFetchers = metaDataOption.DisabledMetadataFetchers.ToList(); - if (!disabledFetchers.Contains(_providerName)) - { - disabledFetchers.Add(_providerName); - metaDataOption.DisabledMetadataFetchers = disabledFetchers.ToArray(); - } - } - } - - migrationKeyList.Add(migrationKey); - _config.Configuration.Migrations = migrationKeyList.ToArray(); - _config.SaveConfiguration(); - } - - } - } -} diff --git a/MediaBrowser.Server.Startup.Common/Migrations/UpdateLevelMigration.cs b/MediaBrowser.Server.Startup.Common/Migrations/UpdateLevelMigration.cs index ec00fb33d..4afd5bd34 100644 --- a/MediaBrowser.Server.Startup.Common/Migrations/UpdateLevelMigration.cs +++ b/MediaBrowser.Server.Startup.Common/Migrations/UpdateLevelMigration.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Implementations.Updates; @@ -41,12 +42,6 @@ namespace MediaBrowser.Server.Startup.Common.Migrations { var updateLevel = _config.Configuration.SystemUpdateLevel; - if (updateLevel == PackageVersionClass.Dev) - { - // It's already dev, there's nothing to check - return; - } - await CheckVersion(currentVersion, updateLevel, CancellationToken.None).ConfigureAwait(false); } catch @@ -55,21 +50,39 @@ namespace MediaBrowser.Server.Startup.Common.Migrations } } - private async Task CheckVersion(Version currentVersion, PackageVersionClass updateLevel, CancellationToken cancellationToken) + private async Task CheckVersion(Version currentVersion, PackageVersionClass currentUpdateLevel, CancellationToken cancellationToken) { - var releases = await new GithubUpdater(_httpClient, _jsonSerializer, TimeSpan.FromMinutes(5)) + var releases = await new GithubUpdater(_httpClient, _jsonSerializer) .GetLatestReleases("MediaBrowser", "Emby", _releaseAssetFilename, cancellationToken).ConfigureAwait(false); - var newUpdateLevel = updateLevel; + var newUpdateLevel = GetNewUpdateLevel(currentVersion, currentUpdateLevel, releases); + + if (newUpdateLevel != currentUpdateLevel) + { + _config.Configuration.SystemUpdateLevel = newUpdateLevel; + _config.SaveConfiguration(); + } + } + + private PackageVersionClass GetNewUpdateLevel(Version currentVersion, PackageVersionClass currentUpdateLevel, List<GithubUpdater.RootObject> releases) + { + var newUpdateLevel = currentUpdateLevel; // If the current version is later than current stable, set the update level to beta if (releases.Count >= 1) { var release = releases[0]; var version = ParseVersion(release.tag_name); - if (version != null && currentVersion > version) + if (version != null) { - newUpdateLevel = PackageVersionClass.Beta; + if (currentVersion > version) + { + newUpdateLevel = PackageVersionClass.Beta; + } + else + { + return PackageVersionClass.Release; + } } } @@ -78,17 +91,20 @@ namespace MediaBrowser.Server.Startup.Common.Migrations { var release = releases[1]; var version = ParseVersion(release.tag_name); - if (version != null && currentVersion > version) + if (version != null) { - newUpdateLevel = PackageVersionClass.Dev; + if (currentVersion > version) + { + newUpdateLevel = PackageVersionClass.Dev; + } + else + { + return PackageVersionClass.Beta; + } } } - if (newUpdateLevel != updateLevel) - { - _config.Configuration.SystemUpdateLevel = newUpdateLevel; - _config.SaveConfiguration(); - } + return newUpdateLevel; } private Version ParseVersion(string versionString) diff --git a/MediaBrowser.ServerApplication/MainStartup.cs b/MediaBrowser.ServerApplication/MainStartup.cs index 8f153f33f..cdacdc81f 100644 --- a/MediaBrowser.ServerApplication/MainStartup.cs +++ b/MediaBrowser.ServerApplication/MainStartup.cs @@ -292,7 +292,6 @@ namespace MediaBrowser.ServerApplication ErrorModes.SEM_NOGPFAULTERRORBOX | ErrorModes.SEM_NOOPENFILEERRORBOX); } - var task = _appHost.Init(initProgress); Task.WaitAll(task); @@ -332,7 +331,7 @@ namespace MediaBrowser.ServerApplication Application.Run(); } - private static SplashForm _splash; + internal static SplashForm _splash; private static Thread _splashThread; private static void ShowSplashScreen(Version appVersion, Progress<double> progress, ILogger logger) { diff --git a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj index 9c5470358..a32312493 100644 --- a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj +++ b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj @@ -119,6 +119,7 @@ <Compile Include="MainStartup.cs" /> <Compile Include="Native\LnkShortcutHandler.cs" /> <Compile Include="Native\DbConnector.cs" /> + <Compile Include="Native\LoopbackUtil.cs" /> <Compile Include="Native\Standby.cs" /> <Compile Include="Native\ServerAuthorization.cs" /> <Compile Include="Native\WindowsApp.cs" /> diff --git a/MediaBrowser.ServerApplication/Native/LoopbackUtil.cs b/MediaBrowser.ServerApplication/Native/LoopbackUtil.cs new file mode 100644 index 000000000..5b260685b --- /dev/null +++ b/MediaBrowser.ServerApplication/Native/LoopbackUtil.cs @@ -0,0 +1,358 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace MediaBrowser.ServerApplication.Native +{ + /// <summary> + /// http://blogs.msdn.com/b/fiddler/archive/2011/12/10/fiddler-windows-8-apps-enable-LoopUtil-network-isolation-exemption.aspx + /// </summary> + public class LoopUtil + { + //http://msdn.microsoft.com/en-us/library/windows/desktop/aa379595(v=vs.85).aspx + [StructLayout(LayoutKind.Sequential)] + internal struct SID_AND_ATTRIBUTES + { + public IntPtr Sid; + public uint Attributes; + } + + [StructLayoutAttribute(LayoutKind.Sequential)] + internal struct INET_FIREWALL_AC_CAPABILITIES + { + public uint count; + public IntPtr capabilities; //SID_AND_ATTRIBUTES + } + + [StructLayoutAttribute(LayoutKind.Sequential)] + internal struct INET_FIREWALL_AC_BINARIES + { + public uint count; + public IntPtr binaries; + } + + [StructLayoutAttribute(LayoutKind.Sequential)] + internal struct INET_FIREWALL_APP_CONTAINER + { + internal IntPtr appContainerSid; + internal IntPtr userSid; + [MarshalAs(UnmanagedType.LPWStr)] + public string appContainerName; + [MarshalAs(UnmanagedType.LPWStr)] + public string displayName; + [MarshalAs(UnmanagedType.LPWStr)] + public string description; + internal INET_FIREWALL_AC_CAPABILITIES capabilities; + internal INET_FIREWALL_AC_BINARIES binaries; + [MarshalAs(UnmanagedType.LPWStr)] + public string workingDirectory; + [MarshalAs(UnmanagedType.LPWStr)] + public string packageFullName; + } + + + // Call this API to free the memory returned by the Enumeration API + [DllImport("FirewallAPI.dll")] + internal static extern void NetworkIsolationFreeAppContainers(IntPtr pACs); + + // Call this API to load the current list of LoopUtil-enabled AppContainers + [DllImport("FirewallAPI.dll")] + internal static extern uint NetworkIsolationGetAppContainerConfig(out uint pdwCntACs, out IntPtr appContainerSids); + + // Call this API to set the LoopUtil-exemption list + [DllImport("FirewallAPI.dll")] + private static extern uint NetworkIsolationSetAppContainerConfig(uint pdwCntACs, SID_AND_ATTRIBUTES[] appContainerSids); + + + // Use this API to convert a string SID into an actual SID + [DllImport("advapi32.dll", SetLastError = true)] + internal static extern bool ConvertStringSidToSid(string strSid, out IntPtr pSid); + + [DllImport("advapi32", CharSet = CharSet.Auto, SetLastError = true)] + static extern bool ConvertSidToStringSid( + [MarshalAs(UnmanagedType.LPArray)] byte[] pSID, + out IntPtr ptrSid); + + [DllImport("advapi32", CharSet = CharSet.Auto, SetLastError = true)] + static extern bool ConvertSidToStringSid(IntPtr pSid, out string strSid); + + // Use this API to convert a string reference (e.g. "@{blah.pri?ms-resource://whatever}") into a plain string + [DllImport("shlwapi.dll", CharSet = CharSet.Unicode, ExactSpelling = true)] + internal static extern int SHLoadIndirectString(string pszSource, StringBuilder pszOutBuf); + + // Call this API to enumerate all of the AppContainers on the system + [DllImport("FirewallAPI.dll")] + internal static extern uint NetworkIsolationEnumAppContainers(uint Flags, out uint pdwCntPublicACs, out IntPtr ppACs); + // DWORD NetworkIsolationEnumAppContainers( + // _In_ DWORD Flags, + // _Out_ DWORD *pdwNumPublicAppCs, + // _Out_ PINET_FIREWALL_APP_CONTAINER *ppPublicAppCs + //); + + //http://msdn.microsoft.com/en-gb/library/windows/desktop/hh968116.aspx + enum NETISO_FLAG + { + NETISO_FLAG_FORCE_COMPUTE_BINARIES = 0x1, + NETISO_FLAG_MAX = 0x2 + } + + + public class AppContainer + { + public String appContainerName { get; set; } + public String displayName { get; set; } + public String workingDirectory { get; set; } + public String StringSid { get; set; } + public List<uint> capabilities { get; set; } + public bool LoopUtil { get; set; } + + public AppContainer(String _appContainerName, String _displayName, String _workingDirectory, IntPtr _sid) + { + this.appContainerName = _appContainerName; + this.displayName = _displayName; + this.workingDirectory = _workingDirectory; + String tempSid; + ConvertSidToStringSid(_sid, out tempSid); + this.StringSid = tempSid; + } + } + + internal List<LoopUtil.INET_FIREWALL_APP_CONTAINER> _AppList; + internal List<LoopUtil.SID_AND_ATTRIBUTES> _AppListConfig; + public List<AppContainer> Apps = new List<AppContainer>(); + internal IntPtr _pACs; + + public LoopUtil() + { + LoadApps(); + } + + public void LoadApps() + { + Apps.Clear(); + _pACs = IntPtr.Zero; + //Full List of Apps + _AppList = PI_NetworkIsolationEnumAppContainers(); + //List of Apps that have LoopUtil enabled. + _AppListConfig = PI_NetworkIsolationGetAppContainerConfig(); + foreach (var PI_app in _AppList) + { + AppContainer app = new AppContainer(PI_app.appContainerName, PI_app.displayName, PI_app.workingDirectory, PI_app.appContainerSid); + + var app_capabilities = LoopUtil.getCapabilites(PI_app.capabilities); + if (app_capabilities.Count > 0) + { + //var sid = new SecurityIdentifier(app_capabilities[0], 0); + + IntPtr arrayValue = IntPtr.Zero; + //var b = LoopUtil.ConvertStringSidToSid(app_capabilities[0].Sid, out arrayValue); + //string mysid; + //var b = LoopUtil.ConvertSidToStringSid(app_capabilities[0].Sid, out mysid); + } + app.LoopUtil = CheckLoopback(PI_app.appContainerSid); + Apps.Add(app); + } + } + private bool CheckLoopback(IntPtr intPtr) + { + foreach (SID_AND_ATTRIBUTES item in _AppListConfig) + { + string left, right; + ConvertSidToStringSid(item.Sid, out left); + ConvertSidToStringSid(intPtr, out right); + + if (left == right) + { + return true; + } + } + return false; + } + + private bool CreateExcemptions(string appName) + { + var hasChanges = false; + + foreach (var app in Apps) + { + if ((app.appContainerName ?? string.Empty).IndexOf(appName, StringComparison.OrdinalIgnoreCase) != -1 || + (app.displayName ?? string.Empty).IndexOf(appName, StringComparison.OrdinalIgnoreCase) != -1) + { + if (!app.LoopUtil) + { + app.LoopUtil = true; + hasChanges = true; + } + } + } + + return hasChanges; + } + + public static void Run(string appName) + { + var util = new LoopUtil(); + util.LoadApps(); + + var hasChanges = util.CreateExcemptions(appName); + + if (hasChanges) + { + util.SaveLoopbackState(); + } + util.SaveLoopbackState(); + } + + private static List<SID_AND_ATTRIBUTES> getCapabilites(INET_FIREWALL_AC_CAPABILITIES cap) + { + List<SID_AND_ATTRIBUTES> mycap = new List<SID_AND_ATTRIBUTES>(); + + IntPtr arrayValue = cap.capabilities; + + var structSize = Marshal.SizeOf(typeof(SID_AND_ATTRIBUTES)); + for (var i = 0; i < cap.count; i++) + { + var cur = (SID_AND_ATTRIBUTES)Marshal.PtrToStructure(arrayValue, typeof(SID_AND_ATTRIBUTES)); + mycap.Add(cur); + arrayValue = new IntPtr((long)(arrayValue) + (long)(structSize)); + } + + return mycap; + + } + + private static List<SID_AND_ATTRIBUTES> getContainerSID(INET_FIREWALL_AC_CAPABILITIES cap) + { + List<SID_AND_ATTRIBUTES> mycap = new List<SID_AND_ATTRIBUTES>(); + + IntPtr arrayValue = cap.capabilities; + + var structSize = Marshal.SizeOf(typeof(SID_AND_ATTRIBUTES)); + for (var i = 0; i < cap.count; i++) + { + var cur = (SID_AND_ATTRIBUTES)Marshal.PtrToStructure(arrayValue, typeof(SID_AND_ATTRIBUTES)); + mycap.Add(cur); + arrayValue = new IntPtr((long)(arrayValue) + (long)(structSize)); + } + + return mycap; + + } + + private static List<SID_AND_ATTRIBUTES> PI_NetworkIsolationGetAppContainerConfig() + { + + IntPtr arrayValue = IntPtr.Zero; + uint size = 0; + var list = new List<SID_AND_ATTRIBUTES>(); + + // Pin down variables + GCHandle handle_pdwCntPublicACs = GCHandle.Alloc(size, GCHandleType.Pinned); + GCHandle handle_ppACs = GCHandle.Alloc(arrayValue, GCHandleType.Pinned); + + uint retval = NetworkIsolationGetAppContainerConfig(out size, out arrayValue); + + var structSize = Marshal.SizeOf(typeof(SID_AND_ATTRIBUTES)); + for (var i = 0; i < size; i++) + { + var cur = (SID_AND_ATTRIBUTES)Marshal.PtrToStructure(arrayValue, typeof(SID_AND_ATTRIBUTES)); + list.Add(cur); + arrayValue = new IntPtr((long)(arrayValue) + (long)(structSize)); + } + + //release pinned variables. + handle_pdwCntPublicACs.Free(); + handle_ppACs.Free(); + + return list; + + + } + + private List<INET_FIREWALL_APP_CONTAINER> PI_NetworkIsolationEnumAppContainers() + { + + IntPtr arrayValue = IntPtr.Zero; + uint size = 0; + var list = new List<INET_FIREWALL_APP_CONTAINER>(); + + // Pin down variables + GCHandle handle_pdwCntPublicACs = GCHandle.Alloc(size, GCHandleType.Pinned); + GCHandle handle_ppACs = GCHandle.Alloc(arrayValue, GCHandleType.Pinned); + + //uint retval2 = NetworkIsolationGetAppContainerConfig( out size, out arrayValue); + + uint retval = NetworkIsolationEnumAppContainers((Int32)NETISO_FLAG.NETISO_FLAG_MAX, out size, out arrayValue); + _pACs = arrayValue; //store the pointer so it can be freed when we close the form + + var structSize = Marshal.SizeOf(typeof(INET_FIREWALL_APP_CONTAINER)); + for (var i = 0; i < size; i++) + { + var cur = (INET_FIREWALL_APP_CONTAINER)Marshal.PtrToStructure(arrayValue, typeof(INET_FIREWALL_APP_CONTAINER)); + list.Add(cur); + arrayValue = new IntPtr((long)(arrayValue) + (long)(structSize)); + } + + //release pinned variables. + handle_pdwCntPublicACs.Free(); + handle_ppACs.Free(); + + return list; + + + } + + public bool SaveLoopbackState() + { + var countEnabled = CountEnabledLoopUtil(); + SID_AND_ATTRIBUTES[] arr = new SID_AND_ATTRIBUTES[countEnabled]; + int count = 0; + + for (int i = 0; i < Apps.Count; i++) + { + if (Apps[i].LoopUtil) + { + arr[count].Attributes = 0; + //TO DO: + IntPtr ptr; + ConvertStringSidToSid(Apps[i].StringSid, out ptr); + arr[count].Sid = ptr; + count++; + } + + } + + + if (NetworkIsolationSetAppContainerConfig((uint)countEnabled, arr) == 0) + { + return true; + } + else + { return false; } + + } + + private int CountEnabledLoopUtil() + { + var count = 0; + for (int i = 0; i < Apps.Count; i++) + { + if (Apps[i].LoopUtil) + { + count++; + } + + } + return count; + } + + public void FreeResources() + { + NetworkIsolationFreeAppContainers(_pACs); + } + + } +} diff --git a/MediaBrowser.ServerApplication/Native/RegisterServer.bat b/MediaBrowser.ServerApplication/Native/RegisterServer.bat index 27f863d58..85baa0d03 100644 --- a/MediaBrowser.ServerApplication/Native/RegisterServer.bat +++ b/MediaBrowser.ServerApplication/Native/RegisterServer.bat @@ -20,7 +20,9 @@ netsh advfirewall firewall add rule name="Port %3" dir=in action=allow protocol= if [%4]==[] GOTO DONE +netsh advfirewall firewall delete rule name="mediabrowser.serverapplication.exe" netsh advfirewall firewall delete rule name="Emby Server" + netsh advfirewall firewall add rule name="Emby Server" dir=in action=allow protocol=TCP program=%4 enable=yes netsh advfirewall firewall add rule name="Emby Server" dir=in action=allow protocol=UDP program=%4 enable=yes diff --git a/MediaBrowser.ServerApplication/Native/WindowsApp.cs b/MediaBrowser.ServerApplication/Native/WindowsApp.cs index 3c9c04acb..b08b82de5 100644 --- a/MediaBrowser.ServerApplication/Native/WindowsApp.cs +++ b/MediaBrowser.ServerApplication/Native/WindowsApp.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; +using System.Windows.Forms; using CommonIO; using MediaBrowser.Controller.Power; using MediaBrowser.Model.System; @@ -203,5 +204,76 @@ namespace MediaBrowser.ServerApplication.Native { ((Process)sender).Dispose(); } + + public void EnableLoopback(string appName) + { + LoopUtil.Run(appName); + } + + private bool Confirm() + { + if (MainStartup._splash == null) + { + return false; + } + + return MessageBox.Show(MainStartup._splash, "Emby has detected that a rule has been added to Windows Firewall that may prevent your other devices from accessing Emby Server. Click OK to remove this rule, or cancel to proceed anyway.", "Windows Firewall", MessageBoxButtons.OKCancel) == DialogResult.OK; + } + + public bool PortsRequireAuthorization(string applicationPath) + { + var appNameSrch = Path.GetFileName(applicationPath); + + var startInfo = new ProcessStartInfo + { + FileName = "netsh", + + Arguments = "advfirewall firewall show rule \"" + appNameSrch + "\"", + + CreateNoWindow = true, + UseShellExecute = false, + WindowStyle = ProcessWindowStyle.Hidden, + ErrorDialog = false, + RedirectStandardOutput = true + }; + + using (var process = Process.Start(startInfo)) + { + process.Start(); + + try + { + var data = process.StandardOutput.ReadToEnd() ?? string.Empty; + + if (data.IndexOf("Block", StringComparison.OrdinalIgnoreCase) != -1) + { + _logger.Info("Found potential windows firewall rule blocking Emby Server: " + data); + return Confirm(); + } + + //var parts = data.Split('\n'); + + //return parts.Length > 4; + //return Confirm(); + return false; + } + catch (Exception ex) + { + _logger.ErrorException("Error querying windows firewall", ex); + + // Hate having to do this + try + { + process.Kill(); + } + catch (Exception ex1) + { + _logger.ErrorException("Error killing process", ex1); + } + + throw; + } + } + } } }
\ No newline at end of file diff --git a/MediaBrowser.ServerApplication/ServerNotifyIcon.cs b/MediaBrowser.ServerApplication/ServerNotifyIcon.cs index d04128a18..7805c0d68 100644 --- a/MediaBrowser.ServerApplication/ServerNotifyIcon.cs +++ b/MediaBrowser.ServerApplication/ServerNotifyIcon.cs @@ -20,6 +20,7 @@ namespace MediaBrowser.ServerApplication private ToolStripMenuItem cmdRestart; private ToolStripSeparator toolStripSeparator1; private ToolStripMenuItem cmdCommunity; + private ToolStripMenuItem cmdPremiere; private Container components; private readonly ILogger _logger; @@ -50,6 +51,7 @@ namespace MediaBrowser.ServerApplication cmdExit = new ToolStripMenuItem(); cmdCommunity = new ToolStripMenuItem(); + cmdPremiere = new ToolStripMenuItem(); toolStripSeparator1 = new ToolStripSeparator(); cmdRestart = new ToolStripMenuItem(); toolStripSeparator2 = new ToolStripSeparator(); @@ -69,6 +71,7 @@ namespace MediaBrowser.ServerApplication contextMenuStrip1.Items.AddRange(new ToolStripItem[] { cmdBrowse, cmdConfigure, + cmdPremiere, toolStripSeparator2, cmdRestart, toolStripSeparator1, @@ -89,6 +92,11 @@ namespace MediaBrowser.ServerApplication cmdCommunity.Name = "cmdCommunity"; cmdCommunity.Size = new System.Drawing.Size(208, 22); // + // cmdPremiere + // + cmdPremiere.Name = "cmdPremiere"; + cmdPremiere.Size = new System.Drawing.Size(208, 22); + // // toolStripSeparator1 // toolStripSeparator1.Name = "toolStripSeparator1"; @@ -118,6 +126,7 @@ namespace MediaBrowser.ServerApplication cmdRestart.Click += cmdRestart_Click; cmdConfigure.Click += cmdConfigure_Click; cmdCommunity.Click += cmdCommunity_Click; + cmdPremiere.Click += cmdPremiere_Click; cmdBrowse.Click += cmdBrowse_Click; _configurationManager.ConfigurationUpdated += Instance_ConfigurationUpdated; @@ -138,6 +147,7 @@ namespace MediaBrowser.ServerApplication cmdExit.Text = _localization.GetLocalizedString("LabelExit"); cmdCommunity.Text = _localization.GetLocalizedString("LabelVisitCommunity"); + cmdPremiere.Text = _localization.GetLocalizedString("Emby Premiere"); cmdBrowse.Text = _localization.GetLocalizedString("LabelBrowseLibrary"); cmdConfigure.Text = _localization.GetLocalizedString("LabelConfigureServer"); cmdRestart.Text = _localization.GetLocalizedString("LabelRestartServer"); @@ -163,6 +173,11 @@ namespace MediaBrowser.ServerApplication BrowserLauncher.OpenWebClient(_appHost); } + void cmdPremiere_Click(object sender, EventArgs e) + { + BrowserLauncher.OpenEmbyPremiere(_appHost); + } + void cmdCommunity_Click(object sender, EventArgs e) { BrowserLauncher.OpenCommunity(_appHost); diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs index aec4632ae..2f8b34879 100644 --- a/MediaBrowser.WebDashboard/Api/DashboardService.cs +++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs @@ -58,11 +58,6 @@ namespace MediaBrowser.WebDashboard.Api { } - [Route("/web/staticfiles", "GET")] - public class GetCacheFiles - { - } - /// <summary> /// Class GetDashboardResource /// </summary> @@ -145,37 +140,6 @@ namespace MediaBrowser.WebDashboard.Api return ResultFactory.GetStaticResult(Request, page.Plugin.Version.ToString().GetMD5(), null, null, MimeTypes.GetMimeType("page.html"), () => GetPackageCreator().ModifyHtml("dummy.html", page.GetHtmlStream(), null, _appHost.ApplicationVersion.ToString(), null, false)); } - public object Get(GetCacheFiles request) - { - var allFiles = GetCacheFileList(); - - return ResultFactory.GetOptimizedResult(Request, _jsonSerializer.SerializeToString(allFiles)); - } - - private List<string> GetCacheFileList() - { - var creator = GetPackageCreator(); - var directory = creator.DashboardUIPath; - - var skipExtensions = GetDeployIgnoreExtensions(); - var skipNames = GetDeployIgnoreFilenames(); - - return - Directory.GetFiles(directory, "*", SearchOption.AllDirectories) - .Where(i => !skipExtensions.Contains(Path.GetExtension(i) ?? string.Empty, StringComparer.OrdinalIgnoreCase)) - .Where(i => !skipNames.Any(s => - { - if (s.Item2) - { - return string.Equals(s.Item1, Path.GetFileName(i), StringComparison.OrdinalIgnoreCase); - } - - return (Path.GetFileName(i) ?? string.Empty).IndexOf(s.Item1, StringComparison.OrdinalIgnoreCase) != -1; - })) - .Select(i => i.Replace(directory, string.Empty, StringComparison.OrdinalIgnoreCase).Replace("\\", "/").TrimStart('/') + "?v=" + _appHost.ApplicationVersion.ToString()) - .ToList(); - } - /// <summary> /// Gets the specified request. /// </summary> @@ -369,8 +333,6 @@ namespace MediaBrowser.WebDashboard.Api var appVersion = _appHost.ApplicationVersion.ToString(); - File.WriteAllText(Path.Combine(path, "staticfiles"), _jsonSerializer.SerializeToString(GetCacheFileList())); - var mode = request.Mode; // Try to trim the output size a bit diff --git a/MediaBrowser.WebDashboard/Api/PackageCreator.cs b/MediaBrowser.WebDashboard/Api/PackageCreator.cs index 65e7fa5d3..e7247a11f 100644 --- a/MediaBrowser.WebDashboard/Api/PackageCreator.cs +++ b/MediaBrowser.WebDashboard/Api/PackageCreator.cs @@ -346,11 +346,14 @@ 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' data: filesystem:;\">"); + sb.Append("<meta http-equiv=\"Content-Security-Policy\" content=\"default-src * 'self' 'unsafe-inline' 'unsafe-eval' data: gap: file: filesystem:; connect-src ws: *\">"); + } + else + { + sb.Append("<meta http-equiv=\"X-UA-Compatibility\" content=\"IE=Edge\">"); } sb.Append("<link rel=\"manifest\" href=\"manifest.json\">"); - sb.Append("<meta http-equiv=\"X-UA-Compatibility\" content=\"IE=Edge\">"); sb.Append("<meta name=\"format-detection\" content=\"telephone=no\">"); sb.Append("<meta name=\"msapplication-tap-highlight\" content=\"no\">"); sb.Append("<meta name=\"viewport\" content=\"user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width\">"); diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj index 5b66c27f4..2b828b8fc 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -191,6 +191,9 @@ <Content Include="dashboard-ui\scripts\camerauploadsettings.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="dashboard-ui\scripts\livetvschedule.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\scripts\userpasswordpage.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -281,9 +284,6 @@ <Content Include="dashboard-ui\css\nowplayingbar.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\components\imageeditor\imageeditor.template.html">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\favorites.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -755,9 +755,6 @@ <Content Include="dashboard-ui\reports.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\livetvrecordinglist.html">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\livetvseriestimer.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -944,9 +941,6 @@ <Content Include="dashboard-ui\scripts\livetvchannel.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\scripts\livetvrecordinglist.js">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\scripts\livetvseriestimer.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -1086,9 +1080,6 @@ <Content Include="dashboard-ui\music.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\components\imageeditor\imageeditor.js">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\scripts\edititemmetadata.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs index f777ae16b..1f222e75c 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs @@ -25,19 +25,22 @@ namespace MediaBrowser.XbmcMetadata.Parsers /// The logger /// </summary> protected ILogger Logger { get; private set; } + protected IProviderManager ProviderManager { get; private set; } private readonly CultureInfo _usCulture = new CultureInfo("en-US"); private readonly IConfigurationManager _config; + private Dictionary<string, string> _validProviderIds; /// <summary> /// Initializes a new instance of the <see cref="BaseNfoParser{T}" /> class. /// </summary> /// <param name="logger">The logger.</param> /// <param name="config">The configuration.</param> - public BaseNfoParser(ILogger logger, IConfigurationManager config) + public BaseNfoParser(ILogger logger, IConfigurationManager config, IProviderManager providerManager) { Logger = logger; _config = config; + ProviderManager = providerManager; } /// <summary> @@ -68,6 +71,24 @@ namespace MediaBrowser.XbmcMetadata.Parsers ValidationType = ValidationType.None }; + _validProviderIds = _validProviderIds = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase); + + var idInfos = ProviderManager.GetExternalIdInfos(item.Item); + + foreach (var info in idInfos) + { + var id = info.Key + "Id"; + if (!_validProviderIds.ContainsKey(id)) + { + _validProviderIds.Add(id, info.Key); + } + } + + //Additional Mappings + _validProviderIds.Add("collectionnumber", "TmdbCollection"); + _validProviderIds.Add("tmdbcolid", "TmdbCollection"); + _validProviderIds.Add("imdb_id", "Imdb"); + Fetch(item, metadataFile, settings, cancellationToken); } @@ -760,14 +781,6 @@ namespace MediaBrowser.XbmcMetadata.Parsers break; } - case "tvdbid": - var tvdbId = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(tvdbId)) - { - item.SetProviderId(MetadataProviders.Tvdb, tvdbId); - } - break; - case "votes": { var val = reader.ReadElementContentAsString(); @@ -782,127 +795,6 @@ namespace MediaBrowser.XbmcMetadata.Parsers } break; } - case "musicbrainzalbumid": - { - var mbz = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(mbz)) - { - item.SetProviderId(MetadataProviders.MusicBrainzAlbum, mbz); - } - break; - } - case "musicbrainzalbumartistid": - { - var mbz = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(mbz)) - { - item.SetProviderId(MetadataProviders.MusicBrainzAlbumArtist, mbz); - } - break; - } - case "musicbrainzartistid": - { - var mbz = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(mbz)) - { - item.SetProviderId(MetadataProviders.MusicBrainzArtist, mbz); - } - break; - } - case "musicbrainzreleasegroupid": - { - var mbz = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(mbz)) - { - item.SetProviderId(MetadataProviders.MusicBrainzReleaseGroup, mbz); - } - break; - } - case "tvrageid": - { - var id = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(id)) - { - item.SetProviderId(MetadataProviders.TvRage, id); - } - break; - } - case "tvmazeid": - { - var id = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(id)) - { - item.SetProviderId(MetadataProviders.TvMaze, id); - } - break; - } - case "audiodbartistid": - { - var id = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(id)) - { - item.SetProviderId(MetadataProviders.AudioDbArtist, id); - } - break; - } - case "audiodbalbumid": - { - var id = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(id)) - { - item.SetProviderId(MetadataProviders.AudioDbAlbum, id); - } - break; - } - case "rottentomatoesid": - var rtId = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(rtId)) - { - item.SetProviderId(MetadataProviders.RottenTomatoes, rtId); - } - break; - - case "tmdbid": - var tmdb = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(tmdb)) - { - item.SetProviderId(MetadataProviders.Tmdb, tmdb); - } - break; - - case "collectionnumber": - case "tmdbcolid": - var tmdbCollection = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(tmdbCollection)) - { - item.SetProviderId(MetadataProviders.TmdbCollection, tmdbCollection); - } - break; - - case "tvcomid": - var TVcomId = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(TVcomId)) - { - item.SetProviderId(MetadataProviders.Tvcom, TVcomId); - } - break; - - case "zap2itid": - var zap2ItId = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(zap2ItId)) - { - item.SetProviderId(MetadataProviders.Zap2It, zap2ItId); - } - break; - - case "imdb_id": - case "imdbid": - var imDbId = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(imDbId)) - { - item.SetProviderId(MetadataProviders.Imdb, imDbId); - } - break; case "genre": { diff --git a/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs index dfa5c1b71..a5a86fc58 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs @@ -12,7 +12,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers { public class EpisodeNfoParser : BaseNfoParser<Episode> { - public EpisodeNfoParser(ILogger logger, IConfigurationManager config) : base(logger, config) + public EpisodeNfoParser(ILogger logger, IConfigurationManager config, IProviderManager providerManager) : base(logger, config, providerManager) { } diff --git a/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs index dfe88cd3f..3e6196238 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs @@ -10,8 +10,8 @@ namespace MediaBrowser.XbmcMetadata.Parsers { class MovieNfoParser : BaseNfoParser<Video> { - public MovieNfoParser(ILogger logger, IConfigurationManager config) - : base(logger, config) + public MovieNfoParser(ILogger logger, IConfigurationManager config, IProviderManager providerManager) + : base(logger, config, providerManager) { } diff --git a/MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs index 1164040fd..c051ad4f8 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs @@ -9,7 +9,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers { public class SeasonNfoParser : BaseNfoParser<Season> { - public SeasonNfoParser(ILogger logger, IConfigurationManager config) : base(logger, config) + public SeasonNfoParser(ILogger logger, IConfigurationManager config, IProviderManager providerManager) : base(logger, config, providerManager) { } diff --git a/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs index 4dd2e1759..8b7deebf2 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs @@ -11,7 +11,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers { public class SeriesNfoParser : BaseNfoParser<Series> { - public SeriesNfoParser(ILogger logger, IConfigurationManager config) : base(logger, config) + public SeriesNfoParser(ILogger logger, IConfigurationManager config, IProviderManager providerManager) : base(logger, config, providerManager) { } diff --git a/MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs index d0b1b142c..a90cb20fd 100644 --- a/MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs +++ b/MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs @@ -13,17 +13,19 @@ namespace MediaBrowser.XbmcMetadata.Providers { private readonly ILogger _logger; private readonly IConfigurationManager _config; + private readonly IProviderManager _providerManager; - public AlbumNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config) + public AlbumNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager) : base(fileSystem) { _logger = logger; _config = config; + _providerManager = providerManager; } protected override void Fetch(MetadataResult<MusicAlbum> result, string path, CancellationToken cancellationToken) { - new BaseNfoParser<MusicAlbum>(_logger, _config).Fetch(result, path, cancellationToken); + new BaseNfoParser<MusicAlbum>(_logger, _config, _providerManager).Fetch(result, path, cancellationToken); } protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService) diff --git a/MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs index 81c6b1c28..391eb459a 100644 --- a/MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs +++ b/MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs @@ -13,17 +13,19 @@ namespace MediaBrowser.XbmcMetadata.Providers { private readonly ILogger _logger; private readonly IConfigurationManager _config; + private readonly IProviderManager _providerManager; - public ArtistNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config) + public ArtistNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager) : base(fileSystem) { _logger = logger; _config = config; + _providerManager = providerManager; } protected override void Fetch(MetadataResult<MusicArtist> result, string path, CancellationToken cancellationToken) { - new BaseNfoParser<MusicArtist>(_logger, _config).Fetch(result, path, cancellationToken); + new BaseNfoParser<MusicArtist>(_logger, _config, _providerManager).Fetch(result, path, cancellationToken); } protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService) diff --git a/MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs index 26ffc9a19..0a268e8e2 100644 --- a/MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs +++ b/MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs @@ -15,12 +15,14 @@ namespace MediaBrowser.XbmcMetadata.Providers { private readonly ILogger _logger; private readonly IConfigurationManager _config; + private readonly IProviderManager _providerManager; - public BaseVideoNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config) + public BaseVideoNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager) : base(fileSystem) { _logger = logger; _config = config; + _providerManager = providerManager; } protected override void Fetch(MetadataResult<T> result, string path, CancellationToken cancellationToken) @@ -29,7 +31,7 @@ namespace MediaBrowser.XbmcMetadata.Providers { Item = result.Item }; - new MovieNfoParser(_logger, _config).Fetch(tmpItem, path, cancellationToken); + new MovieNfoParser(_logger, _config, _providerManager).Fetch(tmpItem, path, cancellationToken); result.Item = (T)tmpItem.Item; result.People = tmpItem.People; diff --git a/MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs index 5587c7515..d8f3f1a21 100644 --- a/MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs +++ b/MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs @@ -14,19 +14,21 @@ namespace MediaBrowser.XbmcMetadata.Providers { private readonly ILogger _logger; private readonly IConfigurationManager _config; + private readonly IProviderManager _providerManager; - public EpisodeNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config) + public EpisodeNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager) : base(fileSystem) { _logger = logger; _config = config; + _providerManager = providerManager; } protected override void Fetch(MetadataResult<Episode> result, string path, CancellationToken cancellationToken) { var images = new List<LocalImageInfo>(); - new EpisodeNfoParser(_logger, _config).Fetch(result, images, path, cancellationToken); + new EpisodeNfoParser(_logger, _config, _providerManager).Fetch(result, images, path, cancellationToken); result.Images = images; } diff --git a/MediaBrowser.XbmcMetadata/Providers/MovieNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/MovieNfoProvider.cs index bba4010dc..5a759f81e 100644 --- a/MediaBrowser.XbmcMetadata/Providers/MovieNfoProvider.cs +++ b/MediaBrowser.XbmcMetadata/Providers/MovieNfoProvider.cs @@ -2,27 +2,28 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; +using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Logging; namespace MediaBrowser.XbmcMetadata.Providers { public class MovieNfoProvider : BaseVideoNfoProvider<Movie> { - public MovieNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config) : base(fileSystem, logger, config) + public MovieNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager) : base(fileSystem, logger, config, providerManager) { } } public class MusicVideoNfoProvider : BaseVideoNfoProvider<MusicVideo> { - public MusicVideoNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config) : base(fileSystem, logger, config) + public MusicVideoNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager) : base(fileSystem, logger, config, providerManager) { } } public class VideoNfoProvider : BaseVideoNfoProvider<Video> { - public VideoNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config) : base(fileSystem, logger, config) + public VideoNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager) : base(fileSystem, logger, config, providerManager) { } } diff --git a/MediaBrowser.XbmcMetadata/Providers/SeasonNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/SeasonNfoProvider.cs index 3dac0a531..7e116b2c5 100644 --- a/MediaBrowser.XbmcMetadata/Providers/SeasonNfoProvider.cs +++ b/MediaBrowser.XbmcMetadata/Providers/SeasonNfoProvider.cs @@ -13,17 +13,19 @@ namespace MediaBrowser.XbmcMetadata.Providers { private readonly ILogger _logger; private readonly IConfigurationManager _config; + private readonly IProviderManager _providerManager; - public SeasonNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config) + public SeasonNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager) : base(fileSystem) { _logger = logger; _config = config; + _providerManager = providerManager; } protected override void Fetch(MetadataResult<Season> result, string path, CancellationToken cancellationToken) { - new SeasonNfoParser(_logger, _config).Fetch(result, path, cancellationToken); + new SeasonNfoParser(_logger, _config, _providerManager).Fetch(result, path, cancellationToken); } protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService) diff --git a/MediaBrowser.XbmcMetadata/Providers/SeriesNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/SeriesNfoProvider.cs index 4bab8d75b..f03c20954 100644 --- a/MediaBrowser.XbmcMetadata/Providers/SeriesNfoProvider.cs +++ b/MediaBrowser.XbmcMetadata/Providers/SeriesNfoProvider.cs @@ -13,17 +13,19 @@ namespace MediaBrowser.XbmcMetadata.Providers { private readonly ILogger _logger; private readonly IConfigurationManager _config; + private readonly IProviderManager _providerManager; - public SeriesNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config) + public SeriesNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager) : base(fileSystem) { _logger = logger; _config = config; + _providerManager = providerManager; } protected override void Fetch(MetadataResult<Series> result, string path, CancellationToken cancellationToken) { - new SeriesNfoParser(_logger, _config).Fetch(result, path, cancellationToken); + new SeriesNfoParser(_logger, _config, _providerManager).Fetch(result, path, cancellationToken); } protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService) diff --git a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs index c28a15a93..290ea588e 100644 --- a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs +++ b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs @@ -27,8 +27,8 @@ namespace MediaBrowser.XbmcMetadata.Savers { private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); - private static readonly Dictionary<string, string> CommonTags = new[] { - + private static readonly Dictionary<string, string> CommonTags = new[] { + "plot", "customrating", "lockdata", @@ -428,6 +428,8 @@ namespace MediaBrowser.XbmcMetadata.Savers /// <returns>Task.</returns> public static void AddCommonNodes(BaseItem item, XmlWriter writer, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataRepo, IFileSystem fileSystem, IServerConfigurationManager config) { + var writtenProviderIds = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase); + var overview = (item.Overview ?? string.Empty) .StripHtml() .Replace(""", "'"); @@ -572,6 +574,7 @@ namespace MediaBrowser.XbmcMetadata.Savers if (!string.IsNullOrEmpty(rt)) { writer.WriteElementString("rottentomatoesid", rt); + writtenProviderIds.Add(MetadataProviders.RottenTomatoes.ToString()); } var tmdbCollection = item.GetProviderId(MetadataProviders.TmdbCollection); @@ -579,6 +582,7 @@ namespace MediaBrowser.XbmcMetadata.Savers if (!string.IsNullOrEmpty(tmdbCollection)) { writer.WriteElementString("collectionnumber", tmdbCollection); + writtenProviderIds.Add(MetadataProviders.TmdbCollection.ToString()); } var imdb = item.GetProviderId(MetadataProviders.Imdb); @@ -592,6 +596,7 @@ namespace MediaBrowser.XbmcMetadata.Savers { writer.WriteElementString("imdbid", imdb); } + writtenProviderIds.Add(MetadataProviders.Imdb.ToString()); } // Series xml saver already saves this @@ -601,6 +606,7 @@ namespace MediaBrowser.XbmcMetadata.Savers if (!string.IsNullOrEmpty(tvdb)) { writer.WriteElementString("tvdbid", tvdb); + writtenProviderIds.Add(MetadataProviders.Tvdb.ToString()); } } @@ -608,12 +614,14 @@ namespace MediaBrowser.XbmcMetadata.Savers if (!string.IsNullOrEmpty(tmdb)) { writer.WriteElementString("tmdbid", tmdb); + writtenProviderIds.Add(MetadataProviders.Tmdb.ToString()); } var tvcom = item.GetProviderId(MetadataProviders.Tvcom); if (!string.IsNullOrEmpty(tvcom)) { writer.WriteElementString("tvcomid", tvcom); + writtenProviderIds.Add(MetadataProviders.Tvcom.ToString()); } if (!string.IsNullOrEmpty(item.PreferredMetadataLanguage)) @@ -766,6 +774,7 @@ namespace MediaBrowser.XbmcMetadata.Savers if (!string.IsNullOrEmpty(externalId)) { writer.WriteElementString("audiodbartistid", externalId); + writtenProviderIds.Add(MetadataProviders.AudioDbArtist.ToString()); } externalId = item.GetProviderId(MetadataProviders.AudioDbAlbum); @@ -773,6 +782,7 @@ namespace MediaBrowser.XbmcMetadata.Savers if (!string.IsNullOrEmpty(externalId)) { writer.WriteElementString("audiodbalbumid", externalId); + writtenProviderIds.Add(MetadataProviders.AudioDbAlbum.ToString()); } externalId = item.GetProviderId(MetadataProviders.Zap2It); @@ -780,6 +790,7 @@ namespace MediaBrowser.XbmcMetadata.Savers if (!string.IsNullOrEmpty(externalId)) { writer.WriteElementString("zap2itid", externalId); + writtenProviderIds.Add(MetadataProviders.Zap2It.ToString()); } externalId = item.GetProviderId(MetadataProviders.MusicBrainzAlbum); @@ -787,6 +798,7 @@ namespace MediaBrowser.XbmcMetadata.Savers if (!string.IsNullOrEmpty(externalId)) { writer.WriteElementString("musicbrainzalbumid", externalId); + writtenProviderIds.Add(MetadataProviders.MusicBrainzAlbum.ToString()); } externalId = item.GetProviderId(MetadataProviders.MusicBrainzAlbumArtist); @@ -794,6 +806,7 @@ namespace MediaBrowser.XbmcMetadata.Savers if (!string.IsNullOrEmpty(externalId)) { writer.WriteElementString("musicbrainzalbumartistid", externalId); + writtenProviderIds.Add(MetadataProviders.MusicBrainzAlbumArtist.ToString()); } externalId = item.GetProviderId(MetadataProviders.MusicBrainzArtist); @@ -801,6 +814,7 @@ namespace MediaBrowser.XbmcMetadata.Savers if (!string.IsNullOrEmpty(externalId)) { writer.WriteElementString("musicbrainzartistid", externalId); + writtenProviderIds.Add(MetadataProviders.MusicBrainzArtist.ToString()); } externalId = item.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup); @@ -808,24 +822,33 @@ namespace MediaBrowser.XbmcMetadata.Savers if (!string.IsNullOrEmpty(externalId)) { writer.WriteElementString("musicbrainzreleasegroupid", externalId); + writtenProviderIds.Add(MetadataProviders.MusicBrainzReleaseGroup.ToString()); } externalId = item.GetProviderId(MetadataProviders.Gamesdb); if (!string.IsNullOrEmpty(externalId)) { writer.WriteElementString("gamesdbid", externalId); + writtenProviderIds.Add(MetadataProviders.Gamesdb.ToString()); } externalId = item.GetProviderId(MetadataProviders.TvRage); if (!string.IsNullOrEmpty(externalId)) { writer.WriteElementString("tvrageid", externalId); + writtenProviderIds.Add(MetadataProviders.TvRage.ToString()); } - externalId = item.GetProviderId(MetadataProviders.TvMaze); - if (!string.IsNullOrEmpty(externalId)) + if (item.ProviderIds != null) { - writer.WriteElementString("tvmazeid", externalId); + foreach (var providerKey in item.ProviderIds.Keys) + { + var providerId = item.ProviderIds[providerKey]; + if (!string.IsNullOrEmpty(providerId) && !writtenProviderIds.Contains(providerKey)) + { + writer.WriteElementString(providerKey.ToLower() + "id", providerId); + } + } } if (options.SaveImagePathsInNfo) diff --git a/MediaBrowser.sln b/MediaBrowser.sln index c6068f536..0b9dd90cd 100644 --- a/MediaBrowser.sln +++ b/MediaBrowser.sln @@ -1,7 +1,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 -VisualStudioVersion = 14.0.24720.0 +VisualStudioVersion = 14.0.25420.1 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{F0E0E64C-2A6F-4E35-9533-D53AC07C2CD1}" EndProject diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec index cfdfc7ef0..06159dadb 100644 --- a/Nuget/MediaBrowser.Common.Internal.nuspec +++ b/Nuget/MediaBrowser.Common.Internal.nuspec @@ -2,7 +2,7 @@ <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <metadata> <id>MediaBrowser.Common.Internal</id> - <version>3.0.656</version> + <version>3.0.657</version> <title>MediaBrowser.Common.Internal</title> <authors>Luke</authors> <owners>ebr,Luke,scottisafool</owners> @@ -12,7 +12,7 @@ <description>Contains common components shared by Emby Theater and Emby Server. Not intended for plugin developer consumption.</description> <copyright>Copyright © Emby 2013</copyright> <dependencies> - <dependency id="MediaBrowser.Common" version="3.0.656" /> + <dependency id="MediaBrowser.Common" version="3.0.657" /> <dependency id="NLog" version="4.3.6" /> <dependency id="SimpleInjector" version="3.2.0" /> </dependencies> diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec index 1ebf4d052..de1af036d 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/Nuget/MediaBrowser.Common.nuspec @@ -2,7 +2,7 @@ <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <metadata> <id>MediaBrowser.Common</id> - <version>3.0.656</version> + <version>3.0.657</version> <title>MediaBrowser.Common</title> <authors>Emby Team</authors> <owners>ebr,Luke,scottisafool</owners> diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec index 630ff8fd2..d21fefc86 100644 --- a/Nuget/MediaBrowser.Server.Core.nuspec +++ b/Nuget/MediaBrowser.Server.Core.nuspec @@ -2,7 +2,7 @@ <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"> <metadata> <id>MediaBrowser.Server.Core</id> - <version>3.0.656</version> + <version>3.0.657</version> <title>Media Browser.Server.Core</title> <authors>Emby Team</authors> <owners>ebr,Luke,scottisafool</owners> @@ -12,7 +12,7 @@ <description>Contains core components required to build plugins for Emby Server.</description> <copyright>Copyright © Emby 2013</copyright> <dependencies> - <dependency id="MediaBrowser.Common" version="3.0.656" /> + <dependency id="MediaBrowser.Common" version="3.0.657" /> <dependency id="Interfaces.IO" version="1.0.0.5" /> </dependencies> </metadata> |
