Browse Source

Fix warnings

Bond_009 4 years ago
parent
commit
2b400c99ef
46 changed files with 366 additions and 302 deletions
  1. 6 6
      Emby.Dlna/DlnaManager.cs
  2. 17 19
      Emby.Dlna/PlayTo/Device.cs
  3. 9 8
      Emby.Dlna/PlayTo/TransportCommands.cs
  4. 2 2
      Emby.Server.Implementations/Data/SqliteItemRepository.cs
  5. 1 1
      Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
  6. 2 3
      Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
  7. 54 52
      Emby.Server.Implementations/LiveTv/LiveTvManager.cs
  8. 9 7
      Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
  9. 1 1
      Jellyfin.Api/Controllers/ImageController.cs
  10. 3 1
      MediaBrowser.Controller/Entities/Movies/Movie.cs
  11. 1 1
      MediaBrowser.Controller/Entities/TV/Series.cs
  12. 2 1
      MediaBrowser.Controller/Entities/Trailer.cs
  13. 13 4
      MediaBrowser.Controller/Library/Profiler.cs
  14. 1 1
      MediaBrowser.Controller/LiveTv/LiveTvChannel.cs
  15. 1 1
      MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
  16. 4 4
      MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
  17. 1 1
      MediaBrowser.Controller/Providers/IProviderManager.cs
  18. 5 4
      MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs
  19. 7 7
      MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
  20. 1 1
      MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
  21. 1 1
      MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs
  22. 5 3
      MediaBrowser.Model/Dlna/DlnaMaps.cs
  23. 11 10
      MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs
  24. 6 6
      MediaBrowser.Model/Dlna/StreamInfo.cs
  25. 2 2
      MediaBrowser.Providers/Manager/ImageSaver.cs
  26. 29 28
      MediaBrowser.Providers/Manager/ItemImageProvider.cs
  27. 37 36
      MediaBrowser.Providers/Manager/MetadataService.cs
  28. 28 9
      MediaBrowser.Providers/Manager/ProviderManager.cs
  29. 5 10
      MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs
  30. 5 16
      MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs
  31. 2 2
      MediaBrowser.Providers/Music/Extensions.cs
  32. 1 1
      MediaBrowser.Providers/Plugins/MusicBrainz/AlbumProvider.cs
  33. 2 2
      MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs
  34. 2 1
      MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs
  35. 1 1
      MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs
  36. 17 8
      MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs
  37. 9 22
      MediaBrowser.Providers/Plugins/TheTvdb/TvdbClientManager.cs
  38. 4 3
      MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeImageProvider.cs
  39. 2 2
      MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs
  40. 1 1
      MediaBrowser.Providers/Plugins/Tmdb/Movies/GenericTmdbMovieInfo.cs
  41. 48 6
      MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbSearch.cs
  42. 1 1
      MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs
  43. 2 2
      MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProviderBase.cs
  44. 2 2
      MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs
  45. 1 1
      MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs
  46. 2 1
      MediaBrowser.Providers/Studios/StudiosImageProvider.cs

+ 6 - 6
Emby.Dlna/DlnaManager.cs

@@ -54,11 +54,15 @@ namespace Emby.Dlna
             _appHost = appHost;
         }
 
+        private string UserProfilesPath => Path.Combine(_appPaths.ConfigurationDirectoryPath, "dlna", "user");
+
+        private string SystemProfilesPath => Path.Combine(_appPaths.ConfigurationDirectoryPath, "dlna", "system");
+
         public async Task InitProfilesAsync()
         {
             try
             {
-                await ExtractSystemProfilesAsync();
+                await ExtractSystemProfilesAsync().ConfigureAwait(false);
                 LoadProfiles();
             }
             catch (Exception ex)
@@ -240,7 +244,7 @@ namespace Emby.Dlna
             }
             else
             {
-                var headerString = string.Join(", ", headers.Select(i => string.Format("{0}={1}", i.Key, i.Value)).ToArray());
+                var headerString = string.Join(", ", headers.Select(i => string.Format(CultureInfo.InvariantCulture, "{0}={1}", i.Key, i.Value)));
                 _logger.LogDebug("No matching device profile found. {0}", headerString);
             }
 
@@ -280,10 +284,6 @@ namespace Emby.Dlna
             return false;
         }
 
-        private string UserProfilesPath => Path.Combine(_appPaths.ConfigurationDirectoryPath, "dlna", "user");
-
-        private string SystemProfilesPath => Path.Combine(_appPaths.ConfigurationDirectoryPath, "dlna", "system");
-
         private IEnumerable<DeviceProfile> GetProfiles(string path, DeviceProfileType type)
         {
             try

+ 17 - 19
Emby.Dlna/PlayTo/Device.cs

@@ -19,6 +19,8 @@ namespace Emby.Dlna.PlayTo
 {
     public class Device : IDisposable
     {
+        private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
+
         private Timer _timer;
 
         public DeviceInfo Properties { get; set; }
@@ -55,16 +57,13 @@ namespace Emby.Dlna.PlayTo
 
         private readonly ILogger _logger;
 
-        private readonly IServerConfigurationManager _config;
-
         public Action OnDeviceUnavailable { get; set; }
 
-        public Device(DeviceInfo deviceProperties, IHttpClient httpClient, ILogger logger, IServerConfigurationManager config)
+        public Device(DeviceInfo deviceProperties, IHttpClient httpClient, ILogger logger)
         {
             Properties = deviceProperties;
             _httpClient = httpClient;
             _logger = logger;
-            _config = config;
         }
 
         public void Start()
@@ -275,7 +274,7 @@ namespace Emby.Dlna.PlayTo
                 throw new InvalidOperationException("Unable to find service");
             }
 
-            await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType, string.Format("{0:hh}:{0:mm}:{0:ss}", value), "REL_TIME"))
+            await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType, string.Format(CultureInfo.InvariantCulture, "{0:hh}:{0:mm}:{0:ss}", value), "REL_TIME"))
                 .ConfigureAwait(false);
 
             RestartTimer(true);
@@ -285,7 +284,7 @@ namespace Emby.Dlna.PlayTo
         {
             var avCommands = await GetAVProtocolAsync(cancellationToken).ConfigureAwait(false);
 
-            url = url.Replace("&", "&amp;");
+            url = url.Replace("&", "&amp;", StringComparison.Ordinal);
 
             _logger.LogDebug("{0} - SetAvTransport Uri: {1} DlnaHeaders: {2}", Properties.Name, url, header);
 
@@ -297,8 +296,8 @@ namespace Emby.Dlna.PlayTo
 
             var dictionary = new Dictionary<string, string>
             {
-                {"CurrentURI", url},
-                {"CurrentURIMetaData", CreateDidlMeta(metaData)}
+                { "CurrentURI", url },
+                { "CurrentURIMetaData", CreateDidlMeta(metaData) }
             };
 
             var service = GetAvTransportService();
@@ -732,10 +731,10 @@ namespace Emby.Dlna.PlayTo
             }
 
             var trackUriElem = result.Document.Descendants(uPnpNamespaces.AvTransport + "GetPositionInfoResponse").Select(i => i.Element("TrackURI")).FirstOrDefault(i => i != null);
-            var trackUri = trackUriElem == null ? null : trackUriElem.Value;
+            var trackUri = trackUriElem?.Value;
 
             var durationElem = result.Document.Descendants(uPnpNamespaces.AvTransport + "GetPositionInfoResponse").Select(i => i.Element("TrackDuration")).FirstOrDefault(i => i != null);
-            var duration = durationElem == null ? null : durationElem.Value;
+            var duration = durationElem?.Value;
 
             if (!string.IsNullOrWhiteSpace(duration)
                 && !string.Equals(duration, "NOT_IMPLEMENTED", StringComparison.OrdinalIgnoreCase))
@@ -748,7 +747,7 @@ namespace Emby.Dlna.PlayTo
             }
 
             var positionElem = result.Document.Descendants(uPnpNamespaces.AvTransport + "GetPositionInfoResponse").Select(i => i.Element("RelTime")).FirstOrDefault(i => i != null);
-            var position = positionElem == null ? null : positionElem.Value;
+            var position = positionElem?.Value;
 
             if (!string.IsNullOrWhiteSpace(position) && !string.Equals(position, "NOT_IMPLEMENTED", StringComparison.OrdinalIgnoreCase))
             {
@@ -819,7 +818,7 @@ namespace Emby.Dlna.PlayTo
             // some devices send back invalid xml
             try
             {
-                return XElement.Parse(xml.Replace("&", "&amp;"));
+                return XElement.Parse(xml.Replace("&", "&amp;", StringComparison.Ordinal));
             }
             catch (XmlException)
             {
@@ -848,7 +847,7 @@ namespace Emby.Dlna.PlayTo
                 ParentId = container.GetAttributeValue(uPnpNamespaces.ParentId),
                 Title = container.GetValue(uPnpNamespaces.title),
                 IconUrl = container.GetValue(uPnpNamespaces.Artwork),
-                SecondText = "",
+                SecondText = string.Empty,
                 Url = url,
                 ProtocolInfo = GetProtocolInfo(container),
                 MetaData = container.ToString()
@@ -941,12 +940,12 @@ namespace Emby.Dlna.PlayTo
                 return url;
             }
 
-            if (!url.Contains("/"))
+            if (!url.Contains('/', StringComparison.Ordinal))
             {
                 url = "/dmr/" + url;
             }
 
-            if (!url.StartsWith("/"))
+            if (!url.StartsWith("/", StringComparison.Ordinal))
             {
                 url = "/" + url;
             }
@@ -981,7 +980,7 @@ namespace Emby.Dlna.PlayTo
             var deviceProperties = new DeviceInfo()
             {
                 Name = string.Join(" ", friendlyNames),
-                BaseUrl = string.Format("http://{0}:{1}", url.Host, url.Port)
+                BaseUrl = string.Format(CultureInfo.InvariantCulture, "http://{0}:{1}", url.Host, url.Port)
             };
 
             var model = document.Descendants(uPnpNamespaces.ud.GetName("modelName")).FirstOrDefault();
@@ -1068,10 +1067,9 @@ namespace Emby.Dlna.PlayTo
                 }
             }
 
-            return new Device(deviceProperties, httpClient, logger, config);
+            return new Device(deviceProperties, httpClient, logger);
         }
 
-        private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
         private static DeviceIcon CreateIcon(XElement element)
         {
             if (element == null)
@@ -1222,7 +1220,7 @@ namespace Emby.Dlna.PlayTo
 
         public override string ToString()
         {
-            return string.Format("{0} - {1}", Properties.Name, Properties.BaseUrl);
+            return string.Format(CultureInfo.InvariantCulture, "{0} - {1}", Properties.Name, Properties.BaseUrl);
         }
     }
 }

+ 9 - 8
Emby.Dlna/PlayTo/TransportCommands.cs

@@ -2,6 +2,7 @@
 
 using System;
 using System.Collections.Generic;
+using System.Globalization;
 using System.Linq;
 using System.Xml.Linq;
 using Emby.Dlna.Common;
@@ -11,14 +12,16 @@ namespace Emby.Dlna.PlayTo
 {
     public class TransportCommands
     {
+        private const string CommandBase = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" + "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" + "<SOAP-ENV:Body>" + "<m:{0} xmlns:m=\"{1}\">" + "{2}" + "</m:{0}>" + "</SOAP-ENV:Body></SOAP-ENV:Envelope>";
         private List<StateVariable> _stateVariables = new List<StateVariable>();
+        private List<ServiceAction> _serviceActions = new List<ServiceAction>();
+
         public List<StateVariable> StateVariables
         {
             get => _stateVariables;
             set => _stateVariables = value;
         }
 
-        private List<ServiceAction> _serviceActions = new List<ServiceAction>();
         public List<ServiceAction> ServiceActions
         {
             get => _serviceActions;
@@ -123,7 +126,7 @@ namespace Emby.Dlna.PlayTo
                 }
             }
 
-            return string.Format(CommandBase, action.Name, xmlNamespace, stateString);
+            return string.Format(CultureInfo.InvariantCulture, CommandBase, action.Name, xmlNamespace, stateString);
         }
 
         public string BuildPost(ServiceAction action, string xmlNamesapce, object value, string commandParameter = "")
