diff --git a/MediaBrowser.Api/Library/FileOrganizationService.cs b/MediaBrowser.Api/Library/FileOrganizationService.cs index 849e9cf0d..0ed08a860 100644 --- a/MediaBrowser.Api/Library/FileOrganizationService.cs +++ b/MediaBrowser.Api/Library/FileOrganizationService.cs @@ -119,8 +119,6 @@ namespace MediaBrowser.Api.Library { private readonly IFileOrganizationService _iFileOrganizationService; - /// The _json serializer - /// private readonly IJsonSerializer _jsonSerializer; public FileOrganizationService(IFileOrganizationService iFileOrganizationService, IJsonSerializer jsonSerializer) diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 792e46842..0f58a9db8 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -460,7 +460,7 @@ namespace MediaBrowser.Api.Playback // Boost volume to 200% when downsampling from 6ch to 2ch if (channels.HasValue && channels.Value <= 2) { - if (state.AudioStream != null && state.AudioStream.Channels.HasValue && state.AudioStream.Channels.Value > 5) + if (state.AudioStream != null && state.AudioStream.Channels.HasValue && state.AudioStream.Channels.Value > 5 && !ApiEntryPoint.Instance.GetEncodingOptions().DownMixAudioBoost.Equals(1)) { volParam = ",volume=" + ApiEntryPoint.Instance.GetEncodingOptions().DownMixAudioBoost.ToString(UsCulture); } diff --git a/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs b/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs index 733bda9ae..a2e9cd60e 100644 --- a/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs +++ b/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs @@ -41,6 +41,7 @@ namespace MediaBrowser.Dlna.Main private readonly IDeviceDiscovery _deviceDiscovery; private readonly List _registeredServerIds = new List(); + private bool _ssdpHandlerStarted; private bool _dlnaServerStarted; public DlnaEntryPoint(IServerConfigurationManager config, @@ -78,12 +79,22 @@ namespace MediaBrowser.Dlna.Main public void Run() { - StartSsdpHandler(); ReloadComponents(); + _config.ConfigurationUpdated += _config_ConfigurationUpdated; _config.NamedConfigurationUpdated += _config_NamedConfigurationUpdated; } + private bool _lastEnableUpnP; + void _config_ConfigurationUpdated(object sender, EventArgs e) + { + if (_lastEnableUpnP != _config.Configuration.EnableUPnP) + { + ReloadComponents(); + } + _lastEnableUpnP = _config.Configuration.EnableUPnP; + } + void _config_NamedConfigurationUpdated(object sender, ConfigurationUpdateEventArgs e) { if (string.Equals(e.Key, "dlna", StringComparison.OrdinalIgnoreCase)) @@ -94,10 +105,27 @@ namespace MediaBrowser.Dlna.Main private void ReloadComponents() { - var isServerStarted = _dlnaServerStarted; - var options = _config.GetDlnaConfiguration(); + if (!options.EnableServer && !options.EnablePlayTo && !_config.Configuration.EnableUPnP) + { + if (_ssdpHandlerStarted) + { + // Sat/ip live tv depends on device discovery, as well as hd homerun detection + // In order to allow this to be disabled, we need a modular way of knowing if there are + // any parts of the system that are dependant on it + // DisposeSsdpHandler(); + } + return; + } + + if (!_ssdpHandlerStarted) + { + StartSsdpHandler(); + } + + var isServerStarted = _dlnaServerStarted; + if (options.EnableServer && !isServerStarted) { StartDlnaServer(); @@ -124,8 +152,9 @@ namespace MediaBrowser.Dlna.Main try { _ssdpHandler.Start(); + _ssdpHandlerStarted = true; - ((DeviceDiscovery)_deviceDiscovery).Start(_ssdpHandler); + StartDeviceDiscovery(); } catch (Exception ex) { @@ -133,6 +162,48 @@ namespace MediaBrowser.Dlna.Main } } + private void StartDeviceDiscovery() + { + try + { + ((DeviceDiscovery)_deviceDiscovery).Start(_ssdpHandler); + } + catch (Exception ex) + { + _logger.ErrorException("Error starting device discovery", ex); + } + } + + private void DisposeDeviceDiscovery() + { + try + { + ((DeviceDiscovery)_deviceDiscovery).Dispose(); + } + catch (Exception ex) + { + _logger.ErrorException("Error stopping device discovery", ex); + } + } + + private void DisposeSsdpHandler() + { + DisposeDeviceDiscovery(); + + try + { + ((DeviceDiscovery)_deviceDiscovery).Dispose(); + + _ssdpHandler.Dispose(); + + _ssdpHandlerStarted = false; + } + catch (Exception ex) + { + _logger.ErrorException("Error stopping ssdp handlers", ex); + } + } + public void StartDlnaServer() { try @@ -234,6 +305,7 @@ namespace MediaBrowser.Dlna.Main { DisposeDlnaServer(); DisposePlayToManager(); + DisposeSsdpHandler(); } public void DisposeDlnaServer() diff --git a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs index 0ea48fab6..d551d5c8c 100644 --- a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs @@ -1008,7 +1008,7 @@ namespace MediaBrowser.MediaEncoding.Encoder // Boost volume to 200% when downsampling from 6ch to 2ch if (channels.HasValue && channels.Value <= 2) { - if (state.AudioStream != null && state.AudioStream.Channels.HasValue && state.AudioStream.Channels.Value > 5) + if (state.AudioStream != null && state.AudioStream.Channels.HasValue && state.AudioStream.Channels.Value > 5 && !GetEncodingOptions().DownMixAudioBoost.Equals(1)) { volParam = ",volume=" + GetEncodingOptions().DownMixAudioBoost.ToString(UsCulture); } diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index c7c001cee..1a95bdf9d 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -432,6 +432,7 @@ namespace MediaBrowser.MediaEncoding.Probing { stream.Language = GetDictionaryValue(streamInfo.tags, "language"); stream.Comment = GetDictionaryValue(streamInfo.tags, "comment"); + stream.Title = GetDictionaryValue(streamInfo.tags, "title"); } if (string.Equals(streamInfo.codec_type, "audio", StringComparison.OrdinalIgnoreCase)) @@ -540,9 +541,24 @@ namespace MediaBrowser.MediaEncoding.Probing stream.IsForced = string.Equals(isForced, "1", StringComparison.OrdinalIgnoreCase); } + NormalizeStreamTitle(stream); + return stream; } + private void NormalizeStreamTitle(MediaStream stream) + { + if (string.Equals(stream.Title, "sdh", StringComparison.OrdinalIgnoreCase)) + { + stream.Title = null; + } + + if (stream.Type == MediaStreamType.EmbeddedImage) + { + stream.Title = null; + } + } + /// /// Gets a string from an FFProbeResult tags dictionary /// diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs index 25252956b..9b814c5cc 100644 --- a/MediaBrowser.Model/Entities/MediaStream.cs +++ b/MediaBrowser.Model/Entities/MediaStream.cs @@ -1,4 +1,6 @@ -using MediaBrowser.Model.Dlna; +using System; +using System.Collections.Generic; +using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Extensions; using System.Diagnostics; @@ -34,6 +36,91 @@ namespace MediaBrowser.Model.Entities /// The comment. public string Comment { get; set; } + public string Title { get; set; } + + public string DisplayTitle + { + get + { + if (!string.IsNullOrEmpty(Title)) + { + return Title; + } + + if (Type == MediaStreamType.Audio) + { + List attributes = new List(); + + if (!string.IsNullOrEmpty(Language)) + { + attributes.Add(Language); + } + if (!string.IsNullOrEmpty(Codec) && !StringHelper.EqualsIgnoreCase(Codec, "dca")) + { + attributes.Add(Codec); + } + if (!string.IsNullOrEmpty(Profile) && !StringHelper.EqualsIgnoreCase(Profile, "lc")) + { + attributes.Add(Profile); + } + + if (Channels.HasValue) + { + attributes.Add(StringHelper.ToStringCultureInvariant(Channels.Value) + " ch"); + } + + string name = string.Join(" ", attributes.ToArray()); + + if (IsDefault) + { + name += " (D)"; + } + + return name; + } + + if (Type == MediaStreamType.Subtitle) + { + List attributes = new List(); + + if (!string.IsNullOrEmpty(Language)) + { + attributes.Add(Language); + } + if (!string.IsNullOrEmpty(Codec)) + { + attributes.Add(Codec); + } + + string name = string.Join(" ", attributes.ToArray()); + + if (IsDefault) + { + name += " (D)"; + } + + if (IsForced) + { + name += " (F)"; + } + + if (IsExternal) + { + name += " (EXT)"; + } + + return name; + } + + if (Type == MediaStreamType.Video) + { + + } + + return null; + } + } + public string NalLengthSize { get; set; } /// diff --git a/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs b/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs index f402a0a33..83801b3e7 100644 --- a/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs +++ b/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs @@ -356,6 +356,11 @@ namespace MediaBrowser.Server.Implementations.FileOrganization private void SaveSmartMatchString(string matchString, Series series, AutoOrganizeOptions options) { + if (string.IsNullOrEmpty(matchString) || matchString.Length < 3) + { + return; + } + SmartMatchInfo info = options.SmartMatchInfos.FirstOrDefault(i => string.Equals(i.ItemName, series.Name, StringComparison.OrdinalIgnoreCase)); if (info == null) diff --git a/MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationService.cs b/MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationService.cs index 0e8a60612..60d515e12 100644 --- a/MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationService.cs +++ b/MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationService.cs @@ -95,7 +95,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization return _repo.Delete(resultId); } - private AutoOrganizeOptions GetAutoOrganizeptions() + private AutoOrganizeOptions GetAutoOrganizeOptions() { return _config.GetAutoOrganizeOptions(); } @@ -112,7 +112,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization var organizer = new EpisodeFileOrganizer(this, _config, _fileSystem, _logger, _libraryManager, _libraryMonitor, _providerManager); - await organizer.OrganizeEpisodeFile(result.OriginalPath, GetAutoOrganizeptions(), true, CancellationToken.None) + await organizer.OrganizeEpisodeFile(result.OriginalPath, GetAutoOrganizeOptions(), true, CancellationToken.None) .ConfigureAwait(false); } @@ -126,7 +126,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization var organizer = new EpisodeFileOrganizer(this, _config, _fileSystem, _logger, _libraryManager, _libraryMonitor, _providerManager); - await organizer.OrganizeWithCorrection(request, GetAutoOrganizeptions(), CancellationToken.None).ConfigureAwait(false); + await organizer.OrganizeWithCorrection(request, GetAutoOrganizeOptions(), CancellationToken.None).ConfigureAwait(false); } public QueryResult GetSmartMatchInfos(FileOrganizationResultQuery query) @@ -136,7 +136,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization throw new ArgumentNullException("query"); } - var options = GetAutoOrganizeptions(); + var options = GetAutoOrganizeOptions(); var items = options.SmartMatchInfos.Skip(query.StartIndex ?? 0).Take(query.Limit ?? Int32.MaxValue).ToArray(); @@ -159,7 +159,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization throw new ArgumentNullException("matchString"); } - var options = GetAutoOrganizeptions(); + var options = GetAutoOrganizeOptions(); SmartMatchInfo info = options.SmartMatchInfos.FirstOrDefault(i => string.Equals(i.ItemName, itemName)); diff --git a/MediaBrowser.Server.Implementations/Persistence/MediaStreamColumns.cs b/MediaBrowser.Server.Implementations/Persistence/MediaStreamColumns.cs index 76682c63b..948e99cb8 100644 --- a/MediaBrowser.Server.Implementations/Persistence/MediaStreamColumns.cs +++ b/MediaBrowser.Server.Implementations/Persistence/MediaStreamColumns.cs @@ -27,6 +27,7 @@ namespace MediaBrowser.Server.Implementations.Persistence AddCommentColumn(); AddNalColumn(); AddIsAvcColumn(); + AddTitleColumn(); } private void AddIsAvcColumn() @@ -60,6 +61,37 @@ namespace MediaBrowser.Server.Implementations.Persistence _connection.RunQueries(new[] { builder.ToString() }, _logger); } + private void AddTitleColumn() + { + using (var cmd = _connection.CreateCommand()) + { + cmd.CommandText = "PRAGMA table_info(mediastreams)"; + + using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult)) + { + while (reader.Read()) + { + if (!reader.IsDBNull(1)) + { + var name = reader.GetString(1); + + if (string.Equals(name, "Title", StringComparison.OrdinalIgnoreCase)) + { + return; + } + } + } + } + } + + var builder = new StringBuilder(); + + builder.AppendLine("alter table mediastreams"); + builder.AppendLine("add column Title TEXT"); + + _connection.RunQueries(new[] { builder.ToString() }, _logger); + } + private void AddNalColumn() { using (var cmd = _connection.CreateCommand()) diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs index 308ca90e0..8f7119b4a 100644 --- a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs @@ -120,7 +120,7 @@ namespace MediaBrowser.Server.Implementations.Persistence _connection = await dbConnector.Connect(dbFile).ConfigureAwait(false); var createMediaStreamsTableCommand - = "create table if not exists mediastreams (ItemId GUID, StreamIndex INT, StreamType TEXT, Codec TEXT, Language TEXT, ChannelLayout TEXT, Profile TEXT, AspectRatio TEXT, Path TEXT, IsInterlaced BIT, BitRate INT NULL, Channels INT NULL, SampleRate INT NULL, IsDefault BIT, IsForced BIT, IsExternal BIT, Height INT NULL, Width INT NULL, AverageFrameRate FLOAT NULL, RealFrameRate FLOAT NULL, Level FLOAT NULL, PixelFormat TEXT, BitDepth INT NULL, IsAnamorphic BIT NULL, RefFrames INT NULL, CodecTag TEXT NULL, Comment TEXT NULL, NalLengthSize TEXT NULL, IsAvc BIT NULL, PRIMARY KEY (ItemId, StreamIndex))"; + = "create table if not exists mediastreams (ItemId GUID, StreamIndex INT, StreamType TEXT, Codec TEXT, Language TEXT, ChannelLayout TEXT, Profile TEXT, AspectRatio TEXT, Path TEXT, IsInterlaced BIT, BitRate INT NULL, Channels INT NULL, SampleRate INT NULL, IsDefault BIT, IsForced BIT, IsExternal BIT, Height INT NULL, Width INT NULL, AverageFrameRate FLOAT NULL, RealFrameRate FLOAT NULL, Level FLOAT NULL, PixelFormat TEXT, BitDepth INT NULL, IsAnamorphic BIT NULL, RefFrames INT NULL, CodecTag TEXT NULL, Comment TEXT NULL, NalLengthSize TEXT NULL, IsAvc BIT NULL, Title TEXT NULL, PRIMARY KEY (ItemId, StreamIndex))"; string[] queries = { @@ -386,7 +386,8 @@ namespace MediaBrowser.Server.Implementations.Persistence "CodecTag", "Comment", "NalLengthSize", - "IsAvc" + "IsAvc", + "Title" }; /// @@ -3403,6 +3404,7 @@ namespace MediaBrowser.Server.Implementations.Persistence _saveStreamCommand.GetParameter(index++).Value = stream.Comment; _saveStreamCommand.GetParameter(index++).Value = stream.NalLengthSize; _saveStreamCommand.GetParameter(index++).Value = stream.IsAVC; + _saveStreamCommand.GetParameter(index++).Value = stream.Title; _saveStreamCommand.Transaction = transaction; _saveStreamCommand.ExecuteNonQuery(); @@ -3571,6 +3573,11 @@ namespace MediaBrowser.Server.Implementations.Persistence item.IsAVC = reader.GetBoolean(28); } + if (!reader.IsDBNull(29)) + { + item.Title = reader.GetString(29); + } + return item; }