From 74520804f80974ec018a6c45f4e221276ded4cd3 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 21 Dec 2014 13:58:17 -0500 Subject: start on content type setting --- .../Configuration/BaseConfigurationManager.cs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) (limited to 'MediaBrowser.Common.Implementations/Configuration') diff --git a/MediaBrowser.Common.Implementations/Configuration/BaseConfigurationManager.cs b/MediaBrowser.Common.Implementations/Configuration/BaseConfigurationManager.cs index 4a70697fa..a061420d7 100644 --- a/MediaBrowser.Common.Implementations/Configuration/BaseConfigurationManager.cs +++ b/MediaBrowser.Common.Implementations/Configuration/BaseConfigurationManager.cs @@ -208,11 +208,29 @@ namespace MediaBrowser.Common.Implementations.Configuration lock (_configurationSyncLock) { - return ConfigurationHelper.GetXmlConfiguration(configurationType, file, XmlSerializer); + return LoadConfiguration(file, configurationType); } }); } + private object LoadConfiguration(string path, Type configurationType) + { + try + { + return XmlSerializer.DeserializeFromFile(configurationType, path); + } + catch (FileNotFoundException) + { + return Activator.CreateInstance(configurationType); + } + catch (Exception ex) + { + Logger.ErrorException("Error loading configuration file: {0}", ex, path); + + return Activator.CreateInstance(configurationType); + } + } + public void SaveConfiguration(string key, object configuration) { var configurationType = GetConfigurationType(key); -- cgit v1.2.3 From 97ae93fe5eb0f010db9835efd72954f31ccdd2cd Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 21 Dec 2014 14:40:37 -0500 Subject: add standalone EncodingOptions --- MediaBrowser.Api/ApiEntryPoint.cs | 19 +++-- MediaBrowser.Api/Playback/BaseStreamingService.cs | 11 +-- .../Configuration/BaseConfigurationManager.cs | 20 +++++- .../Configuration/IConfigurationFactory.cs | 5 ++ .../MediaBrowser.Controller.csproj | 3 - .../MediaEncoding/EncodingOptions.cs | 80 ---------------------- .../MediaEncoding/EncodingResult.cs | 13 ---- .../MediaEncoding/VideoEncodingOptions.cs | 26 ------- .../Configuration/EncodingConfigurationFactory.cs | 45 ++++++++++++ .../MediaBrowser.MediaEncoding.csproj | 1 + .../MediaBrowser.Model.Portable.csproj | 3 + .../MediaBrowser.Model.net35.csproj | 3 + .../Configuration/EncodingOptions.cs | 19 +++++ .../Configuration/ServerConfiguration.cs | 13 ---- MediaBrowser.Model/MediaBrowser.Model.csproj | 1 + .../Configuration/ServerConfigurationManager.cs | 33 --------- 16 files changed, 113 insertions(+), 182 deletions(-) delete mode 100644 MediaBrowser.Controller/MediaEncoding/EncodingOptions.cs delete mode 100644 MediaBrowser.Controller/MediaEncoding/EncodingResult.cs delete mode 100644 MediaBrowser.Controller/MediaEncoding/VideoEncodingOptions.cs create mode 100644 MediaBrowser.MediaEncoding/Configuration/EncodingConfigurationFactory.cs create mode 100644 MediaBrowser.Model/Configuration/EncodingOptions.cs (limited to 'MediaBrowser.Common.Implementations/Configuration') diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs index 95f7ef694..a05d7d1b2 100644 --- a/MediaBrowser.Api/ApiEntryPoint.cs +++ b/MediaBrowser.Api/ApiEntryPoint.cs @@ -1,7 +1,9 @@ using MediaBrowser.Api.Playback; -using MediaBrowser.Controller; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Session; +using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Session; using System; @@ -33,7 +35,7 @@ namespace MediaBrowser.Api /// /// The application paths /// - private readonly IServerApplicationPaths _appPaths; + private readonly IServerConfigurationManager _config; private readonly ISessionManager _sessionManager; @@ -43,13 +45,13 @@ namespace MediaBrowser.Api /// Initializes a new instance of the class. /// /// The logger. - /// The application paths. /// The session manager. - public ApiEntryPoint(ILogger logger, IServerApplicationPaths appPaths, ISessionManager sessionManager) + /// The configuration. + public ApiEntryPoint(ILogger logger, ISessionManager sessionManager, IServerConfigurationManager config) { Logger = logger; - _appPaths = appPaths; _sessionManager = sessionManager; + _config = config; Instance = this; } @@ -73,12 +75,17 @@ namespace MediaBrowser.Api } } + public EncodingOptions GetEncodingOptions() + { + return _config.GetConfiguration("encoding"); + } + /// /// Deletes the encoded media cache. /// private void DeleteEncodedMediaCache() { - foreach (var file in Directory.EnumerateFiles(_appPaths.TranscodingTempPath, "*", SearchOption.AllDirectories) + foreach (var file in Directory.EnumerateFiles(_config.ApplicationPaths.TranscodingTempPath, "*", SearchOption.AllDirectories) .ToList()) { File.Delete(file); diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 203a62dfc..683a1f962 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Extensions; using MediaBrowser.Common.IO; using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Configuration; @@ -249,7 +250,7 @@ namespace MediaBrowser.Api.Playback protected EncodingQuality GetQualitySetting() { - var quality = ServerConfigurationManager.Configuration.MediaEncodingQuality; + var quality = ApiEntryPoint.Instance.GetEncodingOptions().MediaEncodingQuality; if (quality == EncodingQuality.Auto) { @@ -310,7 +311,7 @@ namespace MediaBrowser.Api.Playback { get { - var lib = ServerConfigurationManager.Configuration.H264Encoder; + var lib = ApiEntryPoint.Instance.GetEncodingOptions().H264Encoder; if (!string.IsNullOrWhiteSpace(lib)) { @@ -461,7 +462,7 @@ namespace MediaBrowser.Api.Playback { if (state.AudioStream != null && state.AudioStream.Channels.HasValue && state.AudioStream.Channels.Value > 5) { - volParam = ",volume=" + ServerConfigurationManager.Configuration.DownMixAudioBoost.ToString(UsCulture); + volParam = ",volume=" + ApiEntryPoint.Instance.GetEncodingOptions().DownMixAudioBoost.ToString(UsCulture); } } @@ -953,7 +954,7 @@ namespace MediaBrowser.Api.Playback var transcodingId = Guid.NewGuid().ToString("N"); var commandLineArgs = GetCommandLineArguments(outputPath, transcodingId, state, true); - if (ServerConfigurationManager.Configuration.EnableDebugEncodingLogging) + if (ApiEntryPoint.Instance.GetEncodingOptions().EnableDebugLogging) { commandLineArgs = "-loglevel debug " + commandLineArgs; } diff --git a/MediaBrowser.Common.Implementations/Configuration/BaseConfigurationManager.cs b/MediaBrowser.Common.Implementations/Configuration/BaseConfigurationManager.cs index a061420d7..093010124 100644 --- a/MediaBrowser.Common.Implementations/Configuration/BaseConfigurationManager.cs +++ b/MediaBrowser.Common.Implementations/Configuration/BaseConfigurationManager.cs @@ -233,13 +233,22 @@ namespace MediaBrowser.Common.Implementations.Configuration public void SaveConfiguration(string key, object configuration) { - var configurationType = GetConfigurationType(key); + var configurationStore = GetConfigurationStore(key); + var configurationType = configurationStore.ConfigurationType; if (configuration.GetType() != configurationType) { throw new ArgumentException("Expected configuration type is " + configurationType.Name); } + var validatingStore = configurationStore as IValidatingConfiguration; + if (validatingStore != null) + { + var currentConfiguration = GetConfiguration(key); + + validatingStore.Validate(currentConfiguration, configuration); + } + EventHelper.FireEventIfNotNull(NamedConfigurationUpdating, this, new ConfigurationUpdateEventArgs { Key = key, @@ -267,9 +276,14 @@ namespace MediaBrowser.Common.Implementations.Configuration public Type GetConfigurationType(string key) { - return _configurationStores - .First(i => string.Equals(i.Key, key, StringComparison.OrdinalIgnoreCase)) + return GetConfigurationStore(key) .ConfigurationType; } + + private ConfigurationStore GetConfigurationStore(string key) + { + return _configurationStores + .First(i => string.Equals(i.Key, key, StringComparison.OrdinalIgnoreCase)); + } } } diff --git a/MediaBrowser.Common/Configuration/IConfigurationFactory.cs b/MediaBrowser.Common/Configuration/IConfigurationFactory.cs index d418d0a42..6ed638536 100644 --- a/MediaBrowser.Common/Configuration/IConfigurationFactory.cs +++ b/MediaBrowser.Common/Configuration/IConfigurationFactory.cs @@ -14,4 +14,9 @@ namespace MediaBrowser.Common.Configuration public Type ConfigurationType { get; set; } } + + public interface IValidatingConfiguration + { + void Validate(object oldConfig, object newConfig); + } } diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index c198a58d4..3b9f3a5b2 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -198,16 +198,13 @@ - - - diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingOptions.cs b/MediaBrowser.Controller/MediaEncoding/EncodingOptions.cs deleted file mode 100644 index 26182ebc4..000000000 --- a/MediaBrowser.Controller/MediaEncoding/EncodingOptions.cs +++ /dev/null @@ -1,80 +0,0 @@ -using MediaBrowser.Controller.Dlna; -using MediaBrowser.Model.Dlna; - -namespace MediaBrowser.Controller.MediaEncoding -{ - public class EncodingOptions - { - /// - /// Gets or sets the item identifier. - /// - /// The item identifier. - public string ItemId { get; set; } - - /// - /// Gets or sets the media source identifier. - /// - /// The media source identifier. - public string MediaSourceId { get; set; } - - /// - /// Gets or sets the device profile. - /// - /// The device profile. - public DeviceProfile DeviceProfile { get; set; } - - /// - /// Gets or sets the output path. - /// - /// The output path. - public string OutputPath { get; set; } - - /// - /// Gets or sets the container. - /// - /// The container. - public string Container { get; set; } - - /// - /// Gets or sets the audio codec. - /// - /// The audio codec. - public string AudioCodec { get; set; } - - /// - /// Gets or sets the start time ticks. - /// - /// The start time ticks. - public long? StartTimeTicks { get; set; } - - /// - /// Gets or sets the maximum channels. - /// - /// The maximum channels. - public int? MaxAudioChannels { get; set; } - - /// - /// Gets or sets the channels. - /// - /// The channels. - public int? AudioChannels { get; set; } - - /// - /// Gets or sets the sample rate. - /// - /// The sample rate. - public int? AudioSampleRate { get; set; } - - /// - /// Gets or sets the bit rate. - /// - /// The bit rate. - public int? AudioBitRate { get; set; } - - /// - /// Gets or sets the maximum audio bit rate. - /// - /// The maximum audio bit rate. - public int? MaxAudioBitRate { get; set; } - } -} diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingResult.cs b/MediaBrowser.Controller/MediaEncoding/EncodingResult.cs deleted file mode 100644 index 75ee90e42..000000000 --- a/MediaBrowser.Controller/MediaEncoding/EncodingResult.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace MediaBrowser.Controller.MediaEncoding -{ - public class EncodingResult - { - public string OutputPath { get; set; } - } -} diff --git a/MediaBrowser.Controller/MediaEncoding/VideoEncodingOptions.cs b/MediaBrowser.Controller/MediaEncoding/VideoEncodingOptions.cs deleted file mode 100644 index 773f0ea46..000000000 --- a/MediaBrowser.Controller/MediaEncoding/VideoEncodingOptions.cs +++ /dev/null @@ -1,26 +0,0 @@ - -namespace MediaBrowser.Controller.MediaEncoding -{ - public class VideoEncodingOptions : EncodingOptions - { - public string VideoCodec { get; set; } - - public string VideoProfile { get; set; } - - public double? VideoLevel { get; set; } - - public int? VideoStreamIndex { get; set; } - - public int? AudioStreamIndex { get; set; } - - public int? SubtitleStreamIndex { get; set; } - - public int? MaxWidth { get; set; } - - public int? MaxHeight { get; set; } - - public int? Height { get; set; } - - public int? Width { get; set; } - } -} diff --git a/MediaBrowser.MediaEncoding/Configuration/EncodingConfigurationFactory.cs b/MediaBrowser.MediaEncoding/Configuration/EncodingConfigurationFactory.cs new file mode 100644 index 000000000..17470d206 --- /dev/null +++ b/MediaBrowser.MediaEncoding/Configuration/EncodingConfigurationFactory.cs @@ -0,0 +1,45 @@ +using MediaBrowser.Common.Configuration; +using MediaBrowser.Model.Configuration; +using System.Collections.Generic; +using System.IO; + +namespace MediaBrowser.MediaEncoding.Configuration +{ + public class EncodingConfigurationFactory : IConfigurationFactory + { + public IEnumerable GetConfigurations() + { + return new[] + { + new EncodingConfigurationStore() + }; + } + } + + public class EncodingConfigurationStore : ConfigurationStore, IValidatingConfiguration + { + public EncodingConfigurationStore() + { + ConfigurationType = typeof(EncodingOptions); + Key = "encoding"; + } + + public void Validate(object oldConfig, object newConfig) + { + var oldEncodingConfig = (EncodingOptions)oldConfig; + var newEncodingConfig = (EncodingOptions)newConfig; + + var newPath = newEncodingConfig.TranscodingTempPath; + + if (!string.IsNullOrWhiteSpace(newPath) + && !string.Equals(oldEncodingConfig.TranscodingTempPath ?? string.Empty, newPath)) + { + // Validate + if (!Directory.Exists(newPath)) + { + throw new DirectoryNotFoundException(string.Format("{0} does not exist.", newPath)); + } + } + } + } +} diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj index 6f59b7bec..5c472ebc8 100644 --- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj +++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj @@ -56,6 +56,7 @@ Properties\SharedVersion.cs + diff --git a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj index f18c53cd3..9900484cd 100644 --- a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj +++ b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj @@ -188,6 +188,9 @@ Configuration\DynamicDayOfWeek.cs + + Configuration\EncodingOptions.cs + Configuration\EncodingQuality.cs diff --git a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj index f17215988..bd98b5b86 100644 --- a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj +++ b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj @@ -153,6 +153,9 @@ Configuration\DynamicDayOfWeek.cs + + Configuration\EncodingOptions.cs + Configuration\EncodingQuality.cs diff --git a/MediaBrowser.Model/Configuration/EncodingOptions.cs b/MediaBrowser.Model/Configuration/EncodingOptions.cs new file mode 100644 index 000000000..f24367298 --- /dev/null +++ b/MediaBrowser.Model/Configuration/EncodingOptions.cs @@ -0,0 +1,19 @@ + +namespace MediaBrowser.Model.Configuration +{ + public class EncodingOptions + { + public EncodingQuality EncodingQuality { get; set; } + public string TranscodingTempPath { get; set; } + public double DownMixAudioBoost { get; set; } + public string H264Encoder { get; set; } + public bool EnableDebugLogging { get; set; } + + public EncodingOptions() + { + H264Encoder = "libx264"; + DownMixAudioBoost = 2; + EncodingQuality = EncodingQuality.Auto; + } + } +} diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index 59dd04f33..0852e0a5c 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -144,15 +144,8 @@ namespace MediaBrowser.Model.Configuration /// The image saving convention. public ImageSavingConvention ImageSavingConvention { get; set; } - /// - /// Gets or sets the encoding quality. - /// - /// The encoding quality. - public EncodingQuality MediaEncodingQuality { get; set; } - public MetadataOptions[] MetadataOptions { get; set; } - public bool EnableDebugEncodingLogging { get; set; } public string TranscodingTempPath { get; set; } public bool EnableAutomaticRestart { get; set; } @@ -165,15 +158,12 @@ namespace MediaBrowser.Model.Configuration public string UICulture { get; set; } - public double DownMixAudioBoost { get; set; } - public PeopleMetadataOptions PeopleMetadataOptions { get; set; } public bool FindInternetTrailers { get; set; } public string[] InsecureApps7 { get; set; } public bool SaveMetadataHidden { get; set; } - public string H264Encoder { get; set; } /// /// Initializes a new instance of the class. @@ -181,7 +171,6 @@ namespace MediaBrowser.Model.Configuration public ServerConfiguration() : base() { - MediaEncodingQuality = EncodingQuality.Auto; ImageSavingConvention = ImageSavingConvention.Compatible; PublicPort = 8096; HttpServerPortNumber = 8096; @@ -190,7 +179,6 @@ namespace MediaBrowser.Model.Configuration EnableAutomaticRestart = true; EnableUPnP = true; - DownMixAudioBoost = 2; MinResumePct = 5; MaxResumePct = 90; @@ -217,7 +205,6 @@ namespace MediaBrowser.Model.Configuration EnableRealtimeMonitor = true; UICulture = "en-us"; - H264Encoder = "libx264"; PeopleMetadataOptions = new PeopleMetadataOptions(); diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index 759c671e9..e1f0e78f4 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -96,6 +96,7 @@ + diff --git a/MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs b/MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs index b9896e9ce..704bdea29 100644 --- a/MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs +++ b/MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs @@ -32,7 +32,6 @@ namespace MediaBrowser.Server.Implementations.Configuration : base(applicationPaths, logManager, xmlSerializer) { UpdateItemsByNamePath(); - UpdateTranscodingTempPath(); UpdateMetadataPath(); } @@ -71,7 +70,6 @@ namespace MediaBrowser.Server.Implementations.Configuration protected override void OnConfigurationUpdated() { UpdateItemsByNamePath(); - UpdateTranscodingTempPath(); UpdateMetadataPath(); base.OnConfigurationUpdated(); @@ -97,16 +95,6 @@ namespace MediaBrowser.Server.Implementations.Configuration Configuration.MetadataPath; } - /// - /// Updates the transcoding temporary path. - /// - private void UpdateTranscodingTempPath() - { - ((ServerApplicationPaths)ApplicationPaths).TranscodingTempPath = string.IsNullOrEmpty(Configuration.TranscodingTempPath) ? - null : - Configuration.TranscodingTempPath; - } - /// /// Replaces the configuration. /// @@ -117,7 +105,6 @@ namespace MediaBrowser.Server.Implementations.Configuration var newConfig = (ServerConfiguration)newConfiguration; ValidateItemByNamePath(newConfig); - ValidateTranscodingTempPath(newConfig); ValidatePathSubstitutions(newConfig); ValidateMetadataPath(newConfig); @@ -157,26 +144,6 @@ namespace MediaBrowser.Server.Implementations.Configuration } } - /// - /// Validates the transcoding temporary path. - /// - /// The new configuration. - /// - private void ValidateTranscodingTempPath(ServerConfiguration newConfig) - { - var newPath = newConfig.TranscodingTempPath; - - if (!string.IsNullOrWhiteSpace(newPath) - && !string.Equals(Configuration.TranscodingTempPath ?? string.Empty, newPath)) - { - // Validate - if (!Directory.Exists(newPath)) - { - throw new DirectoryNotFoundException(string.Format("{0} does not exist.", newPath)); - } - } - } - /// /// Validates the metadata path. /// -- cgit v1.2.3 From 98ae564226115eeb41cc01b54bb6c9a1707ece3b Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 21 Dec 2014 20:02:15 -0500 Subject: add transcode temp path migration --- MediaBrowser.Api/Playback/BaseStreamingService.cs | 2 +- .../Configuration/BaseConfigurationManager.cs | 7 +++- .../Configuration/ServerConfigurationManager.cs | 32 +++++++++++++++++- .../ApplicationHost.cs | 39 +++++++++++++++------- .../MediaBrowser.Server.Startup.Common.csproj | 1 + .../Migrations/MigrateTranscodingPath.cs | 30 +++++++++++++++++ 6 files changed, 96 insertions(+), 15 deletions(-) create mode 100644 MediaBrowser.Server.Startup.Common/Migrations/MigrateTranscodingPath.cs (limited to 'MediaBrowser.Common.Implementations/Configuration') diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 683a1f962..7b18fb379 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -250,7 +250,7 @@ namespace MediaBrowser.Api.Playback protected EncodingQuality GetQualitySetting() { - var quality = ApiEntryPoint.Instance.GetEncodingOptions().MediaEncodingQuality; + var quality = ApiEntryPoint.Instance.GetEncodingOptions().EncodingQuality; if (quality == EncodingQuality.Auto) { diff --git a/MediaBrowser.Common.Implementations/Configuration/BaseConfigurationManager.cs b/MediaBrowser.Common.Implementations/Configuration/BaseConfigurationManager.cs index 093010124..89f6229ac 100644 --- a/MediaBrowser.Common.Implementations/Configuration/BaseConfigurationManager.cs +++ b/MediaBrowser.Common.Implementations/Configuration/BaseConfigurationManager.cs @@ -105,7 +105,7 @@ namespace MediaBrowser.Common.Implementations.Configuration UpdateCachePath(); } - public void AddParts(IEnumerable factories) + public virtual void AddParts(IEnumerable factories) { _configurationFactories = factories.ToArray(); @@ -266,6 +266,11 @@ namespace MediaBrowser.Common.Implementations.Configuration XmlSerializer.SerializeToFile(configuration, path); } + OnNamedConfigurationUpdated(key, configuration); + } + + protected virtual void OnNamedConfigurationUpdated(string key, object configuration) + { EventHelper.FireEventIfNotNull(NamedConfigurationUpdated, this, new ConfigurationUpdateEventArgs { Key = key, diff --git a/MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs b/MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs index 704bdea29..b3b8ccbd8 100644 --- a/MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs +++ b/MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Common.Configuration; +using System.Collections.Generic; +using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Events; using MediaBrowser.Common.Implementations.Configuration; using MediaBrowser.Controller; @@ -75,6 +76,13 @@ namespace MediaBrowser.Server.Implementations.Configuration base.OnConfigurationUpdated(); } + public override void AddParts(IEnumerable factories) + { + base.AddParts(factories); + + UpdateTranscodingTempPath(); + } + /// /// Updates the items by name path. /// @@ -95,6 +103,28 @@ namespace MediaBrowser.Server.Implementations.Configuration Configuration.MetadataPath; } + /// + /// Updates the transcoding temporary path. + /// + private void UpdateTranscodingTempPath() + { + var encodingConfig = this.GetConfiguration("encoding"); + + ((ServerApplicationPaths)ApplicationPaths).TranscodingTempPath = string.IsNullOrEmpty(encodingConfig.TranscodingTempPath) ? + null : + encodingConfig.TranscodingTempPath; + } + + protected override void OnNamedConfigurationUpdated(string key, object configuration) + { + base.OnNamedConfigurationUpdated(key, configuration); + + if (string.Equals(key, "encoding", StringComparison.OrdinalIgnoreCase)) + { + UpdateTranscodingTempPath(); + } + } + /// /// Replaces the configuration. /// diff --git a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs index 2af33c311..c2520ff6b 100644 --- a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs +++ b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs @@ -238,12 +238,12 @@ namespace MediaBrowser.Server.Startup.Common /// Name of the remote package. /// if set to true [supports native web socket]. /// The native application. - public ApplicationHost(ServerApplicationPaths applicationPaths, - ILogManager logManager, - StartupOptions options, + public ApplicationHost(ServerApplicationPaths applicationPaths, + ILogManager logManager, + StartupOptions options, IFileSystem fileSystem, - string remotePackageName, - bool supportsNativeWebSocket, + string remotePackageName, + bool supportsNativeWebSocket, INativeApp nativeApp) : base(applicationPaths, logManager, fileSystem) { @@ -353,12 +353,14 @@ namespace MediaBrowser.Server.Startup.Common public override async Task Init(IProgress progress) { - PerformVersionMigration(); + PerformPreInitMigrations(); await base.Init(progress).ConfigureAwait(false); + + PerformPostInitMigrations(); } - private void PerformVersionMigration() + private void PerformPreInitMigrations() { var migrations = new List { @@ -375,6 +377,19 @@ namespace MediaBrowser.Server.Startup.Common } } + private void PerformPostInitMigrations() + { + var migrations = new List + { + new MigrateTranscodingPath(ServerConfigurationManager) + }; + + foreach (var task in migrations) + { + task.Run(); + } + } + /// /// Registers resources that classes will depend on /// @@ -383,7 +398,7 @@ namespace MediaBrowser.Server.Startup.Common { await base.RegisterResources(progress).ConfigureAwait(false); - RegisterSingleInstance(new HttpResultFactory(LogManager, FileSystemManager, JsonSerializer)); + RegisterSingleInstance(new HttpResultFactory(LogManager, FileSystemManager, JsonSerializer)); RegisterSingleInstance(this); RegisterSingleInstance(ApplicationPaths); @@ -398,7 +413,7 @@ namespace MediaBrowser.Server.Startup.Common UserDataManager = new UserDataManager(LogManager); RegisterSingleInstance(UserDataManager); - UserRepository = await GetUserRepository().ConfigureAwait(false); + UserRepository = await GetUserRepository().ConfigureAwait(false); RegisterSingleInstance(UserRepository); DisplayPreferencesRepository = new SqliteDisplayPreferencesRepository(ApplicationPaths, JsonSerializer, LogManager); @@ -439,7 +454,7 @@ namespace MediaBrowser.Server.Startup.Common RegisterSingleInstance(() => new SearchEngine(LogManager, LibraryManager, UserManager)); - HttpServer = ServerFactory.CreateServer(this, LogManager, "Media Browser", WebApplicationName, "dashboard/index.html", _supportsNativeWebSocket); + HttpServer = ServerFactory.CreateServer(this, LogManager, "Media Browser", WebApplicationName, "dashboard/index.html", _supportsNativeWebSocket); RegisterSingleInstance(HttpServer, false); progress.Report(10); @@ -533,12 +548,12 @@ namespace MediaBrowser.Server.Startup.Common RegisterSingleInstance(new SessionContext(UserManager, authContext, SessionManager)); RegisterSingleInstance(new AuthService(UserManager, authContext, ServerConfigurationManager, ConnectManager, SessionManager)); - RegisterSingleInstance(new SubtitleEncoder(LibraryManager, LogManager.GetLogger("SubtitleEncoder"), ApplicationPaths, FileSystemManager, MediaEncoder, JsonSerializer)); + RegisterSingleInstance(new SubtitleEncoder(LibraryManager, LogManager.GetLogger("SubtitleEncoder"), ApplicationPaths, FileSystemManager, MediaEncoder, JsonSerializer)); await ConfigureDisplayPreferencesRepositories().ConfigureAwait(false); await ConfigureItemRepositories().ConfigureAwait(false); await ConfigureUserDataRepositories().ConfigureAwait(false); - await ConfigureNotificationsRepository().ConfigureAwait(false); + await ConfigureNotificationsRepository().ConfigureAwait(false); progress.Report(100); SetStaticProperties(); diff --git a/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj b/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj index ab53103e8..38e07fde4 100644 --- a/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj +++ b/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj @@ -66,6 +66,7 @@ + diff --git a/MediaBrowser.Server.Startup.Common/Migrations/MigrateTranscodingPath.cs b/MediaBrowser.Server.Startup.Common/Migrations/MigrateTranscodingPath.cs new file mode 100644 index 000000000..88f60841d --- /dev/null +++ b/MediaBrowser.Server.Startup.Common/Migrations/MigrateTranscodingPath.cs @@ -0,0 +1,30 @@ +using MediaBrowser.Common.Configuration; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Model.Configuration; + +namespace MediaBrowser.Server.Startup.Common.Migrations +{ + public class MigrateTranscodingPath : IVersionMigration + { + private readonly IServerConfigurationManager _config; + + public MigrateTranscodingPath(IServerConfigurationManager config) + { + _config = config; + } + + public void Run() + { + if (!string.IsNullOrWhiteSpace(_config.Configuration.TranscodingTempPath)) + { + var newConfig = _config.GetConfiguration("encoding"); + + newConfig.TranscodingTempPath = _config.Configuration.TranscodingTempPath; + _config.SaveConfiguration("encoding", newConfig); + + _config.Configuration.TranscodingTempPath = null; + _config.SaveConfiguration(); + } + } + } +} -- cgit v1.2.3 From 42b14166029d5251e952b72f5c16cd9ae96aa8cb Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Mon, 22 Dec 2014 22:58:14 -0500 Subject: begin work on daily episodes --- MediaBrowser.Api/ItemUpdateService.cs | 3 +- .../Configuration/BaseConfigurationManager.cs | 4 + .../Devices/DeviceId.cs | 5 +- .../Security/MBLicenseFile.cs | 4 + MediaBrowser.Common/Extensions/BaseExtensions.cs | 11 +++ MediaBrowser.Common/Plugins/BasePlugin.cs | 4 + MediaBrowser.Controller/Entities/TV/Episode.cs | 11 ++- MediaBrowser.Controller/Entities/TV/Season.cs | 78 ++++++++++++------- MediaBrowser.Controller/Library/IUserManager.cs | 7 ++ MediaBrowser.LocalMetadata/BaseXmlProvider.cs | 6 -- .../Subtitles/SubtitleEncoder.cs | 4 + MediaBrowser.Model/ApiClient/IApiClient.cs | 18 ----- .../Movies/FanartMovieImageProvider.cs | 4 + .../Music/FanArtAlbumProvider.cs | 4 + .../Music/FanArtArtistProvider.cs | 4 + .../People/TvdbPersonImageProvider.cs | 4 + MediaBrowser.Providers/TV/FanArtSeasonProvider.cs | 4 + MediaBrowser.Providers/TV/FanartSeriesProvider.cs | 4 + .../TV/MissingEpisodeProvider.cs | 63 +++++++++------- MediaBrowser.Providers/TV/SeriesPostScanTask.cs | 11 ++- MediaBrowser.Providers/TV/TvdbEpisodeProvider.cs | 15 +++- .../TV/TvdbSeasonImageProvider.cs | 4 + .../TV/TvdbSeriesImageProvider.cs | 4 + .../Connect/ConnectManager.cs | 3 +- .../Drawing/ImageProcessor.cs | 5 ++ .../FileOrganization/EpisodeFileOrganizer.cs | 10 ++- .../Library/LibraryManager.cs | 88 ++++++++++++++++++---- .../Library/Resolvers/TV/EpisodeResolver.cs | 2 - .../Library/UserManager.cs | 68 +++++++++++++---- .../Localization/JavaScript/javascript.json | 4 +- .../Localization/Server/server.json | 9 ++- .../News/NewsService.cs | 8 ++ MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs | 4 + 33 files changed, 349 insertions(+), 128 deletions(-) (limited to 'MediaBrowser.Common.Implementations/Configuration') diff --git a/MediaBrowser.Api/ItemUpdateService.cs b/MediaBrowser.Api/ItemUpdateService.cs index 020908ddd..272dff3ec 100644 --- a/MediaBrowser.Api/ItemUpdateService.cs +++ b/MediaBrowser.Api/ItemUpdateService.cs @@ -3,6 +3,7 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.Localization; using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Providers; @@ -73,7 +74,7 @@ namespace MediaBrowser.Api if (locationType == LocationType.FileSystem || locationType == LocationType.Offline) { - if (!(item is ICollectionFolder) && !(item is UserView) && !(item is AggregateFolder)) + if (!(item is ICollectionFolder) && !(item is UserView) && !(item is AggregateFolder) && !(item is LiveTvChannel) && !(item is IItemByName)) { var collectionType = _libraryManager.GetInheritedContentType(item); if (string.IsNullOrWhiteSpace(collectionType)) diff --git a/MediaBrowser.Common.Implementations/Configuration/BaseConfigurationManager.cs b/MediaBrowser.Common.Implementations/Configuration/BaseConfigurationManager.cs index 89f6229ac..c53947e44 100644 --- a/MediaBrowser.Common.Implementations/Configuration/BaseConfigurationManager.cs +++ b/MediaBrowser.Common.Implementations/Configuration/BaseConfigurationManager.cs @@ -223,6 +223,10 @@ namespace MediaBrowser.Common.Implementations.Configuration { return Activator.CreateInstance(configurationType); } + catch (DirectoryNotFoundException) + { + return Activator.CreateInstance(configurationType); + } catch (Exception ex) { Logger.ErrorException("Error loading configuration file: {0}", ex, path); diff --git a/MediaBrowser.Common.Implementations/Devices/DeviceId.cs b/MediaBrowser.Common.Implementations/Devices/DeviceId.cs index 5af236026..7c0dc1e1f 100644 --- a/MediaBrowser.Common.Implementations/Devices/DeviceId.cs +++ b/MediaBrowser.Common.Implementations/Devices/DeviceId.cs @@ -38,7 +38,10 @@ namespace MediaBrowser.Common.Implementations.Devices _logger.Error("Invalid value found in device id file"); } } - catch (FileNotFoundException ex) + catch (DirectoryNotFoundException) + { + } + catch (FileNotFoundException) { } catch (Exception ex) diff --git a/MediaBrowser.Common.Implementations/Security/MBLicenseFile.cs b/MediaBrowser.Common.Implementations/Security/MBLicenseFile.cs index 8f3225f4e..63381efcd 100644 --- a/MediaBrowser.Common.Implementations/Security/MBLicenseFile.cs +++ b/MediaBrowser.Common.Implementations/Security/MBLicenseFile.cs @@ -101,6 +101,10 @@ namespace MediaBrowser.Common.Implementations.Security { contents = File.ReadAllLines(licenseFile); } + catch (DirectoryNotFoundException) + { + (File.Create(licenseFile)).Close(); + } catch (FileNotFoundException) { (File.Create(licenseFile)).Close(); diff --git a/MediaBrowser.Common/Extensions/BaseExtensions.cs b/MediaBrowser.Common/Extensions/BaseExtensions.cs index 8e96373f4..4c94f3aa2 100644 --- a/MediaBrowser.Common/Extensions/BaseExtensions.cs +++ b/MediaBrowser.Common/Extensions/BaseExtensions.cs @@ -1,4 +1,6 @@ using System; +using System.Globalization; +using System.Linq; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; @@ -54,6 +56,15 @@ namespace MediaBrowser.Common.Extensions return sb.ToString(); } + public static string RemoveDiacritics(this string text) + { + return String.Concat( + text.Normalize(NormalizationForm.FormD) + .Where(ch => CharUnicodeInfo.GetUnicodeCategory(ch) != + UnicodeCategory.NonSpacingMark) + ).Normalize(NormalizationForm.FormC); + } + /// /// Gets the M d5. /// diff --git a/MediaBrowser.Common/Plugins/BasePlugin.cs b/MediaBrowser.Common/Plugins/BasePlugin.cs index 1a536b4ff..ce068463e 100644 --- a/MediaBrowser.Common/Plugins/BasePlugin.cs +++ b/MediaBrowser.Common/Plugins/BasePlugin.cs @@ -204,6 +204,10 @@ namespace MediaBrowser.Common.Plugins { return (TConfigurationType)XmlSerializer.DeserializeFromFile(typeof(TConfigurationType), path); } + catch (DirectoryNotFoundException) + { + return (TConfigurationType)Activator.CreateInstance(typeof(TConfigurationType)); + } catch (FileNotFoundException) { return (TConfigurationType)Activator.CreateInstance(typeof(TConfigurationType)); diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs index 6b6f07d4c..6b67cebc8 100644 --- a/MediaBrowser.Controller/Entities/TV/Episode.cs +++ b/MediaBrowser.Controller/Entities/TV/Episode.cs @@ -1,11 +1,11 @@ using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Users; using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; -using MediaBrowser.Model.Users; namespace MediaBrowser.Controller.Entities.TV { @@ -178,6 +178,15 @@ namespace MediaBrowser.Controller.Entities.TV } } + [IgnoreDataMember] + public bool IsInSeasonFolder + { + get + { + return FindParent() != null; + } + } + [IgnoreDataMember] public string SeriesName { diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs index ceddbbc3b..54db12b6f 100644 --- a/MediaBrowser.Controller/Entities/TV/Season.cs +++ b/MediaBrowser.Controller/Entities/TV/Season.cs @@ -1,13 +1,12 @@ -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Localization; +using MediaBrowser.Controller.Localization; using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Querying; +using MediaBrowser.Model.Users; +using MoreLinq; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; -using MediaBrowser.Model.Users; namespace MediaBrowser.Controller.Entities.TV { @@ -156,24 +155,6 @@ namespace MediaBrowser.Controller.Entities.TV return IndexNumber != null ? IndexNumber.Value.ToString("0000") : Name; } - private IEnumerable GetEpisodes() - { - var series = Series; - - if (series != null && series.ContainsEpisodesWithoutSeasonFolders) - { - var seasonNumber = IndexNumber; - - if (seasonNumber.HasValue) - { - return series.RecursiveChildren.OfType() - .Where(i => i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == seasonNumber.Value); - } - } - - return Children.OfType(); - } - [IgnoreDataMember] public bool IsMissingSeason { @@ -221,16 +202,32 @@ namespace MediaBrowser.Controller.Entities.TV var episodes = GetRecursiveChildren(user) .OfType(); - if (IndexNumber.HasValue) + var series = Series; + + if (IndexNumber.HasValue && series != null) { - var series = Series; + return series.GetEpisodes(user, IndexNumber.Value, includeMissingEpisodes, includeVirtualUnairedEpisodes, episodes); + } - if (series != null) + if (series != null && series.ContainsEpisodesWithoutSeasonFolders) + { + var seasonNumber = IndexNumber; + var list = episodes.ToList(); + + if (seasonNumber.HasValue) { - return series.GetEpisodes(user, IndexNumber.Value, includeMissingEpisodes, includeVirtualUnairedEpisodes, episodes); + list.AddRange(series.GetRecursiveChildren(user).OfType() + .Where(i => i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == seasonNumber.Value)); + } + else + { + list.AddRange(series.GetRecursiveChildren(user).OfType() + .Where(i => !i.ParentIndexNumber.HasValue)); } - } + episodes = list.DistinctBy(i => i.Id); + } + if (!includeMissingEpisodes) { episodes = episodes.Where(i => !i.IsMissingEpisode); @@ -245,6 +242,33 @@ namespace MediaBrowser.Controller.Entities.TV .Cast(); } + private IEnumerable GetEpisodes() + { + var episodes = RecursiveChildren.OfType(); + var series = Series; + + if (series != null && series.ContainsEpisodesWithoutSeasonFolders) + { + var seasonNumber = IndexNumber; + var list = episodes.ToList(); + + if (seasonNumber.HasValue) + { + list.AddRange(series.RecursiveChildren.OfType() + .Where(i => i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == seasonNumber.Value)); + } + else + { + list.AddRange(series.RecursiveChildren.OfType() + .Where(i => !i.ParentIndexNumber.HasValue)); + } + + episodes = list.DistinctBy(i => i.Id); + } + + return episodes; + } + public override IEnumerable GetChildren(User user, bool includeLinkedChildren) { return GetEpisodes(user); diff --git a/MediaBrowser.Controller/Library/IUserManager.cs b/MediaBrowser.Controller/Library/IUserManager.cs index f7fbc9c20..f5846973e 100644 --- a/MediaBrowser.Controller/Library/IUserManager.cs +++ b/MediaBrowser.Controller/Library/IUserManager.cs @@ -186,5 +186,12 @@ namespace MediaBrowser.Controller.Library /// The user identifier. /// The user policy. Task UpdateUserPolicy(string userId, UserPolicy userPolicy); + + /// + /// Makes the valid username. + /// + /// The username. + /// System.String. + string MakeValidUsername(string username); } } diff --git a/MediaBrowser.LocalMetadata/BaseXmlProvider.cs b/MediaBrowser.LocalMetadata/BaseXmlProvider.cs index 82e7809e8..74e3b61ca 100644 --- a/MediaBrowser.LocalMetadata/BaseXmlProvider.cs +++ b/MediaBrowser.LocalMetadata/BaseXmlProvider.cs @@ -28,8 +28,6 @@ namespace MediaBrowser.LocalMetadata var path = file.FullName; - //await XmlProviderUtils.XmlParsingResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false); - try { result.Item = new T(); @@ -45,10 +43,6 @@ namespace MediaBrowser.LocalMetadata { result.HasMetadata = false; } - finally - { - //XmlProviderUtils.XmlParsingResourcePool.Release(); - } return result; } diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs index 7c512840b..67c9123f5 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs @@ -612,6 +612,10 @@ namespace MediaBrowser.MediaEncoding.Subtitles catch (FileNotFoundException) { + } + catch (DirectoryNotFoundException) + { + } catch (IOException ex) { diff --git a/MediaBrowser.Model/ApiClient/IApiClient.cs b/MediaBrowser.Model/ApiClient/IApiClient.cs index dde6ca0b1..0181325fe 100644 --- a/MediaBrowser.Model/ApiClient/IApiClient.cs +++ b/MediaBrowser.Model/ApiClient/IApiClient.cs @@ -505,15 +505,6 @@ namespace MediaBrowser.Model.ApiClient /// The cancellation token. /// Task<PublicSystemInfo>. Task GetPublicSystemInfoAsync(CancellationToken cancellationToken = default(CancellationToken)); - - /// - /// Gets a person - /// - /// The name. - /// The user id. - /// Task{BaseItemDto}. - /// userId - Task GetPersonAsync(string name, string userId); /// /// Gets a list of plugins installed on the server @@ -967,15 +958,6 @@ namespace MediaBrowser.Model.ApiClient /// item string GetPersonImageUrl(BaseItemPerson item, ImageOptions options); - /// - /// Gets an image url that can be used to download an image from the api - /// - /// The name of the person - /// The options. - /// System.String. - /// name - string GetPersonImageUrl(string name, ImageOptions options); - /// /// Gets an image url that can be used to download an image from the api /// diff --git a/MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs b/MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs index a7ccf3f6e..6813f2ff5 100644 --- a/MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs +++ b/MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs @@ -106,6 +106,10 @@ namespace MediaBrowser.Providers.Movies { // No biggie. Don't blow up } + catch (DirectoryNotFoundException) + { + // No biggie. Don't blow up + } } var language = item.GetPreferredMetadataLanguage(); diff --git a/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs b/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs index 94d682f44..123ff9e29 100644 --- a/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs +++ b/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs @@ -82,6 +82,10 @@ namespace MediaBrowser.Providers.Music catch (FileNotFoundException) { + } + catch (DirectoryNotFoundException) + { + } } diff --git a/MediaBrowser.Providers/Music/FanArtArtistProvider.cs b/MediaBrowser.Providers/Music/FanArtArtistProvider.cs index a8df95fd1..6f633cfc8 100644 --- a/MediaBrowser.Providers/Music/FanArtArtistProvider.cs +++ b/MediaBrowser.Providers/Music/FanArtArtistProvider.cs @@ -90,6 +90,10 @@ namespace MediaBrowser.Providers.Music catch (FileNotFoundException) { + } + catch (DirectoryNotFoundException) + { + } } diff --git a/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs b/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs index 63d054664..253acc13f 100644 --- a/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs +++ b/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs @@ -83,6 +83,10 @@ namespace MediaBrowser.Providers.People { return null; } + catch (DirectoryNotFoundException) + { + return null; + } } private RemoteImageInfo GetImageInfo(string xmlFile, string personName, CancellationToken cancellationToken) diff --git a/MediaBrowser.Providers/TV/FanArtSeasonProvider.cs b/MediaBrowser.Providers/TV/FanArtSeasonProvider.cs index 05244af74..9f0cd4ff1 100644 --- a/MediaBrowser.Providers/TV/FanArtSeasonProvider.cs +++ b/MediaBrowser.Providers/TV/FanArtSeasonProvider.cs @@ -98,6 +98,10 @@ namespace MediaBrowser.Providers.TV { // No biggie. Don't blow up } + catch (DirectoryNotFoundException) + { + // No biggie. Don't blow up + } } } diff --git a/MediaBrowser.Providers/TV/FanartSeriesProvider.cs b/MediaBrowser.Providers/TV/FanartSeriesProvider.cs index afc71698b..8ba25e9f1 100644 --- a/MediaBrowser.Providers/TV/FanartSeriesProvider.cs +++ b/MediaBrowser.Providers/TV/FanartSeriesProvider.cs @@ -106,6 +106,10 @@ namespace MediaBrowser.Providers.TV { // No biggie. Don't blow up } + catch (DirectoryNotFoundException) + { + // No biggie. Don't blow up + } } var language = item.GetPreferredMetadataLanguage(); diff --git a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs index 21d41ca00..0b52956de 100644 --- a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs @@ -2,6 +2,7 @@ using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Localization; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; @@ -22,14 +23,16 @@ namespace MediaBrowser.Providers.TV private readonly IServerConfigurationManager _config; private readonly ILogger _logger; private readonly ILibraryManager _libraryManager; + private readonly ILocalizationManager _localization; private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); - public MissingEpisodeProvider(ILogger logger, IServerConfigurationManager config, ILibraryManager libraryManager) + public MissingEpisodeProvider(ILogger logger, IServerConfigurationManager config, ILibraryManager libraryManager, ILocalizationManager localization) { _logger = logger; _config = config; _libraryManager = libraryManager; + _localization = localization; } public async Task Run(IEnumerable> series, CancellationToken cancellationToken) @@ -93,16 +96,16 @@ namespace MediaBrowser.Providers.TV var hasBadData = HasInvalidContent(group); - var anySeasonsRemoved = await RemoveObsoleteOrMissingSeasons(group, episodeLookup, false) + var anySeasonsRemoved = await RemoveObsoleteOrMissingSeasons(group, episodeLookup) .ConfigureAwait(false); - var anyEpisodesRemoved = await RemoveObsoleteOrMissingEpisodes(group, episodeLookup, false) + var anyEpisodesRemoved = await RemoveObsoleteOrMissingEpisodes(group, episodeLookup) .ConfigureAwait(false); var hasNewEpisodes = false; var hasNewSeasons = false; - foreach (var series in group.Where(s => s.ContainsEpisodesWithoutSeasonFolders)) + foreach (var series in group) { hasNewSeasons = await AddDummySeasonFolders(series, cancellationToken).ConfigureAwait(false); } @@ -165,14 +168,15 @@ namespace MediaBrowser.Providers.TV /// private async Task AddDummySeasonFolders(Series series, CancellationToken cancellationToken) { - var existingEpisodes = series.RecursiveChildren + var episodesInSeriesFolder = series.RecursiveChildren .OfType() + .Where(i => !i.IsInSeasonFolder) .ToList(); var hasChanges = false; // Loop through the unique season numbers - foreach (var seasonNumber in existingEpisodes.Select(i => i.ParentIndexNumber ?? -1) + foreach (var seasonNumber in episodesInSeriesFolder.Select(i => i.ParentIndexNumber ?? -1) .Where(i => i >= 0) .Distinct() .ToList()) @@ -188,6 +192,20 @@ namespace MediaBrowser.Providers.TV } } + // Unknown season - create a dummy season to put these under + if (episodesInSeriesFolder.Any(i => !i.ParentIndexNumber.HasValue)) + { + var hasSeason = series.Children.OfType() + .Any(i => !i.IndexNumber.HasValue); + + if (!hasSeason) + { + await AddSeason(series, null, cancellationToken).ConfigureAwait(false); + + hasChanges = true; + } + } + return hasChanges; } @@ -292,8 +310,7 @@ namespace MediaBrowser.Providers.TV /// Removes the virtual entry after a corresponding physical version has been added /// private async Task RemoveObsoleteOrMissingEpisodes(IEnumerable series, - IEnumerable> episodeLookup, - bool forceRemoveAll) + IEnumerable> episodeLookup) { var existingEpisodes = (from s in series let seasonOffset = TvdbSeriesProvider.GetSeriesOffset(s.ProviderIds) ?? ((s.AnimeSeriesIndex ?? 1) - 1) @@ -312,11 +329,6 @@ namespace MediaBrowser.Providers.TV var episodesToRemove = virtualEpisodes .Where(i => { - if (forceRemoveAll) - { - return true; - } - if (i.Episode.IndexNumber.HasValue && i.Episode.ParentIndexNumber.HasValue) { var seasonNumber = i.Episode.ParentIndexNumber.Value + i.SeasonOffset; @@ -362,11 +374,9 @@ namespace MediaBrowser.Providers.TV /// /// The series. /// The episode lookup. - /// if set to true [force remove all]. /// Task{System.Boolean}. private async Task RemoveObsoleteOrMissingSeasons(IEnumerable series, - IEnumerable> episodeLookup, - bool forceRemoveAll) + IEnumerable> episodeLookup) { var existingSeasons = (from s in series let seasonOffset = TvdbSeriesProvider.GetSeriesOffset(s.ProviderIds) ?? ((s.AnimeSeriesIndex ?? 1) - 1) @@ -385,11 +395,6 @@ namespace MediaBrowser.Providers.TV var seasonsToRemove = virtualSeasons .Where(i => { - if (forceRemoveAll) - { - return true; - } - if (i.Season.IndexNumber.HasValue) { var seasonNumber = i.Season.IndexNumber.Value + i.SeasonOffset; @@ -409,7 +414,9 @@ namespace MediaBrowser.Providers.TV return false; } - return true; + // Season does not have a number + // Remove if there are no episodes directly in series without a season number + return i.Season.Series.RecursiveChildren.OfType().All(s => s.ParentIndexNumber.HasValue || s.IsInSeasonFolder); }) .ToList(); @@ -472,20 +479,22 @@ namespace MediaBrowser.Providers.TV /// The cancellation token. /// Task{Season}. private async Task AddSeason(Series series, - int seasonNumber, + int? seasonNumber, CancellationToken cancellationToken) { - _logger.Info("Creating Season {0} entry for {1}", seasonNumber, series.Name); + var seasonName = seasonNumber == 0 ? + _config.Configuration.SeasonZeroDisplayName : + (seasonNumber.HasValue ? string.Format(_localization.GetLocalizedString("NameSeasonNumber"), seasonNumber.Value.ToString(UsCulture)) : _localization.GetLocalizedString("NameSeasonUnknown")); - var name = seasonNumber == 0 ? _config.Configuration.SeasonZeroDisplayName : string.Format("Season {0}", seasonNumber.ToString(UsCulture)); + _logger.Info("Creating Season {0} entry for {1}", seasonName, series.Name); var season = new Season { - Name = name, + Name = seasonName, IndexNumber = seasonNumber, Parent = series, DisplayMediaType = typeof(Season).Name, - Id = (series.Id + seasonNumber.ToString(UsCulture) + name).GetMBId(typeof(Season)) + Id = (series.Id + (seasonNumber ?? -1).ToString(UsCulture) + seasonName).GetMBId(typeof(Season)) }; await series.AddChild(season, cancellationToken).ConfigureAwait(false); diff --git a/MediaBrowser.Providers/TV/SeriesPostScanTask.cs b/MediaBrowser.Providers/TV/SeriesPostScanTask.cs index d350d2fe4..e1986a7c1 100644 --- a/MediaBrowser.Providers/TV/SeriesPostScanTask.cs +++ b/MediaBrowser.Providers/TV/SeriesPostScanTask.cs @@ -1,11 +1,12 @@ -using System.Collections.Generic; -using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Localization; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using System; +using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -25,12 +26,14 @@ namespace MediaBrowser.Providers.TV private readonly ILibraryManager _libraryManager; private readonly IServerConfigurationManager _config; private readonly ILogger _logger; + private readonly ILocalizationManager _localization; - public SeriesPostScanTask(ILibraryManager libraryManager, ILogger logger, IServerConfigurationManager config) + public SeriesPostScanTask(ILibraryManager libraryManager, ILogger logger, IServerConfigurationManager config, ILocalizationManager localization) { _libraryManager = libraryManager; _logger = logger; _config = config; + _localization = localization; } public Task Run(IProgress progress, CancellationToken cancellationToken) @@ -47,7 +50,7 @@ namespace MediaBrowser.Providers.TV var seriesGroups = FindSeriesGroups(seriesList).Where(g => !string.IsNullOrEmpty(g.Key)).ToList(); - await new MissingEpisodeProvider(_logger, _config, _libraryManager).Run(seriesGroups, cancellationToken).ConfigureAwait(false); + await new MissingEpisodeProvider(_logger, _config, _libraryManager, _localization).Run(seriesGroups, cancellationToken).ConfigureAwait(false); var numComplete = 0; diff --git a/MediaBrowser.Providers/TV/TvdbEpisodeProvider.cs b/MediaBrowser.Providers/TV/TvdbEpisodeProvider.cs index ef9f5427c..52c1ab7dd 100644 --- a/MediaBrowser.Providers/TV/TvdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/TvdbEpisodeProvider.cs @@ -72,6 +72,10 @@ namespace MediaBrowser.Providers.TV { // Don't fail the provider because this will just keep on going and going. } + catch (DirectoryNotFoundException) + { + // Don't fail the provider because this will just keep on going and going. + } } return list; @@ -101,6 +105,10 @@ namespace MediaBrowser.Providers.TV { // Don't fail the provider because this will just keep on going and going. } + catch (DirectoryNotFoundException) + { + // Don't fail the provider because this will just keep on going and going. + } } return result; @@ -208,8 +216,9 @@ namespace MediaBrowser.Providers.TV /// Fetches the episode data. /// /// The identifier. + /// The identity. /// The series data path. - /// + /// The series provider ids. /// The cancellation token. /// Task{System.Boolean}. private Episode FetchEpisodeData(EpisodeInfo id, EpisodeIdentity identity, string seriesDataPath, Dictionary seriesProviderIds, CancellationToken cancellationToken) @@ -279,6 +288,10 @@ namespace MediaBrowser.Providers.TV { break; } + catch (DirectoryNotFoundException) + { + break; + } episodeNumber++; } diff --git a/MediaBrowser.Providers/TV/TvdbSeasonImageProvider.cs b/MediaBrowser.Providers/TV/TvdbSeasonImageProvider.cs index efafeae96..1ebd7bed5 100644 --- a/MediaBrowser.Providers/TV/TvdbSeasonImageProvider.cs +++ b/MediaBrowser.Providers/TV/TvdbSeasonImageProvider.cs @@ -94,6 +94,10 @@ namespace MediaBrowser.Providers.TV { // No tvdb data yet. Don't blow up } + catch (DirectoryNotFoundException) + { + // No tvdb data yet. Don't blow up + } } return new RemoteImageInfo[] { }; diff --git a/MediaBrowser.Providers/TV/TvdbSeriesImageProvider.cs b/MediaBrowser.Providers/TV/TvdbSeriesImageProvider.cs index 9cc09c40c..08913d3b4 100644 --- a/MediaBrowser.Providers/TV/TvdbSeriesImageProvider.cs +++ b/MediaBrowser.Providers/TV/TvdbSeriesImageProvider.cs @@ -87,6 +87,10 @@ namespace MediaBrowser.Providers.TV { // No tvdb data yet. Don't blow up } + catch (DirectoryNotFoundException) + { + // No tvdb data yet. Don't blow up + } } return new RemoteImageInfo[] { }; diff --git a/MediaBrowser.Server.Implementations/Connect/ConnectManager.cs b/MediaBrowser.Server.Implementations/Connect/ConnectManager.cs index 376dc3548..67d844543 100644 --- a/MediaBrowser.Server.Implementations/Connect/ConnectManager.cs +++ b/MediaBrowser.Server.Implementations/Connect/ConnectManager.cs @@ -1,4 +1,5 @@ using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; @@ -789,7 +790,7 @@ namespace MediaBrowser.Server.Implementations.Connect if (user == null) { // Add user - user = await _userManager.CreateUser(connectEntry.UserName).ConfigureAwait(false); + user = await _userManager.CreateUser(_userManager.MakeValidUsername(connectEntry.UserName)).ConfigureAwait(false); user.ConnectUserName = connectEntry.UserName; user.ConnectUserId = connectEntry.UserId; diff --git a/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs b/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs index b141fea1e..967c78c50 100644 --- a/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs +++ b/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs @@ -78,6 +78,11 @@ namespace MediaBrowser.Server.Implementations.Drawing // No biggie sizeDictionary = new Dictionary(); } + catch (DirectoryNotFoundException) + { + // No biggie + sizeDictionary = new Dictionary(); + } catch (Exception ex) { logger.ErrorException("Error parsing image size cache file", ex); diff --git a/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs b/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs index 3b5e34520..432ea1f69 100644 --- a/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs +++ b/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs @@ -459,11 +459,11 @@ namespace MediaBrowser.Server.Implementations.FileOrganization private bool IsSameEpisode(string sourcePath, string newPath) { - var sourceFileInfo = new FileInfo(sourcePath); - var destinationFileInfo = new FileInfo(newPath); - try { + var sourceFileInfo = new FileInfo(sourcePath); + var destinationFileInfo = new FileInfo(newPath); + if (sourceFileInfo.Length == destinationFileInfo.Length) { return true; @@ -473,6 +473,10 @@ namespace MediaBrowser.Server.Implementations.FileOrganization { return false; } + catch (DirectoryNotFoundException) + { + return false; + } return false; } diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index d52288f87..66125784c 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -1755,9 +1755,12 @@ namespace MediaBrowser.Server.Implementations.Library var resolver = new EpisodeResolver(new ExtendedNamingOptions(), new Naming.Logging.NullLogger()); + var fileType = episode.VideoType == VideoType.BluRay || episode.VideoType == VideoType.Dvd || episode.VideoType == VideoType.HdDvd ? + FileInfoType.Directory : + FileInfoType.File; + var locationType = episode.LocationType; - var fileType = /*args.IsDirectory ? FileInfoType.Directory :*/ FileInfoType.File; var episodeInfo = locationType == LocationType.FileSystem || locationType == LocationType.Offline ? resolver.Resolve(episode.Path, fileType) : new Naming.TV.EpisodeInfo(); @@ -1769,29 +1772,42 @@ namespace MediaBrowser.Server.Implementations.Library var changed = false; - if (!episode.IndexNumber.HasValue) + if (episodeInfo.IsByDate) { - episode.IndexNumber = episodeInfo.EpisodeNumber; - if (episode.IndexNumber.HasValue) { + episode.IndexNumber = null; changed = true; } - } - - if (!episode.IndexNumberEnd.HasValue) - { - episode.IndexNumberEnd = episodeInfo.EndingEpsiodeNumber; if (episode.IndexNumberEnd.HasValue) { + episode.IndexNumberEnd = null; changed = true; } - } - if (!episode.ParentIndexNumber.HasValue) - { - episode.ParentIndexNumber = episodeInfo.SeasonNumber; + if (!episode.PremiereDate.HasValue) + { + if (episodeInfo.Year.HasValue && episodeInfo.Month.HasValue && episodeInfo.Day.HasValue) + { + episode.PremiereDate = new DateTime(episodeInfo.Year.Value, episodeInfo.Month.Value, episodeInfo.Day.Value).ToUniversalTime(); + } + + if (episode.PremiereDate.HasValue) + { + changed = true; + } + } + + if (!episode.ProductionYear.HasValue) + { + episode.ProductionYear = episodeInfo.Year; + + if (episode.ProductionYear.HasValue) + { + changed = true; + } + } if (!episode.ParentIndexNumber.HasValue) { @@ -1801,11 +1817,53 @@ namespace MediaBrowser.Server.Implementations.Library { episode.ParentIndexNumber = season.IndexNumber; } + + if (episode.ParentIndexNumber.HasValue) + { + changed = true; + } + } + } + else + { + if (!episode.IndexNumber.HasValue) + { + episode.IndexNumber = episodeInfo.EpisodeNumber; + + if (episode.IndexNumber.HasValue) + { + changed = true; + } } - if (episode.ParentIndexNumber.HasValue) + if (!episode.IndexNumberEnd.HasValue) { - changed = true; + episode.IndexNumberEnd = episodeInfo.EndingEpsiodeNumber; + + if (episode.IndexNumberEnd.HasValue) + { + changed = true; + } + } + + if (!episode.ParentIndexNumber.HasValue) + { + episode.ParentIndexNumber = episodeInfo.SeasonNumber; + + if (!episode.ParentIndexNumber.HasValue) + { + var season = episode.Season; + + if (season != null) + { + episode.ParentIndexNumber = season.IndexNumber; + } + } + + if (episode.ParentIndexNumber.HasValue) + { + changed = true; + } } } diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs index 39b0a93cc..1a873f01e 100644 --- a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs +++ b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs @@ -1,7 +1,5 @@ using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; -using MediaBrowser.Naming.Common; -using MediaBrowser.Naming.IO; using System.Linq; namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV diff --git a/MediaBrowser.Server.Implementations/Library/UserManager.cs b/MediaBrowser.Server.Implementations/Library/UserManager.cs index 1d58ad074..02d7c1be1 100644 --- a/MediaBrowser.Server.Implementations/Library/UserManager.cs +++ b/MediaBrowser.Server.Implementations/Library/UserManager.cs @@ -171,6 +171,38 @@ namespace MediaBrowser.Server.Implementations.Library return AuthenticateUser(username, passwordSha1, null, remoteEndPoint); } + public bool IsValidUsername(string username) + { + // Usernames can contain letters (a-z), numbers (0-9), dashes (-), underscores (_), apostrophes ('), and periods (.) + return username.All(IsValidCharacter); + } + + private bool IsValidCharacter(char i) + { + return char.IsLetterOrDigit(i) || char.Equals(i, '-') || char.Equals(i, '_') || char.Equals(i, '\'') || + char.Equals(i, '.'); + } + + public string MakeValidUsername(string username) + { + if (IsValidUsername(username)) + { + return username; + } + + // Usernames can contain letters (a-z), numbers (0-9), dashes (-), underscores (_), apostrophes ('), and periods (.) + var builder = new StringBuilder(); + + foreach (var c in username) + { + if (IsValidCharacter(c)) + { + builder.Append(c); + } + } + return builder.ToString(); + } + public async Task AuthenticateUser(string username, string passwordSha1, string passwordMd5, string remoteEndPoint) { if (string.IsNullOrWhiteSpace(username)) @@ -178,7 +210,8 @@ namespace MediaBrowser.Server.Implementations.Library throw new ArgumentNullException("username"); } - var user = Users.FirstOrDefault(i => string.Equals(username, i.Name, StringComparison.OrdinalIgnoreCase)); + var user = Users + .FirstOrDefault(i => string.Equals(username, i.Name, StringComparison.OrdinalIgnoreCase)); if (user == null) { @@ -203,20 +236,6 @@ namespace MediaBrowser.Server.Implementations.Library } } - // Maybe user accidently entered connect credentials. let's be flexible - if (!success && user.ConnectLinkType.HasValue && !string.IsNullOrWhiteSpace(passwordMd5)) - { - try - { - await _connectFactory().Authenticate(user.ConnectUserName, passwordMd5).ConfigureAwait(false); - success = true; - } - catch - { - - } - } - // Update LastActivityDate and LastLoginDate, then save if (success) { @@ -273,7 +292,7 @@ namespace MediaBrowser.Server.Implementations.Library // There always has to be at least one user. if (users.Count == 0) { - var name = Environment.UserName; + var name = MakeValidUsername(Environment.UserName); var user = InstantiateNewUser(name, false); @@ -477,6 +496,11 @@ namespace MediaBrowser.Server.Implementations.Library throw new ArgumentNullException("name"); } + if (!IsValidUsername(name)) + { + throw new ArgumentException("Only alphanumeric characters are allowed."); + } + if (Users.Any(u => u.Name.Equals(name, StringComparison.OrdinalIgnoreCase))) { throw new ArgumentException(string.Format("A user with the name '{0}' already exists.", name)); @@ -803,6 +827,10 @@ namespace MediaBrowser.Server.Implementations.Library return (UserPolicy)_xmlSerializer.DeserializeFromFile(typeof(UserPolicy), path); } } + catch (DirectoryNotFoundException) + { + return GetDefaultPolicy(user); + } catch (FileNotFoundException) { return GetDefaultPolicy(user); @@ -840,6 +868,8 @@ namespace MediaBrowser.Server.Implementations.Library var path = GetPolifyFilePath(user); + Directory.CreateDirectory(Path.GetDirectoryName(path)); + lock (_policySyncLock) { _xmlSerializer.SerializeToFile(userPolicy, path); @@ -900,6 +930,10 @@ namespace MediaBrowser.Server.Implementations.Library return (UserConfiguration)_xmlSerializer.DeserializeFromFile(typeof(UserConfiguration), path); } } + catch (DirectoryNotFoundException) + { + return new UserConfiguration(); + } catch (FileNotFoundException) { return new UserConfiguration(); @@ -930,6 +964,8 @@ namespace MediaBrowser.Server.Implementations.Library config = _jsonSerializer.DeserializeFromString(json); } + Directory.CreateDirectory(Path.GetDirectoryName(path)); + lock (_configSyncLock) { _xmlSerializer.SerializeToFile(config, path); diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json index 695200239..827a2388d 100644 --- a/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json +++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json @@ -419,7 +419,7 @@ "HeaderMediaLocations": "Media Locations", "LabelFolderTypeValue": "Folder type: {0}", "LabelPathSubstitutionHelp": "Optional: Path substitution can map server paths to network shares that clients can access for direct playback.", - "FolderTypeMixed": "Mixed videos", + "FolderTypeMixed": "Mixed content", "FolderTypeMovies": "Movies", "FolderTypeMusic": "Music", "FolderTypeAdultVideos": "Adult videos", @@ -658,5 +658,5 @@ "LabelItemLimitHelp": "Optional. Set a limit to the number of items that will be synced.", "MessageBookPluginRequired": "Requires installation of the Bookshelf plugin", "MessageGamePluginRequired": "Requires installation of the GameBrowser plugin", - "MessageMixedContentHelp": "Content will be displayed with as a plain folder structure" + "MessageMixedContentHelp": "Content will be displayed as a plain folder structure" } diff --git a/MediaBrowser.Server.Implementations/Localization/Server/server.json b/MediaBrowser.Server.Implementations/Localization/Server/server.json index 9a515d492..c0af13950 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/server.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/server.json @@ -37,7 +37,7 @@ "ButtonOk": "Ok", "ButtonCancel": "Cancel", "ButtonNew": "New", - "FolderTypeMixed": "Mixed videos", + "FolderTypeMixed": "Mixed content", "FolderTypeMovies": "Movies", "FolderTypeMusic": "Music", "FolderTypeAdultVideos": "Adult videos", @@ -48,7 +48,7 @@ "FolderTypeBooks": "Books", "FolderTypeTvShows": "TV", "FolderTypeInherit": "Inherit", - "LabelContentType": "Content type:", + "LabelContentType": "Content type:", "HeaderSetupLibrary": "Setup your media library", "ButtonAddMediaFolder": "Add media folder", "LabelFolderType": "Folder type:", @@ -1307,5 +1307,8 @@ "LabelEnableSingleImageInDidlLimitHelp": "Some devices will not render properly if multiple images are embedded within Didl.", "TabActivity": "Activity", "TitleSync": "Sync", - "OptionAllowSyncContent": "Allow syncing media to devices" + "OptionAllowSyncContent": "Allow syncing media to devices", + "NameSeasonUnknown": "Season Unknown", + "NameSeasonNumber": "Season {0}", + "LabelNewUserNameHelp": "Usernames can contain letters (a-z), numbers (0-9), dashes (-), underscores (_), apostrophes ('), and periods (.)" } diff --git a/MediaBrowser.Server.Implementations/News/NewsService.cs b/MediaBrowser.Server.Implementations/News/NewsService.cs index 9eeadfab7..684363d01 100644 --- a/MediaBrowser.Server.Implementations/News/NewsService.cs +++ b/MediaBrowser.Server.Implementations/News/NewsService.cs @@ -26,6 +26,14 @@ namespace MediaBrowser.Server.Implementations.News { return GetProductNewsInternal(query); } + catch (DirectoryNotFoundException) + { + // No biggie + return new QueryResult + { + Items = new NewsItem[] { } + }; + } catch (FileNotFoundException) { // No biggie diff --git a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs index 86e92530f..0f1d53ea6 100644 --- a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs +++ b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs @@ -256,6 +256,10 @@ namespace MediaBrowser.XbmcMetadata.Savers catch (FileNotFoundException) { + } + catch (DirectoryNotFoundException) + { + } writer.WriteEndElement(); -- cgit v1.2.3 From 2e53ff1fd0379ed6e4861f062815402230205ff0 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 26 Dec 2014 14:34:39 -0500 Subject: move files out of common --- .../ScheduledTasksWebSocketListener.cs | 1 + .../Session/SessionInfoWebSocketListener.cs | 1 + .../System/ActivityLogWebSocketListener.cs | 4 +- .../System/SystemInfoWebSocketListener.cs | 1 + .../Configuration/ConfigurationHelper.cs | 59 ++++ .../MediaBrowser.Common.Implementations.csproj | 1 + .../Configuration/ConfigurationHelper.cs | 59 ---- MediaBrowser.Common/MediaBrowser.Common.csproj | 3 - .../Net/BasePeriodicWebSocketListener.cs | 326 -------------------- MediaBrowser.Common/Net/IWebSocketListener.cs | 17 -- .../MediaBrowser.Controller.csproj | 2 + .../Net/BasePeriodicWebSocketListener.cs | 327 +++++++++++++++++++++ MediaBrowser.Controller/Net/IWebSocketListener.cs | 18 ++ .../Session/SessionWebSocketListener.cs | 1 + 14 files changed, 413 insertions(+), 407 deletions(-) create mode 100644 MediaBrowser.Common.Implementations/Configuration/ConfigurationHelper.cs delete mode 100644 MediaBrowser.Common/Configuration/ConfigurationHelper.cs delete mode 100644 MediaBrowser.Common/Net/BasePeriodicWebSocketListener.cs delete mode 100644 MediaBrowser.Common/Net/IWebSocketListener.cs create mode 100644 MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs create mode 100644 MediaBrowser.Controller/Net/IWebSocketListener.cs (limited to 'MediaBrowser.Common.Implementations/Configuration') diff --git a/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs b/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs index f34c53b16..cb8f91ab6 100644 --- a/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs +++ b/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs @@ -1,5 +1,6 @@ using MediaBrowser.Common.Net; using MediaBrowser.Common.ScheduledTasks; +using MediaBrowser.Controller.Net; using MediaBrowser.Model.Events; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Tasks; diff --git a/MediaBrowser.Api/Session/SessionInfoWebSocketListener.cs b/MediaBrowser.Api/Session/SessionInfoWebSocketListener.cs index e6b525e53..25130776e 100644 --- a/MediaBrowser.Api/Session/SessionInfoWebSocketListener.cs +++ b/MediaBrowser.Api/Session/SessionInfoWebSocketListener.cs @@ -1,5 +1,6 @@ using MediaBrowser.Common.Net; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Session; diff --git a/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs b/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs index 4629b2a8c..a951cd3d6 100644 --- a/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs +++ b/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs @@ -1,5 +1,5 @@ -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Activity; +using MediaBrowser.Controller.Activity; +using MediaBrowser.Controller.Net; using MediaBrowser.Model.Activity; using MediaBrowser.Model.Events; using MediaBrowser.Model.Logging; diff --git a/MediaBrowser.Api/System/SystemInfoWebSocketListener.cs b/MediaBrowser.Api/System/SystemInfoWebSocketListener.cs index c20cef3b3..49a3e3291 100644 --- a/MediaBrowser.Api/System/SystemInfoWebSocketListener.cs +++ b/MediaBrowser.Api/System/SystemInfoWebSocketListener.cs @@ -1,5 +1,6 @@ using MediaBrowser.Common.Net; using MediaBrowser.Controller; +using MediaBrowser.Controller.Net; using MediaBrowser.Model.Logging; using MediaBrowser.Model.System; using System.Threading.Tasks; diff --git a/MediaBrowser.Common.Implementations/Configuration/ConfigurationHelper.cs b/MediaBrowser.Common.Implementations/Configuration/ConfigurationHelper.cs new file mode 100644 index 000000000..ff5b8bd59 --- /dev/null +++ b/MediaBrowser.Common.Implementations/Configuration/ConfigurationHelper.cs @@ -0,0 +1,59 @@ +using MediaBrowser.Model.Serialization; +using System; +using System.IO; +using System.Linq; + +namespace MediaBrowser.Common.Implementations.Configuration +{ + /// + /// Class ConfigurationHelper + /// + public static class ConfigurationHelper + { + /// + /// Reads an xml configuration file from the file system + /// It will immediately re-serialize and save if new serialization data is available due to property changes + /// + /// The type. + /// The path. + /// The XML serializer. + /// System.Object. + public static object GetXmlConfiguration(Type type, string path, IXmlSerializer xmlSerializer) + { + object configuration; + + byte[] buffer = null; + + // Use try/catch to avoid the extra file system lookup using File.Exists + try + { + buffer = File.ReadAllBytes(path); + + configuration = xmlSerializer.DeserializeFromBytes(type, buffer); + } + catch (Exception) + { + configuration = Activator.CreateInstance(type); + } + + using (var stream = new MemoryStream()) + { + xmlSerializer.SerializeToStream(configuration, stream); + + // Take the object we just got and serialize it back to bytes + var newBytes = stream.ToArray(); + + // If the file didn't exist before, or if something has changed, re-save + if (buffer == null || !buffer.SequenceEqual(newBytes)) + { + Directory.CreateDirectory(Path.GetDirectoryName(path)); + + // Save it after load in case we got new items + File.WriteAllBytes(path, newBytes); + } + + return configuration; + } + } + } +} diff --git a/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj b/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj index 42a402940..0aaf0b4e0 100644 --- a/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj +++ b/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj @@ -80,6 +80,7 @@ + diff --git a/MediaBrowser.Common/Configuration/ConfigurationHelper.cs b/MediaBrowser.Common/Configuration/ConfigurationHelper.cs deleted file mode 100644 index 7212b70e1..000000000 --- a/MediaBrowser.Common/Configuration/ConfigurationHelper.cs +++ /dev/null @@ -1,59 +0,0 @@ -using MediaBrowser.Model.Serialization; -using System; -using System.IO; -using System.Linq; - -namespace MediaBrowser.Common.Configuration -{ - /// - /// Class ConfigurationHelper - /// - public static class ConfigurationHelper - { - /// - /// Reads an xml configuration file from the file system - /// It will immediately re-serialize and save if new serialization data is available due to property changes - /// - /// The type. - /// The path. - /// The XML serializer. - /// System.Object. - public static object GetXmlConfiguration(Type type, string path, IXmlSerializer xmlSerializer) - { - object configuration; - - byte[] buffer = null; - - // Use try/catch to avoid the extra file system lookup using File.Exists - try - { - buffer = File.ReadAllBytes(path); - - configuration = xmlSerializer.DeserializeFromBytes(type, buffer); - } - catch (Exception) - { - configuration = Activator.CreateInstance(type); - } - - using (var stream = new MemoryStream()) - { - xmlSerializer.SerializeToStream(configuration, stream); - - // Take the object we just got and serialize it back to bytes - var newBytes = stream.ToArray(); - - // If the file didn't exist before, or if something has changed, re-save - if (buffer == null || !buffer.SequenceEqual(newBytes)) - { - Directory.CreateDirectory(Path.GetDirectoryName(path)); - - // Save it after load in case we got new items - File.WriteAllBytes(path, newBytes); - } - - return configuration; - } - } - } -} diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj index 140a4ae0e..b4cc17f51 100644 --- a/MediaBrowser.Common/MediaBrowser.Common.csproj +++ b/MediaBrowser.Common/MediaBrowser.Common.csproj @@ -53,7 +53,6 @@ Properties\SharedVersion.cs - @@ -64,11 +63,9 @@ - - diff --git a/MediaBrowser.Common/Net/BasePeriodicWebSocketListener.cs b/MediaBrowser.Common/Net/BasePeriodicWebSocketListener.cs deleted file mode 100644 index a2af3707b..000000000 --- a/MediaBrowser.Common/Net/BasePeriodicWebSocketListener.cs +++ /dev/null @@ -1,326 +0,0 @@ -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Net; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Common.Net -{ - /// - /// Starts sending data over a web socket periodically when a message is received, and then stops when a corresponding stop message is received - /// - /// The type of the T return data type. - /// The type of the T state type. - public abstract class BasePeriodicWebSocketListener : IWebSocketListener, IDisposable - where TStateType : WebSocketListenerState, new() - where TReturnDataType : class - { - /// - /// The _active connections - /// - protected readonly List> ActiveConnections = - new List>(); - - /// - /// Gets the name. - /// - /// The name. - protected abstract string Name { get; } - - /// - /// Gets the data to send. - /// - /// The state. - /// Task{`1}. - protected abstract Task GetDataToSend(TStateType state); - - /// - /// The logger - /// - protected ILogger Logger; - - /// - /// Initializes a new instance of the class. - /// - /// The logger. - /// logger - protected BasePeriodicWebSocketListener(ILogger logger) - { - if (logger == null) - { - throw new ArgumentNullException("logger"); - } - - Logger = logger; - } - - /// - /// The null task result - /// - protected Task NullTaskResult = Task.FromResult(true); - - /// - /// Processes the message. - /// - /// The message. - /// Task. - public Task ProcessMessage(WebSocketMessageInfo message) - { - if (message.MessageType.Equals(Name + "Start", StringComparison.OrdinalIgnoreCase)) - { - Start(message); - } - - if (message.MessageType.Equals(Name + "Stop", StringComparison.OrdinalIgnoreCase)) - { - Stop(message); - } - - return NullTaskResult; - } - - protected readonly CultureInfo UsCulture = new CultureInfo("en-US"); - - protected virtual bool SendOnTimer - { - get - { - return true; - } - } - - /// - /// Starts sending messages over a web socket - /// - /// The message. - private void Start(WebSocketMessageInfo message) - { - var vals = message.Data.Split(','); - - var dueTimeMs = long.Parse(vals[0], UsCulture); - var periodMs = long.Parse(vals[1], UsCulture); - - var cancellationTokenSource = new CancellationTokenSource(); - - Logger.Info("{1} Begin transmitting over websocket to {0}", message.Connection.RemoteEndPoint, GetType().Name); - - var timer = SendOnTimer ? - new Timer(TimerCallback, message.Connection, Timeout.Infinite, Timeout.Infinite) : - null; - - var state = new TStateType - { - IntervalMs = periodMs, - InitialDelayMs = dueTimeMs - }; - - var semaphore = new SemaphoreSlim(1, 1); - - lock (ActiveConnections) - { - ActiveConnections.Add(new Tuple(message.Connection, cancellationTokenSource, timer, state, semaphore)); - } - - if (timer != null) - { - timer.Change(TimeSpan.FromMilliseconds(dueTimeMs), TimeSpan.FromMilliseconds(periodMs)); - } - } - - /// - /// Timers the callback. - /// - /// The state. - private void TimerCallback(object state) - { - var connection = (IWebSocketConnection)state; - - Tuple tuple; - - lock (ActiveConnections) - { - tuple = ActiveConnections.FirstOrDefault(c => c.Item1 == connection); - } - - if (tuple == null) - { - return; - } - - if (connection.State != WebSocketState.Open || tuple.Item2.IsCancellationRequested) - { - DisposeConnection(tuple); - return; - } - - SendData(tuple); - } - - protected void SendData(bool force) - { - List> tuples; - - lock (ActiveConnections) - { - tuples = ActiveConnections - .Where(c => - { - if (c.Item1.State == WebSocketState.Open && !c.Item2.IsCancellationRequested) - { - var state = c.Item4; - - if (force || (DateTime.UtcNow - state.DateLastSendUtc).TotalMilliseconds >= state.IntervalMs) - { - return true; - } - } - - return false; - }) - .ToList(); - } - - foreach (var tuple in tuples) - { - SendData(tuple); - } - } - - private async void SendData(Tuple tuple) - { - var connection = tuple.Item1; - - try - { - await tuple.Item5.WaitAsync(tuple.Item2.Token).ConfigureAwait(false); - - var state = tuple.Item4; - - var data = await GetDataToSend(state).ConfigureAwait(false); - - if (data != null) - { - await connection.SendAsync(new WebSocketMessage - { - MessageType = Name, - Data = data - - }, tuple.Item2.Token).ConfigureAwait(false); - - state.DateLastSendUtc = DateTime.UtcNow; - } - - tuple.Item5.Release(); - } - catch (OperationCanceledException) - { - if (tuple.Item2.IsCancellationRequested) - { - DisposeConnection(tuple); - } - } - catch (Exception ex) - { - Logger.ErrorException("Error sending web socket message {0}", ex, Name); - DisposeConnection(tuple); - } - } - - /// - /// Stops sending messages over a web socket - /// - /// The message. - private void Stop(WebSocketMessageInfo message) - { - lock (ActiveConnections) - { - var connection = ActiveConnections.FirstOrDefault(c => c.Item1 == message.Connection); - - if (connection != null) - { - DisposeConnection(connection); - } - } - } - - /// - /// Disposes the connection. - /// - /// The connection. - private void DisposeConnection(Tuple connection) - { - Logger.Info("{1} stop transmitting over websocket to {0}", connection.Item1.RemoteEndPoint, GetType().Name); - - var timer = connection.Item3; - - if (timer != null) - { - try - { - timer.Dispose(); - } - catch (ObjectDisposedException) - { - - } - } - - try - { - connection.Item2.Cancel(); - connection.Item2.Dispose(); - } - catch (ObjectDisposedException) - { - - } - - try - { - connection.Item5.Dispose(); - } - catch (ObjectDisposedException) - { - - } - - ActiveConnections.Remove(connection); - } - - /// - /// Releases unmanaged and - optionally - managed resources. - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected virtual void Dispose(bool dispose) - { - if (dispose) - { - lock (ActiveConnections) - { - foreach (var connection in ActiveConnections.ToList()) - { - DisposeConnection(connection); - } - } - } - } - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - public void Dispose() - { - Dispose(true); - } - } - - public class WebSocketListenerState - { - public DateTime DateLastSendUtc { get; set; } - public long InitialDelayMs { get; set; } - public long IntervalMs { get; set; } - } -} diff --git a/MediaBrowser.Common/Net/IWebSocketListener.cs b/MediaBrowser.Common/Net/IWebSocketListener.cs deleted file mode 100644 index 4b6c4111d..000000000 --- a/MediaBrowser.Common/Net/IWebSocketListener.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Threading.Tasks; - -namespace MediaBrowser.Common.Net -{ - /// - ///This is an interface for listening to messages coming through a web socket connection - /// - public interface IWebSocketListener - { - /// - /// Processes the message. - /// - /// The message. - /// Task. - Task ProcessMessage(WebSocketMessageInfo message); - } -} diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 3b9f3a5b2..21ae6bf61 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -207,6 +207,7 @@ + @@ -218,6 +219,7 @@ + diff --git a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs new file mode 100644 index 000000000..f1e371c1a --- /dev/null +++ b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs @@ -0,0 +1,327 @@ +using MediaBrowser.Common.Net; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Net; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Controller.Net +{ + /// + /// Starts sending data over a web socket periodically when a message is received, and then stops when a corresponding stop message is received + /// + /// The type of the T return data type. + /// The type of the T state type. + public abstract class BasePeriodicWebSocketListener : IWebSocketListener, IDisposable + where TStateType : WebSocketListenerState, new() + where TReturnDataType : class + { + /// + /// The _active connections + /// + protected readonly List> ActiveConnections = + new List>(); + + /// + /// Gets the name. + /// + /// The name. + protected abstract string Name { get; } + + /// + /// Gets the data to send. + /// + /// The state. + /// Task{`1}. + protected abstract Task GetDataToSend(TStateType state); + + /// + /// The logger + /// + protected ILogger Logger; + + /// + /// Initializes a new instance of the class. + /// + /// The logger. + /// logger + protected BasePeriodicWebSocketListener(ILogger logger) + { + if (logger == null) + { + throw new ArgumentNullException("logger"); + } + + Logger = logger; + } + + /// + /// The null task result + /// + protected Task NullTaskResult = Task.FromResult(true); + + /// + /// Processes the message. + /// + /// The message. + /// Task. + public Task ProcessMessage(WebSocketMessageInfo message) + { + if (message.MessageType.Equals(Name + "Start", StringComparison.OrdinalIgnoreCase)) + { + Start(message); + } + + if (message.MessageType.Equals(Name + "Stop", StringComparison.OrdinalIgnoreCase)) + { + Stop(message); + } + + return NullTaskResult; + } + + protected readonly CultureInfo UsCulture = new CultureInfo("en-US"); + + protected virtual bool SendOnTimer + { + get + { + return true; + } + } + + /// + /// Starts sending messages over a web socket + /// + /// The message. + private void Start(WebSocketMessageInfo message) + { + var vals = message.Data.Split(','); + + var dueTimeMs = long.Parse(vals[0], UsCulture); + var periodMs = long.Parse(vals[1], UsCulture); + + var cancellationTokenSource = new CancellationTokenSource(); + + Logger.Info("{1} Begin transmitting over websocket to {0}", message.Connection.RemoteEndPoint, GetType().Name); + + var timer = SendOnTimer ? + new Timer(TimerCallback, message.Connection, Timeout.Infinite, Timeout.Infinite) : + null; + + var state = new TStateType + { + IntervalMs = periodMs, + InitialDelayMs = dueTimeMs + }; + + var semaphore = new SemaphoreSlim(1, 1); + + lock (ActiveConnections) + { + ActiveConnections.Add(new Tuple(message.Connection, cancellationTokenSource, timer, state, semaphore)); + } + + if (timer != null) + { + timer.Change(TimeSpan.FromMilliseconds(dueTimeMs), TimeSpan.FromMilliseconds(periodMs)); + } + } + + /// + /// Timers the callback. + /// + /// The state. + private void TimerCallback(object state) + { + var connection = (IWebSocketConnection)state; + + Tuple tuple; + + lock (ActiveConnections) + { + tuple = ActiveConnections.FirstOrDefault(c => c.Item1 == connection); + } + + if (tuple == null) + { + return; + } + + if (connection.State != WebSocketState.Open || tuple.Item2.IsCancellationRequested) + { + DisposeConnection(tuple); + return; + } + + SendData(tuple); + } + + protected void SendData(bool force) + { + List> tuples; + + lock (ActiveConnections) + { + tuples = ActiveConnections + .Where(c => + { + if (c.Item1.State == WebSocketState.Open && !c.Item2.IsCancellationRequested) + { + var state = c.Item4; + + if (force || (DateTime.UtcNow - state.DateLastSendUtc).TotalMilliseconds >= state.IntervalMs) + { + return true; + } + } + + return false; + }) + .ToList(); + } + + foreach (var tuple in tuples) + { + SendData(tuple); + } + } + + private async void SendData(Tuple tuple) + { + var connection = tuple.Item1; + + try + { + await tuple.Item5.WaitAsync(tuple.Item2.Token).ConfigureAwait(false); + + var state = tuple.Item4; + + var data = await GetDataToSend(state).ConfigureAwait(false); + + if (data != null) + { + await connection.SendAsync(new WebSocketMessage + { + MessageType = Name, + Data = data + + }, tuple.Item2.Token).ConfigureAwait(false); + + state.DateLastSendUtc = DateTime.UtcNow; + } + + tuple.Item5.Release(); + } + catch (OperationCanceledException) + { + if (tuple.Item2.IsCancellationRequested) + { + DisposeConnection(tuple); + } + } + catch (Exception ex) + { + Logger.ErrorException("Error sending web socket message {0}", ex, Name); + DisposeConnection(tuple); + } + } + + /// + /// Stops sending messages over a web socket + /// + /// The message. + private void Stop(WebSocketMessageInfo message) + { + lock (ActiveConnections) + { + var connection = ActiveConnections.FirstOrDefault(c => c.Item1 == message.Connection); + + if (connection != null) + { + DisposeConnection(connection); + } + } + } + + /// + /// Disposes the connection. + /// + /// The connection. + private void DisposeConnection(Tuple connection) + { + Logger.Info("{1} stop transmitting over websocket to {0}", connection.Item1.RemoteEndPoint, GetType().Name); + + var timer = connection.Item3; + + if (timer != null) + { + try + { + timer.Dispose(); + } + catch (ObjectDisposedException) + { + + } + } + + try + { + connection.Item2.Cancel(); + connection.Item2.Dispose(); + } + catch (ObjectDisposedException) + { + + } + + try + { + connection.Item5.Dispose(); + } + catch (ObjectDisposedException) + { + + } + + ActiveConnections.Remove(connection); + } + + /// + /// Releases unmanaged and - optionally - managed resources. + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected virtual void Dispose(bool dispose) + { + if (dispose) + { + lock (ActiveConnections) + { + foreach (var connection in ActiveConnections.ToList()) + { + DisposeConnection(connection); + } + } + } + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + Dispose(true); + } + } + + public class WebSocketListenerState + { + public DateTime DateLastSendUtc { get; set; } + public long InitialDelayMs { get; set; } + public long IntervalMs { get; set; } + } +} diff --git a/MediaBrowser.Controller/Net/IWebSocketListener.cs b/MediaBrowser.Controller/Net/IWebSocketListener.cs new file mode 100644 index 000000000..2b4fc7676 --- /dev/null +++ b/MediaBrowser.Controller/Net/IWebSocketListener.cs @@ -0,0 +1,18 @@ +using MediaBrowser.Common.Net; +using System.Threading.Tasks; + +namespace MediaBrowser.Controller.Net +{ + /// + ///This is an interface for listening to messages coming through a web socket connection + /// + public interface IWebSocketListener + { + /// + /// Processes the message. + /// + /// The message. + /// Task. + Task ProcessMessage(WebSocketMessageInfo message); + } +} diff --git a/MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs b/MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs index ed590a1f2..36a7fcbd8 100644 --- a/MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs +++ b/MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs @@ -1,4 +1,5 @@ using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Serialization; -- cgit v1.2.3