@@ -147,7 +150,7 @@ namespace Emby.Dlna.PlayTo
                 }
             }
 
-            return string.Format(CommandBase, action.Name, xmlNamesapce, stateString);
+            return string.Format(CultureInfo.InvariantCulture, CommandBase, action.Name, xmlNamesapce, stateString);
         }
 
         public string BuildPost(ServiceAction action, string xmlNamesapce, object value, Dictionary<string, string> dictionary)
@@ -170,7 +173,7 @@ namespace Emby.Dlna.PlayTo
                 }
             }
 
-            return string.Format(CommandBase, action.Name, xmlNamesapce, stateString);
+            return string.Format(CultureInfo.InvariantCulture, CommandBase, action.Name, xmlNamesapce, stateString);
         }
 
         private string BuildArgumentXml(Argument argument, string value, string commandParameter = "")
@@ -183,12 +186,10 @@ namespace Emby.Dlna.PlayTo
                                  state.AllowedValues.FirstOrDefault() ??
                                  value;
 
-                return string.Format("<{0} xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"{1}\">{2}</{0}>", argument.Name, state.DataType ?? "string", sendValue);
+                return string.Format(CultureInfo.InvariantCulture, "<{0} xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"{1}\">{2}</{0}>", argument.Name, state.DataType ?? "string", sendValue);
             }
 
-            return string.Format("<{0}>{1}</{0}>", argument.Name, value);
+            return string.Format(CultureInfo.InvariantCulture, "<{0}>{1}</{0}>", argument.Name, value);
         }
-
-        private const string CommandBase = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" + "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" + "<SOAP-ENV:Body>" + "<m:{0} xmlns:m=\"{1}\">" + "{2}" + "</m:{0}>" + "</SOAP-ENV:Body></SOAP-ENV:Envelope>";
     }
 }

+ 2 - 2
Emby.Server.Implementations/Data/SqliteItemRepository.cs

@@ -4560,13 +4560,13 @@ namespace Emby.Server.Implementations.Data
             if (query.AncestorIds.Length > 1)
             {
                 var inClause = string.Join(",", query.AncestorIds.Select(i => "'" + i.ToString("N", CultureInfo.InvariantCulture) + "'"));
-                whereClauses.Add(string.Format("Guid in (select itemId from AncestorIds where AncestorIdText in ({0}))", inClause));
+                whereClauses.Add(string.Format(CultureInfo.InvariantCulture, "Guid in (select itemId from AncestorIds where AncestorIdText in ({0}))", inClause));
             }
 
             if (!string.IsNullOrWhiteSpace(query.AncestorWithPresentationUniqueKey))
             {
                 var inClause = "select guid from TypedBaseItems where PresentationUniqueKey=@AncestorWithPresentationUniqueKey";
-                whereClauses.Add(string.Format("Guid in (select itemId from AncestorIds where AncestorId in ({0}))", inClause));
+                whereClauses.Add(string.Format(CultureInfo.InvariantCulture, "Guid in (select itemId from AncestorIds where AncestorId in ({0}))", inClause));
                 if (statement != null)
                 {
                     statement.TryBind("@AncestorWithPresentationUniqueKey", query.AncestorWithPresentationUniqueKey);

+ 1 - 1
Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs

@@ -230,7 +230,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
 
             if (filters.Count > 0)
             {
-                output += string.Format(" -vf \"{0}\"", string.Join(",", filters.ToArray()));
+                output += string.Format(CultureInfo.InvariantCulture, " -vf \"{0}\"", string.Join(",", filters.ToArray()));
             }
 
             return output;

+ 2 - 3
Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs

@@ -237,7 +237,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
                     && !programInfo.IsRepeat
                     && (programInfo.EpisodeNumber ?? 0) == 0)
                 {
-                    programInfo.ShowId = programInfo.ShowId + programInfo.StartDate.Ticks.ToString(CultureInfo.InvariantCulture);
+                    programInfo.ShowId += programInfo.StartDate.Ticks.ToString(CultureInfo.InvariantCulture);
                 }
             }
             else
@@ -246,7 +246,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
             }
 
             // Construct an id from the channel and start date
-            programInfo.Id = string.Format("{0}_{1:O}", program.ChannelId, program.StartDate);
+            programInfo.Id = string.Format(CultureInfo.InvariantCulture, "{0}_{1:O}", program.ChannelId, program.StartDate);
 
             if (programInfo.IsMovie)
             {
@@ -296,7 +296,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings
                 Name = c.DisplayName,
                 ImageUrl = c.Icon != null && !string.IsNullOrEmpty(c.Icon.Source) ? c.Icon.Source : null,
                 Number = string.IsNullOrWhiteSpace(c.Number) ? c.Id : c.Number
-
             }).ToList();
         }
     }

+ 54 - 52
Emby.Server.Implementations/LiveTv/LiveTvManager.cs

@@ -41,6 +41,7 @@ namespace Emby.Server.Implementations.LiveTv
     /// </summary>
     public class LiveTvManager : ILiveTvManager, IDisposable
     {
+        private const int MaxGuideDays = 14;
         private const string ExternalServiceTag = "ExternalServiceId";
 
         private const string EtagKey = "ProgramEtag";
@@ -560,7 +561,7 @@ namespace Emby.Server.Implementations.LiveTv
 
             item.Audio = info.Audio;
             item.ChannelId = channel.Id;
-            item.CommunityRating = item.CommunityRating ?? info.CommunityRating;
+            item.CommunityRating ??= info.CommunityRating;
             if ((item.CommunityRating ?? 0).Equals(0))
             {
                 item.CommunityRating = null;
@@ -645,8 +646,8 @@ namespace Emby.Server.Implementations.LiveTv
             item.IsSeries = isSeries;
 
             item.Name = info.Name;
-            item.OfficialRating = item.OfficialRating ?? info.OfficialRating;
-            item.Overview = item.Overview ?? info.Overview;
+            item.OfficialRating ??= info.OfficialRating;
+            item.Overview ??= info.Overview;
             item.RunTimeTicks = (info.EndDate - info.StartDate).Ticks;
             item.ProviderIds = info.ProviderIds;
 
@@ -683,19 +684,23 @@ namespace Emby.Server.Implementations.LiveTv
             {
                 if (!string.IsNullOrWhiteSpace(info.ImagePath))
                 {
-                    item.SetImage(new ItemImageInfo
-                    {
-                        Path = info.ImagePath,
-                        Type = ImageType.Primary
-                    }, 0);
+                    item.SetImage(
+                        new ItemImageInfo
+                        {
+                            Path = info.ImagePath,
+                            Type = ImageType.Primary
+                        },
+                        0);
                 }
                 else if (!string.IsNullOrWhiteSpace(info.ImageUrl))
                 {
-                    item.SetImage(new ItemImageInfo
-                    {
-                        Path = info.ImageUrl,
-                        Type = ImageType.Primary
-                    }, 0);
+                    item.SetImage(
+                        new ItemImageInfo
+                        {
+                            Path = info.ImageUrl,
+                            Type = ImageType.Primary
+                        },
+                        0);
                 }
             }
 
@@ -703,11 +708,13 @@ namespace Emby.Server.Implementations.LiveTv
             {
                 if (!string.IsNullOrWhiteSpace(info.ThumbImageUrl))
                 {
-                    item.SetImage(new ItemImageInfo
-                    {
-                        Path = info.ThumbImageUrl,
-                        Type = ImageType.Thumb
-                    }, 0);
+                    item.SetImage(
+                        new ItemImageInfo
+                        {
+                            Path = info.ThumbImageUrl,
+                            Type = ImageType.Thumb
+                        },
+                        0);
                 }
             }
 
@@ -715,11 +722,13 @@ namespace Emby.Server.Implementations.LiveTv
             {
                 if (!string.IsNullOrWhiteSpace(info.LogoImageUrl))
                 {
-                    item.SetImage(new ItemImageInfo
-                    {
-                        Path = info.LogoImageUrl,
-                        Type = ImageType.Logo
-                    }, 0);
+                    item.SetImage(
+                        new ItemImageInfo
+                        {
+                            Path = info.LogoImageUrl,
+                            Type = ImageType.Logo
+                        },
+                        0);
                 }
             }
 
@@ -727,11 +736,13 @@ namespace Emby.Server.Implementations.LiveTv
             {
                 if (!string.IsNullOrWhiteSpace(info.BackdropImageUrl))
                 {
-                    item.SetImage(new ItemImageInfo
-                    {
-                        Path = info.BackdropImageUrl,
-                        Type = ImageType.Backdrop
-                    }, 0);
+                    item.SetImage(
+                        new ItemImageInfo
+                        {
+                            Path = info.BackdropImageUrl,
+                            Type = ImageType.Backdrop
+                        },
+                        0);
                 }
             }
 
@@ -786,7 +797,6 @@ namespace Emby.Server.Implementations.LiveTv
 
             if (query.OrderBy.Count == 0)
             {
-
                 // Unless something else was specified, order by start date to take advantage of a specialized index
                 query.OrderBy = new[]
                 {
@@ -824,7 +834,7 @@ namespace Emby.Server.Implementations.LiveTv
 
             if (!string.IsNullOrWhiteSpace(query.SeriesTimerId))
             {
-                var seriesTimers = await GetSeriesTimersInternal(new SeriesTimerQuery { }, cancellationToken).ConfigureAwait(false);
+                var seriesTimers = await GetSeriesTimersInternal(new SeriesTimerQuery(), cancellationToken).ConfigureAwait(false);
                 var seriesTimer = seriesTimers.Items.FirstOrDefault(i => string.Equals(_tvDtoService.GetInternalSeriesTimerId(i.Id).ToString("N", CultureInfo.InvariantCulture), query.SeriesTimerId, StringComparison.OrdinalIgnoreCase));
                 if (seriesTimer != null)
                 {
@@ -847,13 +857,11 @@ namespace Emby.Server.Implementations.LiveTv
 
             var returnArray = _dtoService.GetBaseItemDtos(queryResult.Items, options, user);
 
-            var result = new QueryResult<BaseItemDto>
+            return new QueryResult<BaseItemDto>
             {
                 Items = returnArray,
                 TotalRecordCount = queryResult.TotalRecordCount
             };
-
-            return result;
         }
 
         public QueryResult<BaseItem> GetRecommendedProgramsInternal(InternalItemsQuery query, DtoOptions options, CancellationToken cancellationToken)
@@ -1173,7 +1181,6 @@ namespace Emby.Server.Implementations.LiveTv
 
                     var existingPrograms = _libraryManager.GetItemList(new InternalItemsQuery
                     {
-
                         IncludeItemTypes = new string[] { typeof(LiveTvProgram).Name },
                         ChannelIds = new Guid[] { currentChannel.Id },
                         DtoOptions = new DtoOptions(true)
@@ -1298,8 +1305,6 @@ namespace Emby.Server.Implementations.LiveTv
             }
         }
 
-        private const int MaxGuideDays = 14;
-
         private double GetGuideDays()
         {
             var config = GetConfiguration();
@@ -1712,7 +1717,7 @@ namespace Emby.Server.Implementations.LiveTv
 
             if (timer == null)
             {
-                throw new ResourceNotFoundException(string.Format("Timer with Id {0} not found", id));
+                throw new ResourceNotFoundException(string.Format(CultureInfo.InvariantCulture, "Timer with Id {0} not found", id));
             }
 
             var service = GetService(timer.ServiceName);
@@ -1731,7 +1736,7 @@ namespace Emby.Server.Implementations.LiveTv
 
             if (timer == null)
             {
-                throw new ResourceNotFoundException(string.Format("SeriesTimer with Id {0} not found", id));
+                throw new ResourceNotFoundException(string.Format(CultureInfo.InvariantCulture, "SeriesTimer with Id {0} not found", id));
             }
 
             var service = GetService(timer.ServiceName);
@@ -1743,10 +1748,12 @@ namespace Emby.Server.Implementations.LiveTv
 
         public async Task<TimerInfoDto> GetTimer(string id, CancellationToken cancellationToken)
         {
-            var results = await GetTimers(new TimerQuery
-            {
-                Id = id
-            }, cancellationToken).ConfigureAwait(false);
+            var results = await GetTimers(
+                new TimerQuery
+                {
+                    Id = id
+                },
+                cancellationToken).ConfigureAwait(false);
 
             return results.Items.FirstOrDefault(i => string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase));
         }
@@ -1794,10 +1801,7 @@ namespace Emby.Server.Implementations.LiveTv
             }
 
             var returnArray = timers
-                .Select(i =>
-                {
-                    return i.Item1;
-                })
+                .Select(i => i.Item1)
                 .ToArray();
 
             return new QueryResult<SeriesTimerInfo>
@@ -1968,7 +1972,7 @@ namespace Emby.Server.Implementations.LiveTv
 
             if (service == null)
             {
-                service = _services.First();
+                service = _services[0];
             }
 
             var info = await service.GetNewTimerDefaultsAsync(cancellationToken, programInfo).ConfigureAwait(false);
@@ -1994,9 +1998,7 @@ namespace Emby.Server.Implementations.LiveTv
         {
             var info = await GetNewTimerDefaultsInternal(cancellationToken).ConfigureAwait(false);
 
-            var obj = _tvDtoService.GetSeriesTimerInfoDto(info.Item1, info.Item2, null);
-
-            return obj;
+            return _tvDtoService.GetSeriesTimerInfoDto(info.Item1, info.Item2, null);
         }
 
         public async Task<SeriesTimerInfoDto> GetNewTimerDefaults(string programId, CancellationToken cancellationToken)
@@ -2125,6 +2127,7 @@ namespace Emby.Server.Implementations.LiveTv
         public void Dispose()
         {
             Dispose(true);
+            GC.SuppressFinalize(this);
         }
 
         private bool _disposed = false;
@@ -2447,8 +2450,7 @@ namespace Emby.Server.Implementations.LiveTv
                 .SelectMany(i => i.Locations)
                 .Distinct(StringComparer.OrdinalIgnoreCase)
                 .Select(i => _libraryManager.FindByPath(i, true))
-                .Where(i => i != null)
-                .Where(i => i.IsVisibleStandalone(user))
+                .Where(i => i != null && i.IsVisibleStandalone(user))
                 .SelectMany(i => _libraryManager.GetCollectionFolders(i))
                 .GroupBy(x => x.Id)
                 .Select(x => x.First())

+ 9 - 7
Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs

@@ -182,12 +182,14 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
         {
             var model = await GetModelInfo(info, false, cancellationToken).ConfigureAwait(false);
 
-            using (var response = await _httpClient.SendAsync(new HttpRequestOptions()
-            {
-                Url = string.Format("{0}/tuners.html", GetApiUrl(info)),
-                CancellationToken = cancellationToken,
-                BufferContent = false
-            }, HttpMethod.Get).ConfigureAwait(false))
+            using (var response = await _httpClient.SendAsync(
+                new HttpRequestOptions()
+                {
+                    Url = string.Format(CultureInfo.InvariantCulture, "{0}/tuners.html", GetApiUrl(info)),
+                    CancellationToken = cancellationToken,
+                    BufferContent = false
+                },
+                HttpMethod.Get).ConfigureAwait(false))
             using (var stream = response.Content)
             using (var sr = new StreamReader(stream, System.Text.Encoding.UTF8))
             {
@@ -730,7 +732,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
                 // Need a way to set the Receive timeout on the socket otherwise this might never timeout?
                 try
                 {
-                    await udpClient.SendToAsync(discBytes, 0, discBytes.Length, new IPEndPoint(IPAddress.Parse("255.255.255.255"), 65001), cancellationToken);
+                    await udpClient.SendToAsync(discBytes, 0, discBytes.Length, new IPEndPoint(IPAddress.Parse("255.255.255.255"), 65001), cancellationToken).ConfigureAwait(false);
                     var receiveBuffer = new byte[8192];
 
                     while (!cancellationToken.IsCancellationRequested)

+ 1 - 1
Jellyfin.Api/Controllers/ImageController.cs

@@ -113,7 +113,7 @@ namespace Jellyfin.Api.Controllers
             user.ProfileImage = new Data.Entities.ImageInfo(Path.Combine(userDataPath, "profile" + MimeTypes.ToExtension(mimeType)));
 
             await _providerManager
-                .SaveImage(user, memoryStream, mimeType, user.ProfileImage.Path)
+                .SaveImage(memoryStream, mimeType, user.ProfileImage.Path)
                 .ConfigureAwait(false);
             await _userManager.UpdateUserAsync(user).ConfigureAwait(false);
 

+ 3 - 1
MediaBrowser.Controller/Entities/Movies/Movie.cs

@@ -1,5 +1,7 @@
+
 using System;
 using System.Collections.Generic;
+using System.Globalization;
 using System.Linq;
 using System.Text.Json.Serialization;
 using System.Threading;
@@ -179,7 +181,7 @@ namespace MediaBrowser.Controller.Entities.Movies
                 list.Add(new ExternalUrl
                 {
                     Name = "Trakt",
-                    Url = string.Format("https://trakt.tv/movies/{0}", imdbId)
+                    Url = string.Format(CultureInfo.InvariantCulture, "https://trakt.tv/movies/{0}", imdbId)
                 });
             }
 

+ 1 - 1
MediaBrowser.Controller/Entities/TV/Series.cs

@@ -496,7 +496,7 @@ namespace MediaBrowser.Controller.Entities.TV
                 list.Add(new ExternalUrl
                 {
                     Name = "Trakt",
-                    Url = string.Format("https://trakt.tv/shows/{0}", imdbId)
+                    Url = string.Format(CultureInfo.InvariantCulture, "https://trakt.tv/shows/{0}", imdbId)
                 });
             }
 

+ 2 - 1
MediaBrowser.Controller/Entities/Trailer.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Collections.Generic;
+using System.Globalization;
 using System.Text.Json.Serialization;
 using Jellyfin.Data.Enums;
 using MediaBrowser.Controller.Providers;
@@ -86,7 +87,7 @@ namespace MediaBrowser.Controller.Entities
                 list.Add(new ExternalUrl
                 {
                     Name = "Trakt",
-                    Url = string.Format("https://trakt.tv/movies/{0}", imdbId)
+                    Url = string.Format(CultureInfo.InvariantCulture, "https://trakt.tv/movies/{0}", imdbId)
                 });
             }
 

+ 13 - 4
MediaBrowser.Controller/Library/Profiler.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Diagnostics;
+using System.Globalization;
 using Microsoft.Extensions.Logging;
 
 namespace MediaBrowser.Controller.Library
@@ -13,6 +14,7 @@ namespace MediaBrowser.Controller.Library
         /// The name.
         /// </summary>
         readonly string _name;
+
         /// <summary>
         /// The stopwatch.
         /// </summary>
@@ -44,6 +46,7 @@ namespace MediaBrowser.Controller.Library
         public void Dispose()
         {
             Dispose(true);
+            GC.SuppressFinalize(this);
         }
 
         /// <summary>
@@ -58,13 +61,19 @@ namespace MediaBrowser.Controller.Library
                 string message;
                 if (_stopwatch.ElapsedMilliseconds > 300000)
                 {
-                    message = string.Format("{0} took {1} minutes.",
-                        _name, ((float)_stopwatch.ElapsedMilliseconds / 60000).ToString("F"));
+                    message = string.Format(
+                        CultureInfo.InvariantCulture,
+                        "{0} took {1} minutes.",
+                        _name,
+                        ((float)_stopwatch.ElapsedMilliseconds / 60000).ToString("F", CultureInfo.InvariantCulture));
                 }
                 else
                 {
-                    message = string.Format("{0} took {1} seconds.",
-                        _name, ((float)_stopwatch.ElapsedMilliseconds / 1000).ToString("#0.000"));
+                    message = string.Format(
+                        CultureInfo.InvariantCulture,
+                        "{0} took {1} seconds.",
+                        _name,
+                        ((float)_stopwatch.ElapsedMilliseconds / 1000).ToString("#0.000", CultureInfo.InvariantCulture));
                 }
 
                 _logger.LogInformation(message);

+ 1 - 1
MediaBrowser.Controller/LiveTv/LiveTvChannel.cs

@@ -63,7 +63,7 @@ namespace MediaBrowser.Controller.LiveTv
 
                 if (double.TryParse(Number, NumberStyles.Any, CultureInfo.InvariantCulture, out number))
                 {
-                    return string.Format("{0:00000.0}", number) + "-" + (Name ?? string.Empty);
+                    return string.Format(CultureInfo.InvariantCulture, "{0:00000.0}", number) + "-" + (Name ?? string.Empty);
                 }
             }
 

+ 1 - 1
MediaBrowser.Controller/LiveTv/LiveTvProgram.cs

@@ -261,7 +261,7 @@ namespace MediaBrowser.Controller.LiveTv
                     list.Add(new ExternalUrl
                     {
                         Name = "Trakt",
-                        Url = string.Format("https://trakt.tv/movies/{0}", imdbId)
+                        Url = string.Format(CultureInfo.InvariantCulture, "https://trakt.tv/movies/{0}", imdbId)
                     });
                 }
             }

+ 4 - 4
MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs

@@ -675,7 +675,7 @@ namespace MediaBrowser.Controller.MediaEncoding
             //     }
             // }
 
-            // fallbackFontParam = string.Format(":force_style='FontName=Droid Sans Fallback':fontsdir='{0}'", _mediaEncoder.EscapeSubtitleFilterPath(_fileSystem.GetDirectoryName(fallbackFontPath)));
+            // fallbackFontParam = string.Format(CultureInfo.InvariantCulture, ":force_style='FontName=Droid Sans Fallback':fontsdir='{0}'", _mediaEncoder.EscapeSubtitleFilterPath(_fileSystem.GetDirectoryName(fallbackFontPath)));
 
             if (state.SubtitleStream.IsExternal)
             {
@@ -880,7 +880,7 @@ namespace MediaBrowser.Controller.MediaEncoding
                 profileScore = Math.Min(profileScore, 2);
 
                 // http://www.webmproject.org/docs/encoder-parameters/
-                param += string.Format("-speed 16 -quality good -profile:v {0} -slices 8 -crf {1} -qmin {2} -qmax {3}",
+                param += string.Format(CultureInfo.InvariantCulture, "-speed 16 -quality good -profile:v {0} -slices 8 -crf {1} -qmin {2} -qmax {3}",
                     profileScore.ToString(_usCulture),
                     crf,
                     qmin,
@@ -904,7 +904,7 @@ namespace MediaBrowser.Controller.MediaEncoding
             var framerate = GetFramerateParam(state);
             if (framerate.HasValue)
             {
-                param += string.Format(" -r {0}", framerate.Value.ToString(_usCulture));
+                param += string.Format(CultureInfo.InvariantCulture, " -r {0}", framerate.Value.ToString(_usCulture));
             }
 
             var targetVideoCodec = state.ActualOutputVideoCodec;
@@ -1484,7 +1484,7 @@ namespace MediaBrowser.Controller.MediaEncoding
 
             if (time > 0)
             {
-                return string.Format("-ss {0}", _mediaEncoder.GetTimeParameter(time));
+                return string.Format(CultureInfo.InvariantCulture, "-ss {0}", _mediaEncoder.GetTimeParameter(time));
             }
 
             return string.Empty;

+ 1 - 1
MediaBrowser.Controller/Providers/IProviderManager.cs

@@ -72,7 +72,7 @@ namespace MediaBrowser.Controller.Providers
         /// <returns>Task.</returns>
         Task SaveImage(BaseItem item, string source, string mimeType, ImageType type, int? imageIndex, bool? saveLocallyWithMedia, CancellationToken cancellationToken);
 
-        Task SaveImage(User user, Stream source, string mimeType, string path);
+        Task SaveImage(Stream source, string mimeType, string path);
 
         /// <summary>
         /// Adds the metadata providers.

+ 5 - 4
MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs

@@ -1,6 +1,7 @@
 #pragma warning disable CS1591
 
 using System.Collections.Generic;
+using System.Globalization;
 using System.Linq;
 using MediaBrowser.Model.MediaInfo;
 
@@ -14,7 +15,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
             {
                 var url = inputFiles[0];
 
-                return string.Format("\"{0}\"", url);
+                return string.Format(CultureInfo.InvariantCulture, "\"{0}\"", url);
             }
 
             return GetConcatInputArgument(inputFiles);
@@ -33,7 +34,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
             {
                 var files = string.Join("|", inputFiles.Select(NormalizePath));
 
-                return string.Format("concat:\"{0}\"", files);
+                return string.Format(CultureInfo.InvariantCulture, "concat:\"{0}\"", files);
             }
 
             // Determine the input path for video files
@@ -49,13 +50,13 @@ namespace MediaBrowser.MediaEncoding.Encoder
         {
             if (path.IndexOf("://") != -1)
             {
-                return string.Format("\"{0}\"", path);
+                return string.Format(CultureInfo.InvariantCulture, "\"{0}\"", path);
             }
 
             // Quotes are valid path characters in linux and they need to be escaped here with a leading \
             path = NormalizePath(path);
 
-            return string.Format("file:\"{0}\"", path);
+            return string.Format(CultureInfo.InvariantCulture, "file:\"{0}\"", path);
         }
 
         /// <summary>

+ 7 - 7
MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs

@@ -552,8 +552,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
             // Use ffmpeg to sample 100 (we can drop this if required using thumbnail=50 for 50 frames) frames and pick the best thumbnail. Have a fall back just in case.
             var thumbnail = enableThumbnail ? ",thumbnail=24" : string.Empty;
 
-            var args = useIFrame ? string.Format("-i {0}{3} -threads 0 -v quiet -vframes 1 -vf \"{2}{4}\" -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg, thumbnail) :
-                string.Format("-i {0}{3} -threads 0 -v quiet -vframes 1 -vf \"{2}\" -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg);
+            var args = useIFrame ? string.Format(CultureInfo.InvariantCulture, "-i {0}{3} -threads 0 -v quiet -vframes 1 -vf \"{2}{4}\" -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg, thumbnail) :
+                string.Format(CultureInfo.InvariantCulture, "-i {0}{3} -threads 0 -v quiet -vframes 1 -vf \"{2}\" -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg);
 
             var probeSizeArgument = EncodingHelper.GetProbeSizeArgument(1);
             var analyzeDurationArgument = EncodingHelper.GetAnalyzeDurationArgument(1);
@@ -570,7 +570,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
 
             if (offset.HasValue)
             {
-                args = string.Format("-ss {0} ", GetTimeParameter(offset.Value)) + args;
+                args = string.Format(CultureInfo.InvariantCulture, "-ss {0} ", GetTimeParameter(offset.Value)) + args;
             }
 
             if (videoStream != null)
@@ -641,7 +641,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
 
                 if (exitCode == -1 || !file.Exists || file.Length == 0)
                 {
-                    var msg = string.Format("ffmpeg image extraction failed for {0}", inputPath);
+                    var msg = string.Format(CultureInfo.InvariantCulture, "ffmpeg image extraction failed for {0}", inputPath);
 
                     _logger.LogError(msg);
 
@@ -684,13 +684,13 @@ namespace MediaBrowser.MediaEncoding.Encoder
             {
                 var maxWidthParam = maxWidth.Value.ToString(_usCulture);
 
-                vf += string.Format(",scale=min(iw\\,{0}):trunc(ow/dar/2)*2", maxWidthParam);
+                vf += string.Format(CultureInfo.InvariantCulture, ",scale=min(iw\\,{0}):trunc(ow/dar/2)*2", maxWidthParam);
             }
 
             Directory.CreateDirectory(targetDirectory);
             var outputPath = Path.Combine(targetDirectory, filenamePrefix + "%05d.jpg");
 
-            var args = string.Format("-i {0} -threads 0 -v quiet -vf \"{2}\" -f image2 \"{1}\"", inputArgument, outputPath, vf);
+            var args = string.Format(CultureInfo.InvariantCulture, "-i {0} -threads 0 -v quiet -vf \"{2}\" -f image2 \"{1}\"", inputArgument, outputPath, vf);
 
             var probeSizeArgument = EncodingHelper.GetProbeSizeArgument(1);
             var analyzeDurationArgument = EncodingHelper.GetAnalyzeDurationArgument(1);
@@ -790,7 +790,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
 
                 if (exitCode == -1)
                 {
-                    var msg = string.Format("ffmpeg image extraction failed for {0}", inputArgument);
+                    var msg = string.Format(CultureInfo.InvariantCulture, "ffmpeg image extraction failed for {0}", inputArgument);
 
                     _logger.LogError(msg);
 

+ 1 - 1
MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs

@@ -435,7 +435,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
                         CreateNoWindow = true,
                         UseShellExecute = false,
                         FileName = _mediaEncoder.EncoderPath,
-                        Arguments = string.Format("{0} -i \"{1}\" -c:s srt \"{2}\"", encodingParam, inputPath, outputPath),
+                        Arguments = string.Format(CultureInfo.InvariantCulture, "{0} -i \"{1}\" -c:s srt \"{2}\"", encodingParam, inputPath, outputPath),
                         WindowStyle = ProcessWindowStyle.Hidden,
                         ErrorDialog = false
                     },

+ 1 - 1
MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs

@@ -157,7 +157,7 @@ namespace MediaBrowser.Model.Dlna
             //    flagValue = flagValue | DlnaFlags.TimeBasedSeek;
             //}
 
-            string dlnaflags = string.Format(";DLNA.ORG_FLAGS={0}",
+            string dlnaflags = string.Format(CultureInfo.InvariantCulture, ";DLNA.ORG_FLAGS={0}",
              DlnaMaps.FlagsToString(flagValue));
 
             ResponseProfile mediaProfile = _profile.GetVideoMediaProfile(container,

+ 5 - 3
MediaBrowser.Model/Dlna/DlnaMaps.cs

@@ -1,18 +1,20 @@
 #pragma warning disable CS1591
 
+using System.Globalization;
+
 namespace MediaBrowser.Model.Dlna
 {
     public static class DlnaMaps
     {
         private static readonly string DefaultStreaming =
-             FlagsToString(DlnaFlags.StreamingTransferMode |
+            FlagsToString(DlnaFlags.StreamingTransferMode |
                            DlnaFlags.BackgroundTransferMode |
                            DlnaFlags.ConnectionStall |
                            DlnaFlags.ByteBasedSeek |
                            DlnaFlags.DlnaV15);
 
         private static readonly string DefaultInteractive =
-          FlagsToString(DlnaFlags.InteractiveTransferMode |
+            FlagsToString(DlnaFlags.InteractiveTransferMode |
                         DlnaFlags.BackgroundTransferMode |
                         DlnaFlags.ConnectionStall |
                         DlnaFlags.ByteBasedSeek |
@@ -20,7 +22,7 @@ namespace MediaBrowser.Model.Dlna
 
         public static string FlagsToString(DlnaFlags flags)
         {
-            return string.Format("{0:X8}{1:D24}", (ulong)flags, 0);
+            return string.Format(CultureInfo.InvariantCulture, "{0:X8}{1:D24}", (ulong)flags, 0);
         }
 
         public static string GetOrgOpValue(bool hasKnownRuntime, bool isDirectStream, TranscodeSeekInfo profileTranscodeSeekInfo)

+ 11 - 10
MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs

@@ -3,6 +3,7 @@
 
 using System;
 using System.Collections.Generic;
+using System.Globalization;
 using System.Linq;
 using MediaBrowser.Model.MediaInfo;
 
@@ -142,26 +143,26 @@ namespace MediaBrowser.Model.Dlna
                 {
                     if (timestampType == TransportStreamTimestamp.None)
                     {
-                        return new MediaFormatProfile[] { ValueOf(string.Format("AVC_TS_HP_{0}D_MPEG1_L2_ISO", resolution)) };
+                        return new MediaFormatProfile[] { ValueOf(string.Format(CultureInfo.InvariantCulture, "AVC_TS_HP_{0}D_MPEG1_L2_ISO", resolution)) };
                     }
 
-                    return new MediaFormatProfile[] { ValueOf(string.Format("AVC_TS_HP_{0}D_MPEG1_L2_T", resolution)) };
+                    return new MediaFormatProfile[] { ValueOf(string.Format(CultureInfo.InvariantCulture, "AVC_TS_HP_{0}D_MPEG1_L2_T", resolution)) };
                 }
 
                 if (string.Equals(audioCodec, "aac", StringComparison.OrdinalIgnoreCase))
                 {
-                    return new MediaFormatProfile[] { ValueOf(string.Format("AVC_TS_MP_{0}D_AAC_MULT5{1}", resolution, suffix)) };
+                    return new MediaFormatProfile[] { ValueOf(string.Format(CultureInfo.InvariantCulture, "AVC_TS_MP_{0}D_AAC_MULT5{1}", resolution, suffix)) };
                 }
 
                 if (string.Equals(audioCodec, "mp3", StringComparison.OrdinalIgnoreCase))
                 {
-                    return new MediaFormatProfile[] { ValueOf(string.Format("AVC_TS_MP_{0}D_MPEG1_L3{1}", resolution, suffix)) };
+                    return new MediaFormatProfile[] { ValueOf(string.Format(CultureInfo.InvariantCulture, "AVC_TS_MP_{0}D_MPEG1_L3{1}", resolution, suffix)) };
                 }
 
                 if (string.IsNullOrEmpty(audioCodec) ||
                     string.Equals(audioCodec, "ac3", StringComparison.OrdinalIgnoreCase))
                 {
-                    return new MediaFormatProfile[] { ValueOf(string.Format("AVC_TS_MP_{0}D_AC3{1}", resolution, suffix)) };
+                    return new MediaFormatProfile[] { ValueOf(string.Format(CultureInfo.InvariantCulture, "AVC_TS_MP_{0}D_AC3{1}", resolution, suffix)) };
                 }
             }
             else if (string.Equals(videoCodec, "vc1", StringComparison.OrdinalIgnoreCase))
@@ -180,29 +181,29 @@ namespace MediaBrowser.Model.Dlna
                 {
                     suffix = string.Equals(suffix, "_ISO", StringComparison.OrdinalIgnoreCase) ? suffix : "_T";
 
-                    return new MediaFormatProfile[] { ValueOf(string.Format("VC1_TS_HD_DTS{0}", suffix)) };
+                    return new MediaFormatProfile[] { ValueOf(string.Format(CultureInfo.InvariantCulture, "VC1_TS_HD_DTS{0}", suffix)) };
                 }
             }
             else if (string.Equals(videoCodec, "mpeg4", StringComparison.OrdinalIgnoreCase) || string.Equals(videoCodec, "msmpeg4", StringComparison.OrdinalIgnoreCase))
             {
                 if (string.Equals(audioCodec, "aac", StringComparison.OrdinalIgnoreCase))
                 {
-                    return new MediaFormatProfile[] { ValueOf(string.Format("MPEG4_P2_TS_ASP_AAC{0}", suffix)) };
+                    return new MediaFormatProfile[] { ValueOf(string.Format(CultureInfo.InvariantCulture, "MPEG4_P2_TS_ASP_AAC{0}", suffix)) };
                 }
 
                 if (string.Equals(audioCodec, "mp3", StringComparison.OrdinalIgnoreCase))
                 {
-                    return new MediaFormatProfile[] { ValueOf(string.Format("MPEG4_P2_TS_ASP_MPEG1_L3{0}", suffix)) };
+                    return new MediaFormatProfile[] { ValueOf(string.Format(CultureInfo.InvariantCulture, "MPEG4_P2_TS_ASP_MPEG1_L3{0}", suffix)) };
                 }
 
                 if (string.Equals(audioCodec, "mp2", StringComparison.OrdinalIgnoreCase))
                 {
-                    return new MediaFormatProfile[] { ValueOf(string.Format("MPEG4_P2_TS_ASP_MPEG2_L2{0}", suffix)) };
+                    return new MediaFormatProfile[] { ValueOf(string.Format(CultureInfo.InvariantCulture, "MPEG4_P2_TS_ASP_MPEG2_L2{0}", suffix)) };
                 }
 
                 if (string.Equals(audioCodec, "ac3", StringComparison.OrdinalIgnoreCase))
                 {
-                    return new MediaFormatProfile[] { ValueOf(string.Format("MPEG4_P2_TS_ASP_AC3{0}", suffix)) };
+                    return new MediaFormatProfile[] { ValueOf(string.Format(CultureInfo.InvariantCulture, "MPEG4_P2_TS_ASP_AC3{0}", suffix)) };
                 }
             }
 

+ 6 - 6
MediaBrowser.Model/Dlna/StreamInfo.cs

@@ -191,7 +191,7 @@ namespace MediaBrowser.Model.Dlna
 
                 var encodedValue = pair.Value.Replace(" ", "%20");
 
-                list.Add(string.Format("{0}={1}", pair.Name, encodedValue));
+                list.Add(string.Format(CultureInfo.InvariantCulture, "{0}={1}", pair.Name, encodedValue));
             }
 
             string queryString = string.Join("&", list.ToArray());
@@ -214,18 +214,18 @@ namespace MediaBrowser.Model.Dlna
             {
                 if (string.Equals(SubProtocol, "hls", StringComparison.OrdinalIgnoreCase))
                 {
-                    return string.Format("{0}/audio/{1}/master.m3u8?{2}", baseUrl, ItemId, queryString);
+                    return string.Format(CultureInfo.InvariantCulture, "{0}/audio/{1}/master.m3u8?{2}", baseUrl, ItemId, queryString);
                 }
 
-                return string.Format("{0}/audio/{1}/stream{2}?{3}", baseUrl, ItemId, extension, queryString);
+                return string.Format(CultureInfo.InvariantCulture, "{0}/audio/{1}/stream{2}?{3}", baseUrl, ItemId, extension, queryString);
             }
 
             if (string.Equals(SubProtocol, "hls", StringComparison.OrdinalIgnoreCase))
             {
-                return string.Format("{0}/videos/{1}/master.m3u8?{2}", baseUrl, ItemId, queryString);
+                return string.Format(CultureInfo.InvariantCulture, "{0}/videos/{1}/master.m3u8?{2}", baseUrl, ItemId, queryString);
             }
 
-            return string.Format("{0}/videos/{1}/stream{2}?{3}", baseUrl, ItemId, extension, queryString);
+            return string.Format(CultureInfo.InvariantCulture, "{0}/videos/{1}/stream{2}?{3}", baseUrl, ItemId, extension, queryString);
         }
 
         private static List<NameValuePair> BuildParams(StreamInfo item, string accessToken)
@@ -457,7 +457,7 @@ namespace MediaBrowser.Model.Dlna
             {
                 if (MediaSource.Protocol == MediaProtocol.File || !string.Equals(stream.Codec, subtitleProfile.Format, StringComparison.OrdinalIgnoreCase) || !stream.IsExternal)
                 {
-                    info.Url = string.Format("{0}/Videos/{1}/{2}/Subtitles/{3}/{4}/Stream.{5}",
+                    info.Url = string.Format(CultureInfo.InvariantCulture, "{0}/Videos/{1}/{2}/Subtitles/{3}/{4}/Stream.{5}",
                         baseUrl,
                         ItemId,
                         MediaSourceId,

+ 2 - 2
MediaBrowser.Providers/Manager/ImageSaver.cs

@@ -187,7 +187,7 @@ namespace MediaBrowser.Providers.Manager
             }
         }
 
-        public async Task SaveImage(User user, Stream source, string path)
+        public async Task SaveImage(Stream source, string path)
         {
             await SaveImageToLocation(source, path, path, CancellationToken.None).ConfigureAwait(false);
         }
@@ -355,7 +355,7 @@ namespace MediaBrowser.Providers.Manager
 
             if (string.IsNullOrWhiteSpace(extension))
             {
-                throw new ArgumentException(string.Format("Unable to determine image file extension from mime type {0}", mimeType));
+                throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Unable to determine image file extension from mime type {0}", mimeType));
             }
 
             if (type == ImageType.Thumb && saveLocally)

+ 29 - 28
MediaBrowser.Providers/Manager/ItemImageProvider.cs

@@ -54,7 +54,12 @@ namespace MediaBrowser.Providers.Manager
             return hasChanges;
         }
 
-        public async Task<RefreshResult> RefreshImages(BaseItem item, LibraryOptions libraryOptions, List<IImageProvider> providers, ImageRefreshOptions refreshOptions, MetadataOptions savedOptions, CancellationToken cancellationToken)
+        public async Task<RefreshResult> RefreshImages(
+            BaseItem item,
+            LibraryOptions libraryOptions,
+            List<IImageProvider> providers,
+            ImageRefreshOptions refreshOptions,
+            CancellationToken cancellationToken)
         {
             if (refreshOptions.IsReplacingImage(ImageType.Backdrop))
             {
@@ -78,19 +83,15 @@ namespace MediaBrowser.Providers.Manager
 
             foreach (var provider in providers)
             {
-                var remoteProvider = provider as IRemoteImageProvider;
-
-                if (remoteProvider != null)
+                if (provider is IRemoteImageProvider remoteProvider)
                 {
                     await RefreshFromProvider(item, libraryOptions, remoteProvider, refreshOptions, typeOptions, backdropLimit, screenshotLimit, downloadedImages, result, cancellationToken).ConfigureAwait(false);
                     continue;
                 }
 
-                var dynamicImageProvider = provider as IDynamicImageProvider;
-
-                if (dynamicImageProvider != null)
+                if (provider is IDynamicImageProvider dynamicImageProvider)
                 {
-                    await RefreshFromProvider(item, dynamicImageProvider, refreshOptions, typeOptions, libraryOptions, downloadedImages, result, cancellationToken).ConfigureAwait(false);
+                    await RefreshFromProvider(item, dynamicImageProvider, refreshOptions, typeOptions, downloadedImages, result, cancellationToken).ConfigureAwait(false);
                 }
             }
 
@@ -100,11 +101,11 @@ namespace MediaBrowser.Providers.Manager
         /// <summary>
         /// Refreshes from provider.
         /// </summary>
-        private async Task RefreshFromProvider(BaseItem item,
+        private async Task RefreshFromProvider(
+            BaseItem item,
             IDynamicImageProvider provider,
             ImageRefreshOptions refreshOptions,
             TypeOptions savedOptions,
-            LibraryOptions libraryOptions,
             ICollection<ImageType> downloadedImages,
             RefreshResult result,
             CancellationToken cancellationToken)
@@ -115,7 +116,7 @@ namespace MediaBrowser.Providers.Manager
 
                 foreach (var imageType in images)
                 {
-                    if (!IsEnabled(savedOptions, imageType, item))
+                    if (!IsEnabled(savedOptions, imageType))
                     {
                         continue;
                     }
@@ -133,12 +134,13 @@ namespace MediaBrowser.Providers.Manager
                                 if (response.Protocol == MediaProtocol.Http)
                                 {
                                     _logger.LogDebug("Setting image url into item {0}", item.Id);
-                                    item.SetImage(new ItemImageInfo
-                                    {
-                                        Path = response.Path,
-                                        Type = imageType
-
-                                    }, 0);
+                                    item.SetImage(
+                                        new ItemImageInfo
+                                        {
+                                            Path = response.Path,
+                                            Type = imageType
+                                        },
+                                        0);
                                 }
                                 else
                                 {
@@ -157,7 +159,7 @@ namespace MediaBrowser.Providers.Manager
                             }
 
                             downloadedImages.Add(imageType);
-                            result.UpdateType = result.UpdateType | ItemUpdateType.ImageUpdate;
+                            result.UpdateType |= ItemUpdateType.ImageUpdate;
                         }
                     }
                 }
@@ -279,7 +281,7 @@ namespace MediaBrowser.Providers.Manager
 
                 foreach (var imageType in _singularImages)
                 {
-                    if (!IsEnabled(savedOptions, imageType, item))
+                    if (!IsEnabled(savedOptions, imageType))
                     {
                         continue;
                     }
@@ -299,8 +301,7 @@ namespace MediaBrowser.Providers.Manager
                 minWidth = savedOptions.GetMinWidth(ImageType.Backdrop);
                 await DownloadBackdrops(item, libraryOptions, ImageType.Backdrop, backdropLimit, provider, result, list, minWidth, cancellationToken).ConfigureAwait(false);
 
-                var hasScreenshots = item as IHasScreenshots;
-                if (hasScreenshots != null)
+                if (item is IHasScreenshots hasScreenshots)
                 {
                     minWidth = savedOptions.GetMinWidth(ImageType.Screenshot);
                     await DownloadBackdrops(item, libraryOptions, ImageType.Screenshot, screenshotLimit, provider, result, list, minWidth, cancellationToken).ConfigureAwait(false);
@@ -317,7 +318,7 @@ namespace MediaBrowser.Providers.Manager
             }
         }
 
-        private bool IsEnabled(TypeOptions options, ImageType type, BaseItem item)
+        private bool IsEnabled(TypeOptions options, ImageType type)
         {
             return options.IsEnabled(type);
         }
@@ -452,10 +453,10 @@ namespace MediaBrowser.Providers.Manager
                 .Where(i => i.Type == type && !(i.Width.HasValue && i.Width.Value < minWidth))
                 .ToList();
 
-            if (EnableImageStub(item, type, libraryOptions) && eligibleImages.Count > 0)
+            if (EnableImageStub(item, libraryOptions) && eligibleImages.Count > 0)
             {
                 SaveImageStub(item, type, eligibleImages.Select(i => i.Url));
-                result.UpdateType = result.UpdateType | ItemUpdateType.ImageUpdate;
+                result.UpdateType |= ItemUpdateType.ImageUpdate;
                 return true;
             }
 
@@ -476,7 +477,7 @@ namespace MediaBrowser.Providers.Manager
                         null,
                         cancellationToken).ConfigureAwait(false);
 
-                    result.UpdateType = result.UpdateType | ItemUpdateType.ImageUpdate;
+                    result.UpdateType |= ItemUpdateType.ImageUpdate;
                     return true;
                 }
                 catch (HttpException ex)
@@ -495,7 +496,7 @@ namespace MediaBrowser.Providers.Manager
             return false;
         }
 
-        private bool EnableImageStub(BaseItem item, ImageType type, LibraryOptions libraryOptions)
+        private bool EnableImageStub(BaseItem item, LibraryOptions libraryOptions)
         {
             if (item is LiveTvProgram)
             {
@@ -563,10 +564,10 @@ namespace MediaBrowser.Providers.Manager
 
                 var url = image.Url;
 
-                if (EnableImageStub(item, imageType, libraryOptions))
+                if (EnableImageStub(item, libraryOptions))
                 {
                     SaveImageStub(item, imageType, new[] { url });
-                    result.UpdateType = result.UpdateType | ItemUpdateType.ImageUpdate;
+                    result.UpdateType |= ItemUpdateType.ImageUpdate;
                     continue;
                 }
 

+ 37 - 36
MediaBrowser.Providers/Manager/MetadataService.cs

@@ -52,7 +52,6 @@ namespace MediaBrowser.Providers.Manager
         public async Task<ItemUpdateType> RefreshMetadata(BaseItem item, MetadataRefreshOptions refreshOptions, CancellationToken cancellationToken)
         {
             var itemOfType = (TItemType)item;
-            var config = ProviderManager.GetMetadataOptions(item);
 
             var updateType = ItemUpdateType.None;
             var requiresRefresh = false;
@@ -86,7 +85,7 @@ namespace MediaBrowser.Providers.Manager
                 // Always validate images and check for new locally stored ones.
                 if (itemImageProvider.ValidateImages(item, allImageProviders.OfType<ILocalImageProvider>(), refreshOptions.DirectoryService))
                 {
-                    updateType = updateType | ItemUpdateType.ImageUpdate;
+                    updateType |= ItemUpdateType.ImageUpdate;
                 }
             }
             catch (Exception ex)
@@ -102,7 +101,7 @@ namespace MediaBrowser.Providers.Manager
 
             bool hasRefreshedMetadata = true;
             bool hasRefreshedImages = true;
-            var isFirstRefresh = item.DateLastRefreshed == default(DateTime);
+            var isFirstRefresh = item.DateLastRefreshed == default;
 
             // Next run metadata providers
             if (refreshOptions.MetadataRefreshMode != MetadataRefreshMode.None)
@@ -114,7 +113,7 @@ namespace MediaBrowser.Providers.Manager
                 {
                     if (item.BeforeMetadataRefresh(refreshOptions.ReplaceAllMetadata))
                     {
-                        updateType = updateType | ItemUpdateType.MetadataImport;
+                        updateType |= ItemUpdateType.MetadataImport;
                     }
                 }
 
@@ -132,7 +131,7 @@ namespace MediaBrowser.Providers.Manager
 
                     var result = await RefreshWithProviders(metadataResult, id, refreshOptions, providers, itemImageProvider, cancellationToken).ConfigureAwait(false);
 
-                    updateType = updateType | result.UpdateType;
+                    updateType |= result.UpdateType;
                     if (result.Failures > 0)
                     {
                         hasRefreshedMetadata = false;
@@ -147,9 +146,9 @@ namespace MediaBrowser.Providers.Manager
 
                 if (providers.Count > 0)
                 {
-                    var result = await itemImageProvider.RefreshImages(itemOfType, libraryOptions, providers, refreshOptions, config, cancellationToken).ConfigureAwait(false);
+                    var result = await itemImageProvider.RefreshImages(itemOfType, libraryOptions, providers, refreshOptions, cancellationToken).ConfigureAwait(false);
 
-                    updateType = updateType | result.UpdateType;
+                    updateType |= result.UpdateType;
                     if (result.Failures > 0)
                     {
                         hasRefreshedImages = false;
@@ -158,7 +157,7 @@ namespace MediaBrowser.Providers.Manager
             }
 
             var beforeSaveResult = BeforeSave(itemOfType, isFirstRefresh || refreshOptions.ReplaceAllMetadata || refreshOptions.MetadataRefreshMode == MetadataRefreshMode.FullRefresh || requiresRefresh || refreshOptions.ForceSave, updateType);
-            updateType = updateType | beforeSaveResult;
+            updateType |= beforeSaveResult;
 
             // Save if changes were made, or it's never been saved before
             if (refreshOptions.ForceSave || updateType > ItemUpdateType.None || isFirstRefresh || refreshOptions.ReplaceAllMetadata || requiresRefresh)
@@ -175,7 +174,7 @@ namespace MediaBrowser.Providers.Manager
                 // If any of these properties are set then make sure the updateType is not None, just to force everything to save
                 if (refreshOptions.ForceSave || refreshOptions.ReplaceAllMetadata)
                 {
-                    updateType = updateType | ItemUpdateType.MetadataDownload;
+                    updateType |= ItemUpdateType.MetadataDownload;
                 }
 
                 if (hasRefreshedMetadata && hasRefreshedImages)
@@ -184,11 +183,11 @@ namespace MediaBrowser.Providers.Manager
                 }
                 else
                 {
-                    item.DateLastRefreshed = default(DateTime);
+                    item.DateLastRefreshed = default;
                 }
 
                 // Save to database
-                SaveItem(metadataResult, libraryOptions, updateType, cancellationToken);
+                await SaveItemAsync(metadataResult, libraryOptions, updateType, cancellationToken).ConfigureAwait(false);
             }
 
             await AfterMetadataRefresh(itemOfType, refreshOptions, cancellationToken).ConfigureAwait(false);
@@ -203,26 +202,26 @@ namespace MediaBrowser.Providers.Manager
             lookupInfo.Year = result.ProductionYear;
         }
 
-        protected void SaveItem(MetadataResult<TItemType> result, LibraryOptions libraryOptions, ItemUpdateType reason, CancellationToken cancellationToken)
+        protected async Task SaveItemAsync(MetadataResult<TItemType> result, LibraryOptions libraryOptions, ItemUpdateType reason, CancellationToken cancellationToken)
         {
             if (result.Item.SupportsPeople && result.People != null)
             {
                 var baseItem = result.Item;
 
                 LibraryManager.UpdatePeople(baseItem, result.People);
-                SavePeopleMetadata(result.People, libraryOptions, cancellationToken);
+                await SavePeopleMetadataAsync(result.People, libraryOptions, cancellationToken).ConfigureAwait(false);
             }
 
             result.Item.UpdateToRepository(reason, cancellationToken);
         }
 
-        private void SavePeopleMetadata(List<PersonInfo> people, LibraryOptions libraryOptions, CancellationToken cancellationToken)
+        private async Task SavePeopleMetadataAsync(List<PersonInfo> people, LibraryOptions libraryOptions, CancellationToken cancellationToken)
         {
             foreach (var person in people)
             {
                 cancellationToken.ThrowIfCancellationRequested();
 
-                if (person.ProviderIds.Any() || !string.IsNullOrWhiteSpace(person.ImageUrl))
+                if (person.ProviderIds.Count > 0 || !string.IsNullOrWhiteSpace(person.ImageUrl))
                 {
                     var updateType = ItemUpdateType.MetadataDownload;
 
@@ -239,10 +238,10 @@ namespace MediaBrowser.Providers.Manager
 
                     if (!string.IsNullOrWhiteSpace(person.ImageUrl) && !personEntity.HasImage(ImageType.Primary))
                     {
-                        AddPersonImage(personEntity, libraryOptions, person.ImageUrl, cancellationToken);
+                        await AddPersonImageAsync(personEntity, libraryOptions, person.ImageUrl, cancellationToken).ConfigureAwait(false);
 
                         saveEntity = true;
-                        updateType = updateType | ItemUpdateType.ImageUpdate;
+                        updateType |= ItemUpdateType.ImageUpdate;
                     }
 
                     if (saveEntity)
@@ -253,26 +252,28 @@ namespace MediaBrowser.Providers.Manager
             }
         }
 
-        private void AddPersonImage(Person personEntity, LibraryOptions libraryOptions, string imageUrl, CancellationToken cancellationToken)
+        private async Task AddPersonImageAsync(Person personEntity, LibraryOptions libraryOptions, string imageUrl, CancellationToken cancellationToken)
         {
-            // if (libraryOptions.DownloadImagesInAdvance)
-            //{
-            //    try
-            //    {
-            //        await ProviderManager.SaveImage(personEntity, imageUrl, ImageType.Primary, null, cancellationToken).ConfigureAwait(false);
-            //        return;
-            //    }
-            //    catch (Exception ex)
-            //    {
-            //        Logger.LogError(ex, "Error in AddPersonImage");
-            //    }
-            //}
-
-            personEntity.SetImage(new ItemImageInfo
-            {
-                Path = imageUrl,
-                Type = ImageType.Primary
-            }, 0);
+            if (libraryOptions.DownloadImagesInAdvance)
+            {
+                try
+                {
+                    await ProviderManager.SaveImage(personEntity, imageUrl, ImageType.Primary, null, cancellationToken).ConfigureAwait(false);
+                    return;
+                }
+                catch (Exception ex)
+                {
+                    Logger.LogError(ex, "Error in AddPersonImage");
+                }
+            }
+
+            personEntity.SetImage(
+                new ItemImageInfo
+                {
+                    Path = imageUrl,
+                    Type = ImageType.Primary
+                },
+                0);
         }
 
         protected virtual Task AfterMetadataRefresh(TItemType item, MetadataRefreshOptions refreshOptions, CancellationToken cancellationToken)

+ 28 - 9
MediaBrowser.Providers/Manager/ProviderManager.cs

@@ -210,10 +210,10 @@ namespace MediaBrowser.Providers.Manager
         }
 
         /// <inheritdoc/>
-        public Task SaveImage(User user, Stream source, string mimeType, string path)
+        public Task SaveImage(Stream source, string mimeType, string path)
         {
             return new ImageSaver(_configurationManager, _libraryMonitor, _fileSystem, _logger)
-                .SaveImage(user, source, path);
+                .SaveImage(source, path);
         }
 
         /// <inheritdoc/>
@@ -563,7 +563,7 @@ namespace MediaBrowser.Providers.Manager
             var pluginList = summary.Plugins.ToList();
 
             AddMetadataPlugins(pluginList, dummy, libraryOptions, options);
-            AddImagePlugins(pluginList, dummy, imageProviders);
+            AddImagePlugins(pluginList, imageProviders);
 
             var subtitleProviders = _subtitleManager.GetSupportedProviders(dummy);
 
@@ -594,14 +594,14 @@ namespace MediaBrowser.Providers.Manager
             var providers = GetMetadataProvidersInternal<T>(item, libraryOptions, options, true, true).ToList();
 
             // Locals
-            list.AddRange(providers.Where(i => (i is ILocalMetadataProvider)).Select(i => new MetadataPlugin
+            list.AddRange(providers.Where(i => i is ILocalMetadataProvider).Select(i => new MetadataPlugin
             {
                 Name = i.Name,
                 Type = MetadataPluginType.LocalMetadataProvider
             }));
 
             // Fetchers
-            list.AddRange(providers.Where(i => (i is IRemoteMetadataProvider)).Select(i => new MetadataPlugin
+            list.AddRange(providers.Where(i => i is IRemoteMetadataProvider).Select(i => new MetadataPlugin
             {
                 Name = i.Name,
                 Type = MetadataPluginType.MetadataFetcher
@@ -615,11 +615,10 @@ namespace MediaBrowser.Providers.Manager
             }));
         }
 
-        private void AddImagePlugins<T>(List<MetadataPlugin> list, T item, List<IImageProvider> imageProviders)
-            where T : BaseItem
+        private void AddImagePlugins(List<MetadataPlugin> list, List<IImageProvider> imageProviders)
         {
             // Locals
-            list.AddRange(imageProviders.Where(i => (i is ILocalImageProvider)).Select(i => new MetadataPlugin
+            list.AddRange(imageProviders.Where(i => i is ILocalImageProvider).Select(i => new MetadataPlugin
             {
                 Name = i.Name,
                 Type = MetadataPluginType.LocalImageProvider
@@ -1166,12 +1165,32 @@ namespace MediaBrowser.Providers.Manager
         /// <inheritdoc/>
         public void Dispose()
         {
-            _disposed = true;
+            Dispose(true);
+            GC.SuppressFinalize(this);
+        }
+
+        /// <summary>
+        /// Releases unmanaged and optionally managed resources.
+        /// </summary>
+        /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
+        protected virtual void Dispose(bool disposing)
+        {
+            if (_disposed)
+            {
+                return;
+            }
 
             if (!_disposeCancellationTokenSource.IsCancellationRequested)
             {
                 _disposeCancellationTokenSource.Cancel();
             }
+
+            if (disposing)
+            {
+                _disposeCancellationTokenSource.Dispose();
+            }
+
+            _disposed = true;
         }
     }
 }

+ 5 - 10
MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs

@@ -2,11 +2,9 @@
 
 using System;
 using System.Collections.Generic;
-using System.Globalization;
 using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
-using MediaBrowser.Common.Configuration;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Audio;
 using MediaBrowser.Controller.Library;
@@ -17,7 +15,6 @@ using MediaBrowser.Model.Dlna;
 using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.MediaInfo;
-using MediaBrowser.Model.Serialization;
 
 namespace MediaBrowser.Providers.MediaInfo
 {
@@ -25,19 +22,17 @@ namespace MediaBrowser.Providers.MediaInfo
     {
         private readonly IMediaEncoder _mediaEncoder;
         private readonly IItemRepository _itemRepo;
-        private readonly IApplicationPaths _appPaths;
-        private readonly IJsonSerializer _json;
         private readonly ILibraryManager _libraryManager;
         private readonly IMediaSourceManager _mediaSourceManager;
 
-        private readonly CultureInfo _usCulture = new CultureInfo("en-US");
-
-        public FFProbeAudioInfo(IMediaSourceManager mediaSourceManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, IApplicationPaths appPaths, IJsonSerializer json, ILibraryManager libraryManager)
+        public FFProbeAudioInfo(
+            IMediaSourceManager mediaSourceManager,
+            IMediaEncoder mediaEncoder,
+            IItemRepository itemRepo,
+            ILibraryManager libraryManager)
         {
             _mediaEncoder = mediaEncoder;
             _itemRepo = itemRepo;
-            _appPaths = appPaths;
-            _json = json;
             _libraryManager = libraryManager;
             _mediaSourceManager = mediaSourceManager;
         }

+ 5 - 16
MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs

@@ -40,19 +40,15 @@ namespace MediaBrowser.Providers.MediaInfo
         IHasItemChangeMonitor
     {
         private readonly ILogger<FFProbeProvider> _logger;
-        private readonly IIsoManager _isoManager;
         private readonly IMediaEncoder _mediaEncoder;
         private readonly IItemRepository _itemRepo;
         private readonly IBlurayExaminer _blurayExaminer;
         private readonly ILocalizationManager _localization;
-        private readonly IApplicationPaths _appPaths;
-        private readonly IJsonSerializer _json;
         private readonly IEncodingManager _encodingManager;
         private readonly IServerConfigurationManager _config;
         private readonly ISubtitleManager _subtitleManager;
         private readonly IChapterManager _chapterManager;
         private readonly ILibraryManager _libraryManager;
-        private readonly IChannelManager _channelManager;
         private readonly IMediaSourceManager _mediaSourceManager;
 
         public string Name => "ffprobe";
@@ -126,14 +122,10 @@ namespace MediaBrowser.Providers.MediaInfo
         public FFProbeProvider(
             ILogger<FFProbeProvider> logger,
             IMediaSourceManager mediaSourceManager,
-            IChannelManager channelManager,
-            IIsoManager isoManager,
             IMediaEncoder mediaEncoder,
             IItemRepository itemRepo,
             IBlurayExaminer blurayExaminer,
             ILocalizationManager localization,
-            IApplicationPaths appPaths,
-            IJsonSerializer json,
             IEncodingManager encodingManager,
             IServerConfigurationManager config,
             ISubtitleManager subtitleManager,
@@ -141,19 +133,15 @@ namespace MediaBrowser.Providers.MediaInfo
             ILibraryManager libraryManager)
         {
             _logger = logger;
-            _isoManager = isoManager;
             _mediaEncoder = mediaEncoder;
             _itemRepo = itemRepo;
             _blurayExaminer = blurayExaminer;
             _localization = localization;
-            _appPaths = appPaths;
-            _json = json;
             _encodingManager = encodingManager;
             _config = config;
             _subtitleManager = subtitleManager;
             _chapterManager = chapterManager;
             _libraryManager = libraryManager;
-            _channelManager = channelManager;
             _mediaSourceManager = mediaSourceManager;
 
             _subtitleResolver = new SubtitleResolver(BaseItem.LocalizationManager);
@@ -211,9 +199,9 @@ namespace MediaBrowser.Providers.MediaInfo
 
         private string NormalizeStrmLine(string line)
         {
-            return line.Replace("\t", string.Empty)
-                .Replace("\r", string.Empty)
-                .Replace("\n", string.Empty)
+            return line.Replace("\t", string.Empty, StringComparison.Ordinal)
+                .Replace("\r", string.Empty, StringComparison.Ordinal)
+                .Replace("\n", string.Empty, StringComparison.Ordinal)
                 .Trim();
         }
 
@@ -242,10 +230,11 @@ namespace MediaBrowser.Providers.MediaInfo
                 FetchShortcutInfo(item);
             }
 
-            var prober = new FFProbeAudioInfo(_mediaSourceManager, _mediaEncoder, _itemRepo, _appPaths, _json, _libraryManager);
+            var prober = new FFProbeAudioInfo(_mediaSourceManager, _mediaEncoder, _itemRepo, _libraryManager);
 
             return prober.Probe(item, options, cancellationToken);
         }
+
         // Run last
         public int Order => 100;
     }

+ 2 - 2
MediaBrowser.Providers/Music/Extensions.cs

@@ -6,7 +6,7 @@ using MediaBrowser.Model.Entities;
 
 namespace MediaBrowser.Providers.Music
 {
-    public static class Extensions
+    public static class AlbumInfoExtensions
     {
         public static string GetAlbumArtist(this AlbumInfo info)
         {
@@ -18,7 +18,7 @@ namespace MediaBrowser.Providers.Music
                 return id;
             }
 
-            return info.AlbumArtists.FirstOrDefault();
+            return info.AlbumArtists.Count > 0 ? info.AlbumArtists[0] : default;
         }
 
         public static string GetReleaseGroupId(this AlbumInfo info)

+ 1 - 1
MediaBrowser.Providers/Plugins/MusicBrainz/AlbumProvider.cs

@@ -276,7 +276,7 @@ namespace MediaBrowser.Providers.Music
 
         private async Task<ReleaseResult> GetReleaseResult(string albumName, string artistId, CancellationToken cancellationToken)
         {
-            var url = string.Format("/ws/2/release/?query=\"{0}\" AND arid:{1}",
+            var url = string.Format(CultureInfo.InvariantCulture, "/ws/2/release/?query=\"{0}\" AND arid:{1}",
                 WebUtility.UrlEncode(albumName),
                 artistId);
 

+ 2 - 2
MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs

@@ -46,7 +46,7 @@ namespace MediaBrowser.Providers.Music
                 // They seem to throw bad request failures on any term with a slash
                 var nameToSearch = searchInfo.Name.Replace('/', ' ');
 
-                var url = string.Format("/ws/2/artist/?query=\"{0}\"&dismax=true", UrlEncode(nameToSearch));
+                var url = string.Format(CultureInfo.InvariantCulture, "/ws/2/artist/?query=\"{0}\"&dismax=true", UrlEncode(nameToSearch));
 
                 using (var response = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false))
                 await using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
@@ -62,7 +62,7 @@ namespace MediaBrowser.Providers.Music
                 if (HasDiacritics(searchInfo.Name))
                 {
                     // Try again using the search with accent characters url
-                    url = string.Format("/ws/2/artist/?query=artistaccent:\"{0}\"", UrlEncode(nameToSearch));
+                    url = string.Format(CultureInfo.InvariantCulture, "/ws/2/artist/?query=artistaccent:\"{0}\"", UrlEncode(nameToSearch));
 
                     using var response = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false);
                     await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);

+ 2 - 1
MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs

@@ -2,6 +2,7 @@
 
 using System.Collections.Generic;
 using System.Net.Http;
+using System.Globalization;
 using System.Threading;
 using System.Threading.Tasks;
 using MediaBrowser.Common;
@@ -70,7 +71,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
                         list.Add(new RemoteImageInfo
                         {
                             ProviderName = Name,
-                            Url = string.Format("https://img.omdbapi.com/?i={0}&apikey=2c9d9507", imdbId)
+                            Url = string.Format(CultureInfo.InvariantCulture, "https://img.omdbapi.com/?i={0}&apikey=2c9d9507", imdbId)
                         });
                     }
                 }

+ 1 - 1
MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs

@@ -127,7 +127,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
                 }
             }
 
-            var url = OmdbProvider.GetOmdbUrl(urlQuery, _appHost, cancellationToken);
+            var url = OmdbProvider.GetOmdbUrl(urlQuery);
 
             using var response = await OmdbProvider.GetOmdbResponse(_httpClientFactory.CreateClient(), url, cancellationToken).ConfigureAwait(false);
             await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);

+ 17 - 8
MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs

@@ -256,16 +256,16 @@ namespace MediaBrowser.Providers.Plugins.Omdb
             return false;
         }
 
-        public static string GetOmdbUrl(string query, IApplicationHost appHost, CancellationToken cancellationToken)
+        public static string GetOmdbUrl(string query)
         {
-            const string url = "https://www.omdbapi.com?apikey=2c9d9507";
+            const string Url = "https://www.omdbapi.com?apikey=2c9d9507";
 
             if (string.IsNullOrWhiteSpace(query))
             {
-                return url;
+                return Url;
             }
 
-            return url + "&" + query;
+            return Url + "&" + query;
         }
 
         private async Task<string> EnsureItemInfo(string imdbId, CancellationToken cancellationToken)
@@ -290,7 +290,11 @@ namespace MediaBrowser.Providers.Plugins.Omdb
                 }
             }
 
-            var url = GetOmdbUrl(string.Format("i={0}&plot=short&tomatoes=true&r=json", imdbParam), _appHost, cancellationToken);
+            var url = GetOmdbUrl(
+                string.Format(
+                    CultureInfo.InvariantCulture,
+                    "i={0}&plot=short&tomatoes=true&r=json",
+                    imdbParam));
 
             using var response = await GetOmdbResponse(_httpClientFactory.CreateClient(), url, cancellationToken).ConfigureAwait(false);
             await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
@@ -323,7 +327,12 @@ namespace MediaBrowser.Providers.Plugins.Omdb
                 }
             }
 
-            var url = GetOmdbUrl(string.Format("i={0}&season={1}&detail=full", imdbParam, seasonId), _appHost, cancellationToken);
+            var url = GetOmdbUrl(
+                string.Format(
+                    CultureInfo.InvariantCulture,
+                    "i={0}&season={1}&detail=full",
+                    imdbParam,
+                    seasonId));
 
             using var response = await GetOmdbResponse(_httpClientFactory.CreateClient(), url, cancellationToken).ConfigureAwait(false);
             await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
@@ -348,7 +357,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
 
             var dataPath = Path.Combine(_configurationManager.ApplicationPaths.CachePath, "omdb");
 
-            var filename = string.Format("{0}.json", imdbId);
+            var filename = string.Format(CultureInfo.InvariantCulture, "{0}.json", imdbId);
 
             return Path.Combine(dataPath, filename);
         }
@@ -362,7 +371,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
 
             var dataPath = Path.Combine(_configurationManager.ApplicationPaths.CachePath, "omdb");
 
-            var filename = string.Format("{0}_season_{1}.json", imdbId, seasonId);
+            var filename = string.Format(CultureInfo.InvariantCulture, "{0}_season_{1}.json", imdbId, seasonId);
 
             return Path.Combine(dataPath, filename);
         }

+ 9 - 22
MediaBrowser.Providers/Plugins/TheTvdb/TvdbClientManager.cs

@@ -2,6 +2,7 @@
 
 using System;
 using System.Collections.Generic;
+using System.Globalization;
 using System.Linq;
 using System.Reflection;
 using System.Runtime.CompilerServices;
@@ -19,7 +20,6 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
     {
         private const string DefaultLanguage = "en";
 
-        private readonly SemaphoreSlim _cacheWriteLock = new SemaphoreSlim(1, 1);
         private readonly IMemoryCache _cache;
         private readonly TvDbClient _tvDbClient;
         private DateTime _tokenCreatedAt;
@@ -176,7 +176,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
             string language,
             CancellationToken cancellationToken)
         {
-            searchInfo.SeriesProviderIds.TryGetValue(MetadataProvider.Tvdb.ToString(),
+            searchInfo.SeriesProviderIds.TryGetValue(nameof(MetadataProvider.Tvdb),
                 out var seriesTvdbId);
 
             var episodeQuery = new EpisodeQuery();
@@ -203,10 +203,10 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
             else if (searchInfo.PremiereDate.HasValue)
             {
                 // tvdb expects yyyy-mm-dd format
-                episodeQuery.FirstAired = searchInfo.PremiereDate.Value.ToString("yyyy-MM-dd");
+                episodeQuery.FirstAired = searchInfo.PremiereDate.Value.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);
             }
 
-            return GetEpisodeTvdbId(Convert.ToInt32(seriesTvdbId), episodeQuery, language, cancellationToken);
+            return GetEpisodeTvdbId(Convert.ToInt32(seriesTvdbId, CultureInfo.InvariantCulture), episodeQuery, language, cancellationToken);
         }
 
         public async Task<string> GetEpisodeTvdbId(
@@ -218,7 +218,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
             var episodePage =
                 await GetEpisodesPageAsync(Convert.ToInt32(seriesTvdbId), episodeQuery, language, cancellationToken)
                     .ConfigureAwait(false);
-            return episodePage.Data.FirstOrDefault()?.Id.ToString();
+            return episodePage.Data.FirstOrDefault()?.Id.ToString(CultureInfo.InvariantCulture);
         }
 
         public Task<TvDbResponse<EpisodeRecord[]>> GetEpisodesPageAsync(
@@ -276,23 +276,10 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
                 return cachedValue;
             }
 
-            await _cacheWriteLock.WaitAsync().ConfigureAwait(false);
-            try
-            {
-                if (_cache.TryGetValue(key, out cachedValue))
-                {
-                    return cachedValue;
-                }
-
-                _tvDbClient.AcceptedLanguage = TvdbUtils.NormalizeLanguage(language) ?? DefaultLanguage;
-                var result = await resultFactory.Invoke().ConfigureAwait(false);
-                _cache.Set(key, result, TimeSpan.FromHours(1));
-                return result;
-            }
-            finally
-            {
-                _cacheWriteLock.Release();
-            }
+            _tvDbClient.AcceptedLanguage = TvdbUtils.NormalizeLanguage(language) ?? DefaultLanguage;
+            var result = await resultFactory.Invoke().ConfigureAwait(false);
+            _cache.Set(key, result, TimeSpan.FromHours(1));
+            return result;
         }
 
         private static string GenerateKey(params object[] objects)

+ 4 - 3
MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeImageProvider.cs

@@ -3,6 +3,7 @@
 using System;
 using System.Collections.Generic;
 using System.Net.Http;
+using System.Globalization;
 using System.Threading;
 using System.Threading.Tasks;
 using MediaBrowser.Controller.Entities;
@@ -76,7 +77,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
 
                     var episodeResult =
                         await _tvdbClientManager
-                            .GetEpisodesAsync(Convert.ToInt32(episodeTvdbId), language, cancellationToken)
+                            .GetEpisodesAsync(Convert.ToInt32(episodeTvdbId, CultureInfo.InvariantCulture), language, cancellationToken)
                             .ConfigureAwait(false);
 
                     var image = GetImageInfo(episodeResult.Data);
@@ -103,8 +104,8 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
 
             return new RemoteImageInfo
             {
-                Width = Convert.ToInt32(episode.ThumbWidth),
-                Height = Convert.ToInt32(episode.ThumbHeight),
+                Width = Convert.ToInt32(episode.ThumbWidth, CultureInfo.InvariantCulture),
+                Height = Convert.ToInt32(episode.ThumbHeight, CultureInfo.InvariantCulture),
                 ProviderName = Name,
                 Url = TvdbUtils.BannerUrl + episode.Filename,
                 Type = ImageType.Primary

+ 2 - 2
MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs

@@ -180,7 +180,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets
 
             if (!string.IsNullOrEmpty(language))
             {
-                url += string.Format("&language={0}", TmdbMovieProvider.NormalizeLanguage(language));
+                url += string.Format(CultureInfo.InvariantCulture, "&language={0}", TmdbMovieProvider.NormalizeLanguage(language));
 
                 // Get images in english and with no language
                 url += "&include_image_language=" + TmdbMovieProvider.GetImageLanguagesParam(language);
@@ -250,7 +250,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets
         {
             var path = GetDataPath(appPaths, tmdbId);
 
-            var filename = string.Format("all-{0}.json", preferredLanguage ?? string.Empty);
+            var filename = string.Format(CultureInfo.InvariantCulture, "all-{0}.json", preferredLanguage ?? string.Empty);
 
             return Path.Combine(path, filename);
         }

+ 1 - 1
MediaBrowser.Providers/Plugins/Tmdb/Movies/GenericTmdbMovieInfo.cs

@@ -300,7 +300,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies
             {
                 movie.RemoteTrailers = movieData.Trailers.Youtube.Select(i => new MediaUrl
                 {
-                    Url = string.Format("https://www.youtube.com/watch?v={0}", i.Source),
+                    Url = string.Format(CultureInfo.InvariantCulture, "https://www.youtube.com/watch?v={0}", i.Source),
                     Name = i.Name
 
                 }).ToArray();

+ 48 - 6
MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbSearch.cs

@@ -37,7 +37,8 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies
             ).* # Match rest of string",
             RegexOptions.Compiled | RegexOptions.IgnorePatternWhitespace | RegexOptions.IgnoreCase);
 
-        private const string _searchURL = TmdbUtils.BaseTmdbApiUrl + @"3/search/{3}?api_key={1}&query={0}&language={2}";
+        private const string SearchUrl = TmdbUtils.BaseTmdbApiUrl + @"3/search/{3}?api_key={1}&query={0}&language={2}";
+        private const string SearchUrlWithYear = TmdbUtils.BaseTmdbApiUrl + @"3/search/{3}?api_key={1}&query={0}&language={2}&first_air_date_year={4}";
 
         private readonly ILogger _logger;
         private readonly IJsonSerializer _json;
@@ -124,7 +125,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies
                 name2 = name2.Trim();
 
                 // Search again if the new name is different
-                if (!string.Equals(name2, name) && !string.IsNullOrWhiteSpace(name2))
+                if (!string.Equals(name2, name, StringComparison.Ordinal) && !string.IsNullOrWhiteSpace(name2))
                 {
                     _logger.LogInformation("TmdbSearch: Finding id for item: {0} ({1})", name2, year);
                     results = await GetSearchResults(name2, searchType, year, language, tmdbImageUrl, cancellationToken).ConfigureAwait(false);
@@ -164,10 +165,30 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies
         {
             if (string.IsNullOrWhiteSpace(name))
             {
-                throw new ArgumentException("name");
+                throw new ArgumentException("String can't be null or empty.", nameof(name));
             }
 
-            var url3 = string.Format(_searchURL, WebUtility.UrlEncode(name), TmdbUtils.ApiKey, language, type);
+            string url3;
+            if (year != null && string.Equals(type, "movie", StringComparison.OrdinalIgnoreCase))
+            {
+                url3 = string.Format(
+                    CultureInfo.InvariantCulture,
+                    SearchUrl,
+                    WebUtility.UrlEncode(name),
+                    TmdbUtils.ApiKey,
+                    language,
+                    type) + "&primary_release_year=" + year;
+            }
+            else
+            {
+                url3 = string.Format(
+                    CultureInfo.InvariantCulture,
+                    SearchUrl,
+                    WebUtility.UrlEncode(name),
+                    TmdbUtils.ApiKey,
+                    language,
+                    type);
+            }
 
             var requestMessage = new HttpRequestMessage(HttpMethod.Get, url3);
             foreach (var header in TmdbUtils.AcceptHeaders)
@@ -207,10 +228,31 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies
         {
             if (string.IsNullOrWhiteSpace(name))
             {
-                throw new ArgumentException("name");
+                throw new ArgumentException("String can't be null or empty.", nameof(name));
             }
 
-            var url3 = string.Format(_searchURL, WebUtility.UrlEncode(name), TmdbUtils.ApiKey, language, "tv");
+            string url3;
+            if (year == null)
+            {
+                url3 = string.Format(
+                CultureInfo.InvariantCulture,
+                SearchUrl,
+                WebUtility.UrlEncode(name),
+                TmdbUtils.ApiKey,
+                language,
+                "tv");
+            }
+            else
+            {
+                url3 = string.Format(
+                    CultureInfo.InvariantCulture,
+                    SearchUrlWithYear,
+                    WebUtility.UrlEncode(name),
+                    TmdbUtils.ApiKey,
+                    language,
+                    "tv",
+                    year);
+            }
 
             var requestMessage = new HttpRequestMessage(HttpMethod.Get, url3);
             foreach (var header in TmdbUtils.AcceptHeaders)

+ 1 - 1
MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs

@@ -131,7 +131,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
                         {
                             if (video.Site.Equals("youtube", System.StringComparison.OrdinalIgnoreCase))
                             {
-                                var videoUrl = string.Format("http://www.youtube.com/watch?v={0}", video.Key);
+                                var videoUrl = string.Format(CultureInfo.InvariantCulture, "http://www.youtube.com/watch?v={0}", video.Key);
                                 item.AddTrailerUrl(videoUrl);
                             }
                         }

+ 2 - 2
MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProviderBase.cs

@@ -92,7 +92,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
 
             var path = TmdbSeriesProvider.GetSeriesDataPath(_configurationManager.ApplicationPaths, tmdbId);
 
-            var filename = string.Format("season-{0}-episode-{1}-{2}.json",
+            var filename = string.Format(CultureInfo.InvariantCulture, "season-{0}-episode-{1}-{2}.json",
                 seasonNumber.ToString(CultureInfo.InvariantCulture),
                 episodeNumber.ToString(CultureInfo.InvariantCulture),
                 preferredLanguage);
@@ -116,7 +116,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
 
             if (!string.IsNullOrEmpty(language))
             {
-                url += string.Format("&language={0}", language);
+                url += string.Format(CultureInfo.InvariantCulture, "&language={0}", language);
             }
 
             var includeImageLanguageParam = TmdbMovieProvider.GetImageLanguagesParam(language);

+ 2 - 2
MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs

@@ -180,7 +180,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
 
             var path = TmdbSeriesProvider.GetSeriesDataPath(_configurationManager.ApplicationPaths, tmdbId);
 
-            var filename = string.Format("season-{0}-{1}.json",
+            var filename = string.Format(CultureInfo.InvariantCulture, "season-{0}-{1}.json",
                 seasonNumber.ToString(CultureInfo.InvariantCulture),
                 preferredLanguage);
 
@@ -203,7 +203,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
 
             if (!string.IsNullOrEmpty(language))
             {
-                url += string.Format("&language={0}", TmdbMovieProvider.NormalizeLanguage(language));
+                url += string.Format(CultureInfo.InvariantCulture, "&language={0}", TmdbMovieProvider.NormalizeLanguage(language));
             }
 
             var includeImageLanguageParam = TmdbMovieProvider.GetImageLanguagesParam(language);

+ 1 - 1
MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs

@@ -496,7 +496,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
 
             var path = GetSeriesDataPath(_configurationManager.ApplicationPaths, tmdbId);
 
-            var filename = string.Format("series-{0}.json", preferredLanguage ?? string.Empty);
+            var filename = string.Format(CultureInfo.InvariantCulture, "series-{0}.json", preferredLanguage ?? string.Empty);
 
             return Path.Combine(path, filename);
         }

+ 2 - 1
MediaBrowser.Providers/Studios/StudiosImageProvider.cs

@@ -2,6 +2,7 @@
 
 using System;
 using System.Collections.Generic;
+using System.Globalization;
 using System.IO;
 using System.Linq;
 using System.Net.Http;
@@ -100,7 +101,7 @@ namespace MediaBrowser.Providers.Studios
 
         private string GetUrl(string image, string filename)
         {
-            return string.Format("https://raw.github.com/MediaBrowser/MediaBrowser.Resources/master/images/imagesbyname/studios/{0}/{1}.jpg", image, filename);
+            return string.Format(CultureInfo.InvariantCulture, "https://raw.github.com/MediaBrowser/MediaBrowser.Resources/master/images/imagesbyname/studios/{0}/{1}.jpg", image, filename);
         }
 
         private Task<string> EnsureThumbsList(string file, CancellationToken cancellationToken)