aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--BDInfo/BDInfo.csproj1
-rw-r--r--DvdLib/DvdLib.csproj1
-rw-r--r--Emby.Dlna/Emby.Dlna.csproj1
-rw-r--r--Emby.Dlna/PlayTo/SsdpHttpClient.cs10
-rw-r--r--Emby.Drawing/Emby.Drawing.csproj7
-rw-r--r--Emby.Drawing/ImageProcessor.cs159
-rw-r--r--Emby.Drawing/NullImageEncoder.cs34
-rw-r--r--Emby.IsoMounting/IsoMounter/IsoMounter.csproj1
-rw-r--r--Emby.Naming/Emby.Naming.csproj2
-rw-r--r--Emby.Notifications/Emby.Notifications.csproj1
-rw-r--r--Emby.Photos/Emby.Photos.csproj3
-rw-r--r--Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs2
-rw-r--r--Emby.Server.Implementations/ApplicationHost.cs70
-rw-r--r--Emby.Server.Implementations/Data/BaseSqliteRepository.cs4
-rw-r--r--Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs4
-rw-r--r--Emby.Server.Implementations/Data/SqliteUserRepository.cs4
-rw-r--r--Emby.Server.Implementations/Dto/DtoService.cs2
-rw-r--r--Emby.Server.Implementations/Emby.Server.Implementations.csproj9
-rw-r--r--Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs206
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs2
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs205
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs139
-rw-r--r--Emby.Server.Implementations/Net/SocketFactory.cs42
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodingTempTask.cs156
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs24
-rw-r--r--Emby.Server.Implementations/Updates/InstallationManager.cs314
-rw-r--r--Emby.XmlTv/Emby.XmlTv/Emby.XmlTv.csproj1
-rw-r--r--Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj1
-rw-r--r--Jellyfin.Server/CoreAppHost.cs17
-rw-r--r--Jellyfin.Server/Jellyfin.Server.csproj8
-rw-r--r--Jellyfin.Server/Program.cs27
-rw-r--r--Jellyfin.Server/StartupOptions.cs26
-rw-r--r--MediaBrowser.Api/Images/ImageService.cs8
-rw-r--r--MediaBrowser.Api/MediaBrowser.Api.csproj1
-rw-r--r--MediaBrowser.Api/PackageService.cs10
-rw-r--r--MediaBrowser.Api/Playback/BaseStreamingService.cs9
-rw-r--r--MediaBrowser.Common/Extensions/HexHelper.cs22
-rw-r--r--MediaBrowser.Common/IApplicationHost.cs8
-rw-r--r--MediaBrowser.Common/MediaBrowser.Common.csproj6
-rw-r--r--MediaBrowser.Common/Net/HttpRequestOptions.cs12
-rw-r--r--MediaBrowser.Common/Net/IHttpClient.cs16
-rw-r--r--MediaBrowser.Common/Updates/IInstallationManager.cs21
-rw-r--r--MediaBrowser.Controller/Drawing/IImageEncoder.cs21
-rw-r--r--MediaBrowser.Controller/Drawing/IImageProcessor.cs30
-rw-r--r--MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs5
-rw-r--r--MediaBrowser.Controller/MediaBrowser.Controller.csproj1
-rw-r--r--MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj1
-rw-r--r--MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj3
-rw-r--r--MediaBrowser.Model/Cryptography/PasswordHash.cs1
-rw-r--r--MediaBrowser.Model/MediaBrowser.Model.csproj1
-rw-r--r--MediaBrowser.Model/Net/ISocketFactory.cs12
-rw-r--r--MediaBrowser.Model/Updates/InstallationInfo.cs6
-rw-r--r--MediaBrowser.Model/Updates/PackageVersionInfo.cs22
-rw-r--r--MediaBrowser.Providers/MediaBrowser.Providers.csproj1
-rw-r--r--MediaBrowser.Providers/Omdb/OmdbProvider.cs3
-rw-r--r--MediaBrowser.Providers/Studios/StudiosImageProvider.cs28
-rw-r--r--MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj1
-rw-r--r--MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj1
-rw-r--r--Mono.Nat/Upnp/Messages/GetServicesMessage.cs39
-rw-r--r--Mono.Nat/Upnp/Messages/UpnpMessage.cs46
-rw-r--r--Mono.Nat/Upnp/UpnpNatDevice.cs4
-rw-r--r--jellyfin.ruleset2
62 files changed, 777 insertions, 1047 deletions
diff --git a/BDInfo/BDInfo.csproj b/BDInfo/BDInfo.csproj
index b2c752d0c..9dbaa9e2f 100644
--- a/BDInfo/BDInfo.csproj
+++ b/BDInfo/BDInfo.csproj
@@ -11,6 +11,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+ <GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
</Project>
diff --git a/DvdLib/DvdLib.csproj b/DvdLib/DvdLib.csproj
index b2c752d0c..9dbaa9e2f 100644
--- a/DvdLib/DvdLib.csproj
+++ b/DvdLib/DvdLib.csproj
@@ -11,6 +11,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+ <GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
</Project>
diff --git a/Emby.Dlna/Emby.Dlna.csproj b/Emby.Dlna/Emby.Dlna.csproj
index 4c07087c5..34b49120b 100644
--- a/Emby.Dlna/Emby.Dlna.csproj
+++ b/Emby.Dlna/Emby.Dlna.csproj
@@ -14,6 +14,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+ <GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
<ItemGroup>
diff --git a/Emby.Dlna/PlayTo/SsdpHttpClient.cs b/Emby.Dlna/PlayTo/SsdpHttpClient.cs
index 22aaa6885..217ea3a4b 100644
--- a/Emby.Dlna/PlayTo/SsdpHttpClient.cs
+++ b/Emby.Dlna/PlayTo/SsdpHttpClient.cs
@@ -73,9 +73,6 @@ namespace Emby.Dlna.PlayTo
UserAgent = USERAGENT,
LogErrorResponseBody = true,
BufferContent = false,
-
- // The periodic requests may keep some devices awake
- LogRequestAsDebug = true
};
options.RequestHeaders["HOST"] = ip + ":" + port.ToString(_usCulture);
@@ -98,9 +95,6 @@ namespace Emby.Dlna.PlayTo
LogErrorResponseBody = true,
BufferContent = false,
- // The periodic requests may keep some devices awake
- LogRequestAsDebug = true,
-
CancellationToken = cancellationToken
};
@@ -135,13 +129,9 @@ namespace Emby.Dlna.PlayTo
{
Url = url,
UserAgent = USERAGENT,
- LogRequest = logRequest || _config.GetDlnaConfiguration().EnableDebugLog,
LogErrorResponseBody = true,
BufferContent = false,
- // The periodic requests may keep some devices awake
- LogRequestAsDebug = true,
-
CancellationToken = cancellationToken
};
diff --git a/Emby.Drawing/Emby.Drawing.csproj b/Emby.Drawing/Emby.Drawing.csproj
index 9f97baf77..2e539f2c7 100644
--- a/Emby.Drawing/Emby.Drawing.csproj
+++ b/Emby.Drawing/Emby.Drawing.csproj
@@ -3,6 +3,8 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+ <GenerateDocumentationFile>true</GenerateDocumentationFile>
+ <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
@@ -15,4 +17,9 @@
<Compile Include="..\SharedVersion.cs" />
</ItemGroup>
+ <PropertyGroup>
+ <!-- We need at least C# 7.1 for the "default literal" feature-->
+ <LangVersion>latest</LangVersion>
+ </PropertyGroup>
+
</Project>
diff --git a/Emby.Drawing/ImageProcessor.cs b/Emby.Drawing/ImageProcessor.cs
index a7d95eb20..ce8089e59 100644
--- a/Emby.Drawing/ImageProcessor.cs
+++ b/Emby.Drawing/ImageProcessor.cs
@@ -22,42 +22,47 @@ using Microsoft.Extensions.Logging;
namespace Emby.Drawing
{
/// <summary>
- /// Class ImageProcessor
+ /// Class ImageProcessor.
/// </summary>
public class ImageProcessor : IImageProcessor, IDisposable
{
- /// <summary>
- /// The us culture
- /// </summary>
- protected readonly CultureInfo UsCulture = new CultureInfo("en-US");
+ // Increment this when there's a change requiring caches to be invalidated
+ private const string Version = "3";
- /// <summary>
- /// Gets the list of currently registered image processors
- /// Image processors are specialized metadata providers that run after the normal ones
- /// </summary>
- /// <value>The image enhancers.</value>
- public IImageEnhancer[] ImageEnhancers { get; private set; }
+ private static readonly HashSet<string> _transparentImageTypes
+ = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { ".png", ".webp", ".gif" };
/// <summary>
/// The _logger
/// </summary>
private readonly ILogger _logger;
-
private readonly IFileSystem _fileSystem;
private readonly IServerApplicationPaths _appPaths;
private IImageEncoder _imageEncoder;
private readonly Func<ILibraryManager> _libraryManager;
private readonly Func<IMediaEncoder> _mediaEncoder;
+ private readonly Dictionary<string, LockInfo> _locks = new Dictionary<string, LockInfo>();
+ private bool _disposed = false;
+
+ /// <summary>
+ ///
+ /// </summary>
+ /// <param name="logger"></param>
+ /// <param name="appPaths"></param>
+ /// <param name="fileSystem"></param>
+ /// <param name="imageEncoder"></param>
+ /// <param name="libraryManager"></param>
+ /// <param name="mediaEncoder"></param>
public ImageProcessor(
- ILoggerFactory loggerFactory,
+ ILogger<ImageProcessor> logger,
IServerApplicationPaths appPaths,
IFileSystem fileSystem,
IImageEncoder imageEncoder,
Func<ILibraryManager> libraryManager,
Func<IMediaEncoder> mediaEncoder)
{
- _logger = loggerFactory.CreateLogger(nameof(ImageProcessor));
+ _logger = logger;
_fileSystem = fileSystem;
_imageEncoder = imageEncoder;
_libraryManager = libraryManager;
@@ -69,20 +74,11 @@ namespace Emby.Drawing
ImageHelper.ImageProcessor = this;
}
- public IImageEncoder ImageEncoder
- {
- get => _imageEncoder;
- set
- {
- if (value == null)
- {
- throw new ArgumentNullException(nameof(value));
- }
+ private string ResizedImageCachePath => Path.Combine(_appPaths.ImageCachePath, "resized-images");
- _imageEncoder = value;
- }
- }
+ private string EnhancedImageCachePath => Path.Combine(_appPaths.ImageCachePath, "enhanced-images");
+ /// <inheritdoc />
public IReadOnlyCollection<string> SupportedInputFormats =>
new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
@@ -115,18 +111,20 @@ namespace Emby.Drawing
"wbmp"
};
+ /// <inheritdoc />
+ public IReadOnlyCollection<IImageEnhancer> ImageEnhancers { get; set; }
+ /// <inheritdoc />
public bool SupportsImageCollageCreation => _imageEncoder.SupportsImageCollageCreation;
- private string ResizedImageCachePath => Path.Combine(_appPaths.ImageCachePath, "resized-images");
-
- private string EnhancedImageCachePath => Path.Combine(_appPaths.ImageCachePath, "enhanced-images");
-
- public void AddParts(IEnumerable<IImageEnhancer> enhancers)
+ /// <inheritdoc />
+ public IImageEncoder ImageEncoder
{
- ImageEnhancers = enhancers.ToArray();
+ get => _imageEncoder;
+ set => _imageEncoder = value ?? throw new ArgumentNullException(nameof(value));
}
+ /// <inheritdoc />
public async Task ProcessImage(ImageProcessingOptions options, Stream toStream)
{
var file = await ProcessImage(options).ConfigureAwait(false);
@@ -137,15 +135,15 @@ namespace Emby.Drawing
}
}
+ /// <inheritdoc />
public IReadOnlyCollection<ImageFormat> GetSupportedImageOutputFormats()
=> _imageEncoder.SupportedOutputFormats;
- private static readonly HashSet<string> TransparentImageTypes
- = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { ".png", ".webp", ".gif" };
-
+ /// <inheritdoc />
public bool SupportsTransparency(string path)
- => TransparentImageTypes.Contains(Path.GetExtension(path));
+ => _transparentImageTypes.Contains(Path.GetExtension(path));
+ /// <inheritdoc />
public async Task<(string path, string mimeType, DateTime dateModified)> ProcessImage(ImageProcessingOptions options)
{
if (options == null)
@@ -187,9 +185,9 @@ namespace Emby.Drawing
}
dateModified = supportedImageInfo.dateModified;
- bool requiresTransparency = TransparentImageTypes.Contains(Path.GetExtension(originalImagePath));
+ bool requiresTransparency = _transparentImageTypes.Contains(Path.GetExtension(originalImagePath));
- if (options.Enhancers.Length > 0)
+ if (options.Enhancers.Count > 0)
{
if (item == null)
{
@@ -279,7 +277,7 @@ namespace Emby.Drawing
}
}
- private ImageFormat GetOutputFormat(ImageFormat[] clientSupportedFormats, bool requiresTransparency)
+ private ImageFormat GetOutputFormat(IReadOnlyCollection<ImageFormat> clientSupportedFormats, bool requiresTransparency)
{
var serverFormats = GetSupportedImageOutputFormats();
@@ -321,11 +319,6 @@ namespace Emby.Drawing
}
/// <summary>
- /// Increment this when there's a change requiring caches to be invalidated
- /// </summary>
- private const string Version = "3";
-
- /// <summary>
/// Gets the cache file path based on a set of parameters
/// </summary>
private string GetCacheFilePath(string originalPath, ImageDimensions outputSize, int quality, DateTime dateModified, ImageFormat format, bool addPlayedIndicator, double percentPlayed, int? unwatchedCount, int? blur, string backgroundColor, string foregroundLayer)
@@ -372,9 +365,11 @@ namespace Emby.Drawing
return GetCachePath(ResizedImageCachePath, filename, "." + format.ToString().ToLowerInvariant());
}
+ /// <inheritdoc />
public ImageDimensions GetImageDimensions(BaseItem item, ItemImageInfo info)
=> GetImageDimensions(item, info, true);
+ /// <inheritdoc />
public ImageDimensions GetImageDimensions(BaseItem item, ItemImageInfo info, bool updateItem)
{
int width = info.Width;
@@ -400,26 +395,19 @@ namespace Emby.Drawing
return size;
}
- /// <summary>
- /// Gets the size of the image.
- /// </summary>
+ /// <inheritdoc />
public ImageDimensions GetImageDimensions(string path)
=> _imageEncoder.GetImageSize(path);
- /// <summary>
- /// Gets the image cache tag.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="image">The image.</param>
- /// <returns>Guid.</returns>
- /// <exception cref="ArgumentNullException">item</exception>
+ /// <inheritdoc />
public string GetImageCacheTag(BaseItem item, ItemImageInfo image)
{
- var supportedEnhancers = GetSupportedEnhancers(item, image.Type);
+ var supportedEnhancers = GetSupportedEnhancers(item, image.Type).ToArray();
return GetImageCacheTag(item, image, supportedEnhancers);
}
+ /// <inheritdoc />
public string GetImageCacheTag(BaseItem item, ChapterInfo chapter)
{
try
@@ -437,22 +425,15 @@ namespace Emby.Drawing
}
}
- /// <summary>
- /// Gets the image cache tag.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="image">The image.</param>
- /// <param name="imageEnhancers">The image enhancers.</param>
- /// <returns>Guid.</returns>
- /// <exception cref="ArgumentNullException">item</exception>
- public string GetImageCacheTag(BaseItem item, ItemImageInfo image, IImageEnhancer[] imageEnhancers)
+ /// <inheritdoc />
+ public string GetImageCacheTag(BaseItem item, ItemImageInfo image, IReadOnlyCollection<IImageEnhancer> imageEnhancers)
{
string originalImagePath = image.Path;
DateTime dateModified = image.DateModified;
ImageType imageType = image.Type;
// Optimization
- if (imageEnhancers.Length == 0)
+ if (imageEnhancers.Count == 0)
{
return (originalImagePath + dateModified.Ticks).GetMD5().ToString("N", CultureInfo.InvariantCulture);
}
@@ -480,7 +461,7 @@ namespace Emby.Drawing
{
try
{
- string filename = (originalImagePath + dateModified.Ticks.ToString(UsCulture)).GetMD5().ToString("N", CultureInfo.InvariantCulture);
+ string filename = (originalImagePath + dateModified.Ticks.ToString(CultureInfo.InvariantCulture)).GetMD5().ToString("N", CultureInfo.InvariantCulture);
string cacheExtension = _mediaEncoder().SupportsEncoder("libwebp") ? ".webp" : ".png";
var outputPath = Path.Combine(_appPaths.ImageCachePath, "converted-images", filename + cacheExtension);
@@ -507,16 +488,10 @@ namespace Emby.Drawing
return (originalImagePath, dateModified);
}
- /// <summary>
- /// Gets the enhanced image.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="imageType">Type of the image.</param>
- /// <param name="imageIndex">Index of the image.</param>
- /// <returns>Task{System.String}.</returns>
+ /// <inheritdoc />
public async Task<string> GetEnhancedImage(BaseItem item, ImageType imageType, int imageIndex)
{
- var enhancers = GetSupportedEnhancers(item, imageType);
+ var enhancers = GetSupportedEnhancers(item, imageType).ToArray();
ItemImageInfo imageInfo = item.GetImageInfo(imageType, imageIndex);
@@ -532,7 +507,7 @@ namespace Emby.Drawing
bool inputImageSupportsTransparency,
BaseItem item,
int imageIndex,
- IImageEnhancer[] enhancers,
+ IReadOnlyCollection<IImageEnhancer> enhancers,
CancellationToken cancellationToken)
{
var originalImagePath = image.Path;
@@ -573,6 +548,7 @@ namespace Emby.Drawing
/// <param name="imageIndex">Index of the image.</param>
/// <param name="supportedEnhancers">The supported enhancers.</param>
/// <param name="cacheGuid">The cache unique identifier.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task&lt;System.String&gt;.</returns>
/// <exception cref="ArgumentNullException">
/// originalImagePath
@@ -584,9 +560,9 @@ namespace Emby.Drawing
BaseItem item,
ImageType imageType,
int imageIndex,
- IImageEnhancer[] supportedEnhancers,
+ IReadOnlyCollection<IImageEnhancer> supportedEnhancers,
string cacheGuid,
- CancellationToken cancellationToken)
+ CancellationToken cancellationToken = default)
{
if (string.IsNullOrEmpty(originalImagePath))
{
@@ -680,6 +656,7 @@ namespace Emby.Drawing
{
throw new ArgumentNullException(nameof(path));
}
+
if (string.IsNullOrEmpty(uniqueName))
{
throw new ArgumentNullException(nameof(uniqueName));
@@ -722,6 +699,7 @@ namespace Emby.Drawing
return Path.Combine(path, prefix, filename);
}
+ /// <inheritdoc />
public void CreateImageCollage(ImageCollageOptions options)
{
_logger.LogInformation("Creating image collage and saving to {Path}", options.OutputPath);
@@ -731,38 +709,25 @@ namespace Emby.Drawing
_logger.LogInformation("Completed creation of image collage and saved to {Path}", options.OutputPath);
}
- public IImageEnhancer[] GetSupportedEnhancers(BaseItem item, ImageType imageType)
+ /// <inheritdoc />
+ public IEnumerable<IImageEnhancer> GetSupportedEnhancers(BaseItem item, ImageType imageType)
{
- List<IImageEnhancer> list = null;
-
foreach (var i in ImageEnhancers)
{
- try
- {
- if (i.Supports(item, imageType))
- {
- if (list == null)
- {
- list = new List<IImageEnhancer>();
- }
- list.Add(i);
- }
- }
- catch (Exception ex)
+ if (i.Supports(item, imageType))
{
- _logger.LogError(ex, "Error in image enhancer: {0}", i.GetType().Name);
+ yield return i;
}
}
-
- return list == null ? Array.Empty<IImageEnhancer>() : list.ToArray();
}
- private Dictionary<string, LockInfo> _locks = new Dictionary<string, LockInfo>();
+
private class LockInfo
{
public SemaphoreSlim Lock = new SemaphoreSlim(1, 1);
public int Count = 1;
}
+
private LockInfo GetLock(string key)
{
lock (_locks)
@@ -795,7 +760,7 @@ namespace Emby.Drawing
}
}
- private bool _disposed;
+ /// <inheritdoc />
public void Dispose()
{
_disposed = true;
diff --git a/Emby.Drawing/NullImageEncoder.cs b/Emby.Drawing/NullImageEncoder.cs
index fc4a5af9f..5af7f1622 100644
--- a/Emby.Drawing/NullImageEncoder.cs
+++ b/Emby.Drawing/NullImageEncoder.cs
@@ -5,36 +5,40 @@ using MediaBrowser.Model.Drawing;
namespace Emby.Drawing
{
+ /// <summary>
+ /// A fallback implementation of <see cref="IImageEncoder" />.
+ /// </summary>
public class NullImageEncoder : IImageEncoder
{
+ /// <inheritdoc />
public IReadOnlyCollection<string> SupportedInputFormats
=> new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "png", "jpeg", "jpg" };
+ /// <inheritdoc />
public IReadOnlyCollection<ImageFormat> SupportedOutputFormats
=> new HashSet<ImageFormat>() { ImageFormat.Jpg, ImageFormat.Png };
- public void CropWhiteSpace(string inputPath, string outputPath)
- {
- throw new NotImplementedException();
- }
-
- public string EncodeImage(string inputPath, DateTime dateModified, string outputPath, bool autoOrient, ImageOrientation? orientation, int quality, ImageProcessingOptions options, ImageFormat selectedOutputFormat)
- {
- throw new NotImplementedException();
- }
-
- public void CreateImageCollage(ImageCollageOptions options)
- {
- throw new NotImplementedException();
- }
-
+ /// <inheritdoc />
public string Name => "Null Image Encoder";
+ /// <inheritdoc />
public bool SupportsImageCollageCreation => false;
+ /// <inheritdoc />
public bool SupportsImageEncoding => false;
+ /// <inheritdoc />
public ImageDimensions GetImageSize(string path)
+ => throw new NotImplementedException();
+
+ /// <inheritdoc />
+ public string EncodeImage(string inputPath, DateTime dateModified, string outputPath, bool autoOrient, ImageOrientation? orientation, int quality, ImageProcessingOptions options, ImageFormat selectedOutputFormat)
+ {
+ throw new NotImplementedException();
+ }
+
+ /// <inheritdoc />
+ public void CreateImageCollage(ImageCollageOptions options)
{
throw new NotImplementedException();
}
diff --git a/Emby.IsoMounting/IsoMounter/IsoMounter.csproj b/Emby.IsoMounting/IsoMounter/IsoMounter.csproj
index dafa51cd5..0778b987b 100644
--- a/Emby.IsoMounting/IsoMounter/IsoMounter.csproj
+++ b/Emby.IsoMounting/IsoMounter/IsoMounter.csproj
@@ -12,6 +12,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+ <GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
</Project>
diff --git a/Emby.Naming/Emby.Naming.csproj b/Emby.Naming/Emby.Naming.csproj
index 9e2a4950f..0b1ce2fce 100644
--- a/Emby.Naming/Emby.Naming.csproj
+++ b/Emby.Naming/Emby.Naming.csproj
@@ -23,7 +23,7 @@
<!-- Code analysers-->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
- <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.3" />
+ <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.4" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" />
<PackageReference Include="SerilogAnalyzer" Version="0.15.0" />
</ItemGroup>
diff --git a/Emby.Notifications/Emby.Notifications.csproj b/Emby.Notifications/Emby.Notifications.csproj
index 5c68e48c8..cbd3bde4f 100644
--- a/Emby.Notifications/Emby.Notifications.csproj
+++ b/Emby.Notifications/Emby.Notifications.csproj
@@ -3,6 +3,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+ <GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
<ItemGroup>
diff --git a/Emby.Photos/Emby.Photos.csproj b/Emby.Photos/Emby.Photos.csproj
index c9830abc5..db73cb521 100644
--- a/Emby.Photos/Emby.Photos.csproj
+++ b/Emby.Photos/Emby.Photos.csproj
@@ -10,12 +10,13 @@
</ItemGroup>
<ItemGroup>
- <PackageReference Include="TagLibSharp" Version="2.2.0-beta" />
+ <PackageReference Include="TagLibSharp" Version="2.2.0" />
</ItemGroup>
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+ <GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
</Project>
diff --git a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs
index 3e44c9c0a..fb4ffd74b 100644
--- a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs
+++ b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs
@@ -346,7 +346,7 @@ namespace Emby.Server.Implementations.Activity
});
}
- private void OnPluginUpdated(object sender, GenericEventArgs<Tuple<IPlugin, PackageVersionInfo>> e)
+ private void OnPluginUpdated(object sender, GenericEventArgs<(IPlugin, PackageVersionInfo)> e)
{
CreateLogEntry(new ActivityLogEntry
{
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index 09847b2f8..68b02d4a2 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -121,6 +121,10 @@ namespace Emby.Server.Implementations
/// </summary>
public abstract class ApplicationHost : IServerApplicationHost, IDisposable
{
+ private SqliteUserRepository _userRepository;
+
+ private SqliteDisplayPreferencesRepository _displayPreferencesRepository;
+
/// <summary>
/// Gets a value indicating whether this instance can self restart.
/// </summary>
@@ -291,8 +295,6 @@ namespace Emby.Server.Implementations
/// <value>The user data repository.</value>
private IUserDataManager UserDataManager { get; set; }
- private IUserRepository UserRepository { get; set; }
-
internal SqliteItemRepository ItemRepository { get; set; }
private INotificationManager NotificationManager { get; set; }
@@ -510,13 +512,8 @@ namespace Emby.Server.Implementations
return AllConcreteTypes.Where(i => currentType.IsAssignableFrom(i));
}
- /// <summary>
- /// Gets the exports.
- /// </summary>
- /// <typeparam name="T">The type</typeparam>
- /// <param name="manageLifetime">if set to <c>true</c> [manage lifetime].</param>
- /// <returns>IEnumerable{``0}.</returns>
- public IEnumerable<T> GetExports<T>(bool manageLifetime = true)
+ /// <inheritdoc />
+ public IReadOnlyCollection<T> GetExports<T>(bool manageLifetime = true)
{
var parts = GetExportTypes<T>()
.Select(CreateInstanceSafe)
@@ -538,6 +535,7 @@ namespace Emby.Server.Implementations
/// <summary>
/// Runs the startup tasks.
/// </summary>
+ /// <returns><see cref="Task" />.</returns>
public async Task RunStartupTasksAsync()
{
Logger.LogInformation("Running startup tasks");
@@ -550,7 +548,7 @@ namespace Emby.Server.Implementations
Logger.LogInformation("ServerId: {0}", SystemId);
- var entryPoints = GetExports<IServerEntryPoint>().ToList();
+ var entryPoints = GetExports<IServerEntryPoint>();
var stopWatch = new Stopwatch();
stopWatch.Start();
@@ -757,8 +755,12 @@ namespace Emby.Server.Implementations
UserDataManager = new UserDataManager(LoggerFactory, ServerConfigurationManager, () => UserManager);
serviceCollection.AddSingleton(UserDataManager);
- var displayPreferencesRepo = new SqliteDisplayPreferencesRepository(LoggerFactory, JsonSerializer, ApplicationPaths, FileSystemManager);
- serviceCollection.AddSingleton<IDisplayPreferencesRepository>(displayPreferencesRepo);
+ _displayPreferencesRepository = new SqliteDisplayPreferencesRepository(
+ LoggerFactory.CreateLogger<SqliteDisplayPreferencesRepository>(),
+ JsonSerializer,
+ ApplicationPaths,
+ FileSystemManager);
+ serviceCollection.AddSingleton<IDisplayPreferencesRepository>(_displayPreferencesRepository);
ItemRepository = new SqliteItemRepository(ServerConfigurationManager, this, JsonSerializer, LoggerFactory, LocalizationManager);
serviceCollection.AddSingleton<IItemRepository>(ItemRepository);
@@ -766,9 +768,9 @@ namespace Emby.Server.Implementations
AuthenticationRepository = GetAuthenticationRepository();
serviceCollection.AddSingleton(AuthenticationRepository);
- UserRepository = GetUserRepository();
+ _userRepository = GetUserRepository();
- UserManager = new UserManager(LoggerFactory, ServerConfigurationManager, UserRepository, XmlSerializer, NetworkManager, () => ImageProcessor, () => DtoService, this, JsonSerializer, FileSystemManager);
+ UserManager = new UserManager(LoggerFactory, ServerConfigurationManager, _userRepository, XmlSerializer, NetworkManager, () => ImageProcessor, () => DtoService, this, JsonSerializer, FileSystemManager);
serviceCollection.AddSingleton(UserManager);
LibraryManager = new LibraryManager(this, LoggerFactory, TaskManager, UserManager, ServerConfigurationManager, UserDataManager, () => LibraryMonitor, FileSystemManager, () => ProviderManager, () => UserViewManager);
@@ -801,7 +803,7 @@ namespace Emby.Server.Implementations
serviceCollection.AddSingleton(HttpServer);
- ImageProcessor = GetImageProcessor();
+ ImageProcessor = new ImageProcessor(LoggerFactory.CreateLogger<ImageProcessor>(), ServerConfigurationManager.ApplicationPaths, FileSystemManager, ImageEncoder, () => LibraryManager, () => MediaEncoder);
serviceCollection.AddSingleton(ImageProcessor);
TVSeriesManager = new TVSeriesManager(UserManager, UserDataManager, LibraryManager, ServerConfigurationManager);
@@ -884,7 +886,7 @@ namespace Emby.Server.Implementations
serviceCollection.AddSingleton(typeof(IResourceFileManager), typeof(ResourceFileManager));
- displayPreferencesRepo.Initialize();
+ _displayPreferencesRepository.Initialize();
var userDataRepo = new SqliteUserDataRepository(LoggerFactory, ApplicationPaths);
@@ -953,18 +955,16 @@ namespace Emby.Server.Implementations
}
}
- private IImageProcessor GetImageProcessor()
- {
- return new ImageProcessor(LoggerFactory, ServerConfigurationManager.ApplicationPaths, FileSystemManager, ImageEncoder, () => LibraryManager, () => MediaEncoder);
- }
-
/// <summary>
/// Gets the user repository.
/// </summary>
- /// <returns>Task{IUserRepository}.</returns>
- private IUserRepository GetUserRepository()
+ /// <returns><see cref="Task{SqliteUserRepository}" />.</returns>
+ private SqliteUserRepository GetUserRepository()
{
- var repo = new SqliteUserRepository(LoggerFactory, ApplicationPaths, JsonSerializer);
+ var repo = new SqliteUserRepository(
+ LoggerFactory.CreateLogger<SqliteUserRepository>(),
+ ApplicationPaths,
+ JsonSerializer);
repo.Initialize();
@@ -1087,7 +1087,7 @@ namespace Emby.Server.Implementations
GetExports<IMetadataSaver>(),
GetExports<IExternalId>());
- ImageProcessor.AddParts(GetExports<IImageEnhancer>());
+ ImageProcessor.ImageEnhancers = GetExports<IImageEnhancer>();
LiveTvManager.AddParts(GetExports<ILiveTvService>(), GetExports<ITunerHost>(), GetExports<IListingsProvider>());
@@ -1528,8 +1528,6 @@ namespace Emby.Server.Implementations
{
Url = Url,
LogErrorResponseBody = false,
- LogErrors = false,
- LogRequest = false,
BufferContent = false,
CancellationToken = cancellationToken
}).ConfigureAwait(false))
@@ -1681,8 +1679,8 @@ namespace Emby.Server.Implementations
private async Task<bool> IsIpAddressValidAsync(IPAddress address, CancellationToken cancellationToken)
{
- if (address.Equals(IPAddress.Loopback) ||
- address.Equals(IPAddress.IPv6Loopback))
+ if (address.Equals(IPAddress.Loopback)
+ || address.Equals(IPAddress.IPv6Loopback))
{
return true;
}
@@ -1695,12 +1693,6 @@ namespace Emby.Server.Implementations
return cachedResult;
}
-#if DEBUG
- const bool LogPing = true;
-#else
- const bool LogPing = false;
-#endif
-
try
{
using (var response = await HttpClient.SendAsync(
@@ -1708,8 +1700,6 @@ namespace Emby.Server.Implementations
{
Url = apiUrl,
LogErrorResponseBody = false,
- LogErrors = LogPing,
- LogRequest = LogPing,
BufferContent = false,
CancellationToken = cancellationToken
}, HttpMethod.Post).ConfigureAwait(false))
@@ -1910,10 +1900,12 @@ namespace Emby.Server.Implementations
}
}
- UserRepository.Dispose();
+ _userRepository?.Dispose();
+ _displayPreferencesRepository.Dispose();
}
- UserRepository = null;
+ _userRepository = null;
+ _displayPreferencesRepository = null;
_disposed = true;
}
diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
index 010ad6384..a5bb47afb 100644
--- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
+++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
@@ -124,7 +124,7 @@ namespace Emby.Server.Implementations.Data
}
WriteConnection.Execute("PRAGMA temp_store=" + (int)TempStore);
-
+
// Configuration and pragmas can affect VACUUM so it needs to be last.
WriteConnection.Execute("VACUUM");
@@ -218,7 +218,7 @@ namespace Emby.Server.Implementations.Data
WriteLock.Wait();
try
{
- WriteConnection.Dispose();
+ WriteConnection?.Dispose();
}
finally
{
diff --git a/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs b/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs
index 87096e72f..77f5d9479 100644
--- a/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs
@@ -21,8 +21,8 @@ namespace Emby.Server.Implementations.Data
{
private readonly IFileSystem _fileSystem;
- public SqliteDisplayPreferencesRepository(ILoggerFactory loggerFactory, IJsonSerializer jsonSerializer, IApplicationPaths appPaths, IFileSystem fileSystem)
- : base(loggerFactory.CreateLogger(nameof(SqliteDisplayPreferencesRepository)))
+ public SqliteDisplayPreferencesRepository(ILogger<SqliteDisplayPreferencesRepository> logger, IJsonSerializer jsonSerializer, IApplicationPaths appPaths, IFileSystem fileSystem)
+ : base(logger)
{
_jsonSerializer = jsonSerializer;
_fileSystem = fileSystem;
diff --git a/Emby.Server.Implementations/Data/SqliteUserRepository.cs b/Emby.Server.Implementations/Data/SqliteUserRepository.cs
index de2354eef..d6d250fb3 100644
--- a/Emby.Server.Implementations/Data/SqliteUserRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteUserRepository.cs
@@ -18,10 +18,10 @@ namespace Emby.Server.Implementations.Data
private readonly IJsonSerializer _jsonSerializer;
public SqliteUserRepository(
- ILoggerFactory loggerFactory,
+ ILogger<SqliteUserRepository> logger,
IServerApplicationPaths appPaths,
IJsonSerializer jsonSerializer)
- : base(loggerFactory.CreateLogger(nameof(SqliteUserRepository)))
+ : base(logger)
{
_jsonSerializer = jsonSerializer;
diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs
index 6e7aa1313..1a7f10634 100644
--- a/Emby.Server.Implementations/Dto/DtoService.cs
+++ b/Emby.Server.Implementations/Dto/DtoService.cs
@@ -1364,7 +1364,7 @@ namespace Emby.Server.Implementations.Dto
return null;
}
- var supportedEnhancers = _imageProcessor.GetSupportedEnhancers(item, ImageType.Primary);
+ var supportedEnhancers = _imageProcessor.GetSupportedEnhancers(item, ImageType.Primary).ToArray();
ImageDimensions size;
diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
index f07f8e3bf..c78d96d4a 100644
--- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj
+++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
@@ -32,7 +32,7 @@
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="2.2.0" />
- <PackageReference Include="ServiceStack.Text.Core" Version="5.5.0" />
+ <PackageReference Include="ServiceStack.Text.Core" Version="5.6.0" />
<PackageReference Include="sharpcompress" Version="0.23.0" />
<PackageReference Include="SQLitePCL.pretty.netstandard" Version="1.0.0" />
</ItemGroup>
@@ -47,13 +47,14 @@
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
- <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
- <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+ <PropertyGroup>
+ <!-- We need at least C# 7.3 to compare tuples-->
+ <LangVersion>latest</LangVersion>
</PropertyGroup>
<!-- Code analysers-->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
- <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.3" />
+ <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.4" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" />
<PackageReference Include="SerilogAnalyzer" Version="0.15.0" />
</ItemGroup>
diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs
index a933b53f5..0dd4d4ca5 100644
--- a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs
+++ b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs
@@ -5,9 +5,6 @@ using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
-using System.Net.Http.Headers;
-using System.Text;
-using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
@@ -20,7 +17,7 @@ using Microsoft.Net.Http.Headers;
namespace Emby.Server.Implementations.HttpClientManager
{
/// <summary>
- /// Class HttpClientManager
+ /// Class HttpClientManager.
/// </summary>
public class HttpClientManager : IHttpClient
{
@@ -45,19 +42,9 @@ namespace Emby.Server.Implementations.HttpClientManager
IFileSystem fileSystem,
Func<string> defaultUserAgentFn)
{
- if (appPaths == null)
- {
- throw new ArgumentNullException(nameof(appPaths));
- }
-
- if (logger == null)
- {
- throw new ArgumentNullException(nameof(logger));
- }
-
- _logger = logger;
+ _logger = logger ?? throw new ArgumentNullException(nameof(logger));
_fileSystem = fileSystem;
- _appPaths = appPaths;
+ _appPaths = appPaths ?? throw new ArgumentNullException(nameof(appPaths));
_defaultUserAgentFn = defaultUserAgentFn;
}
@@ -118,7 +105,7 @@ namespace Emby.Server.Implementations.HttpClientManager
request.Headers.Add(HeaderNames.Connection, "Keep-Alive");
}
- //request.Headers.Add(HeaderNames.CacheControl, "no-cache");
+ // request.Headers.Add(HeaderNames.CacheControl, "no-cache");
/*
if (!string.IsNullOrWhiteSpace(userInfo))
@@ -196,7 +183,7 @@ namespace Emby.Server.Implementations.HttpClientManager
}
var url = options.Url;
- var urlHash = url.ToLowerInvariant().GetMD5().ToString("N", CultureInfo.InvariantCulture);
+ var urlHash = url.ToUpperInvariant().GetMD5().ToString("N", CultureInfo.InvariantCulture);
var responseCachePath = Path.Combine(_appPaths.CachePath, "httpclient", urlHash);
@@ -239,7 +226,13 @@ namespace Emby.Server.Implementations.HttpClientManager
{
Directory.CreateDirectory(Path.GetDirectoryName(responseCachePath));
- using (var fileStream = _fileSystem.GetFileStream(responseCachePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.None, true))
+ using (var fileStream = new FileStream(
+ responseCachePath,
+ FileMode.Create,
+ FileAccess.Write,
+ FileShare.None,
+ StreamDefaults.DefaultFileStreamBufferSize,
+ true))
{
await response.Content.CopyToAsync(fileStream).ConfigureAwait(false);
@@ -278,16 +271,11 @@ namespace Emby.Server.Implementations.HttpClientManager
}
}
- if (options.LogRequest)
- {
- _logger.LogDebug("HttpClientManager {0}: {1}", httpMethod.ToString(), options.Url);
- }
-
options.CancellationToken.ThrowIfCancellationRequested();
var response = await client.SendAsync(
httpWebRequest,
- options.BufferContent ? HttpCompletionOption.ResponseContentRead : HttpCompletionOption.ResponseHeadersRead,
+ options.BufferContent || options.CacheMode == CacheMode.Unconditional ? HttpCompletionOption.ResponseContentRead : HttpCompletionOption.ResponseHeadersRead,
options.CancellationToken).ConfigureAwait(false);
await EnsureSuccessStatusCode(response, options).ConfigureAwait(false);
@@ -308,138 +296,6 @@ namespace Emby.Server.Implementations.HttpClientManager
public Task<HttpResponseInfo> Post(HttpRequestOptions options)
=> SendAsync(options, HttpMethod.Post);
- /// <summary>
- /// Downloads the contents of a given url into a temporary location
- /// </summary>
- /// <param name="options">The options.</param>
- /// <returns>Task{System.String}.</returns>
- public async Task<string> GetTempFile(HttpRequestOptions options)
- {
- var response = await GetTempFileResponse(options).ConfigureAwait(false);
- return response.TempFilePath;
- }
-
- public async Task<HttpResponseInfo> GetTempFileResponse(HttpRequestOptions options)
- {
- ValidateParams(options);
-
- Directory.CreateDirectory(_appPaths.TempDirectory);
-
- var tempFile = Path.Combine(_appPaths.TempDirectory, Guid.NewGuid() + ".tmp");
-
- if (options.Progress == null)
- {
- throw new ArgumentException("Options did not have a Progress value.", nameof(options));
- }
-
- options.CancellationToken.ThrowIfCancellationRequested();
-
- var httpWebRequest = GetRequestMessage(options, HttpMethod.Get);
-
- options.Progress.Report(0);
-
- if (options.LogRequest)
- {
- _logger.LogDebug("HttpClientManager.GetTempFileResponse url: {0}", options.Url);
- }
-
- var client = GetHttpClient(options.Url);
-
- try
- {
- options.CancellationToken.ThrowIfCancellationRequested();
-
- using (var response = (await client.SendAsync(httpWebRequest, options.CancellationToken).ConfigureAwait(false)))
- {
- await EnsureSuccessStatusCode(response, options).ConfigureAwait(false);
-
- options.CancellationToken.ThrowIfCancellationRequested();
-
- using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
- using (var fs = _fileSystem.GetFileStream(tempFile, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
- {
- await stream.CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, options.CancellationToken).ConfigureAwait(false);
- }
-
- options.Progress.Report(100);
-
- var responseInfo = new HttpResponseInfo(response.Headers, response.Content.Headers)
- {
- TempFilePath = tempFile,
- StatusCode = response.StatusCode,
- ContentType = response.Content.Headers.ContentType?.MediaType,
- ContentLength = response.Content.Headers.ContentLength
- };
-
- return responseInfo;
- }
- }
- catch (Exception ex)
- {
- if (File.Exists(tempFile))
- {
- File.Delete(tempFile);
- }
-
- throw GetException(ex, options);
- }
- }
-
- private Exception GetException(Exception ex, HttpRequestOptions options)
- {
- if (ex is HttpException)
- {
- return ex;
- }
-
- var webException = ex as WebException
- ?? ex.InnerException as WebException;
-
- if (webException != null)
- {
- if (options.LogErrors)
- {
- _logger.LogError(webException, "Error {Status} getting response from {Url}", webException.Status, options.Url);
- }
-
- var exception = new HttpException(webException.Message, webException);
-
- using (var response = webException.Response as HttpWebResponse)
- {
- if (response != null)
- {
- exception.StatusCode = response.StatusCode;
- }
- }
-
- if (!exception.StatusCode.HasValue)
- {
- if (webException.Status == WebExceptionStatus.NameResolutionFailure ||
- webException.Status == WebExceptionStatus.ConnectFailure)
- {
- exception.IsTimedOut = true;
- }
- }
-
- return exception;
- }
-
- var operationCanceledException = ex as OperationCanceledException
- ?? ex.InnerException as OperationCanceledException;
-
- if (operationCanceledException != null)
- {
- return GetCancellationException(options, options.CancellationToken, operationCanceledException);
- }
-
- if (options.LogErrors)
- {
- _logger.LogError(ex, "Error getting response from {Url}", options.Url);
- }
-
- return ex;
- }
-
private void ValidateParams(HttpRequestOptions options)
{
if (string.IsNullOrEmpty(options.Url))
@@ -471,35 +327,6 @@ namespace Emby.Server.Implementations.HttpClientManager
return url;
}
- /// <summary>
- /// Throws the cancellation exception.
- /// </summary>
- /// <param name="options">The options.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <param name="exception">The exception.</param>
- /// <returns>Exception.</returns>
- private Exception GetCancellationException(HttpRequestOptions options, CancellationToken cancellationToken, OperationCanceledException exception)
- {
- // If the HttpClient's timeout is reached, it will cancel the Task internally
- if (!cancellationToken.IsCancellationRequested)
- {
- var msg = string.Format("Connection to {0} timed out", options.Url);
-
- if (options.LogErrors)
- {
- _logger.LogError(msg);
- }
-
- // Throw an HttpException so that the caller doesn't think it was cancelled by user code
- return new HttpException(msg, exception)
- {
- IsTimedOut = true
- };
- }
-
- return exception;
- }
-
private async Task EnsureSuccessStatusCode(HttpResponseMessage response, HttpRequestOptions options)
{
if (response.IsSuccessStatusCode)
@@ -507,8 +334,11 @@ namespace Emby.Server.Implementations.HttpClientManager
return;
}
- var msg = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
- _logger.LogError("HTTP request failed with message: {Message}", msg);
+ if (options.LogErrorResponseBody)
+ {
+ var msg = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
+ _logger.LogError("HTTP request failed with message: {Message}", msg);
+ }
throw new HttpException(response.ReasonPhrase)
{
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
index db016ec70..ed254accb 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
@@ -256,7 +256,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
var uri = new Uri(GetApiUrl(info));
- using (var manager = new HdHomerunManager(_socketFactory, Logger))
+ using (var manager = new HdHomerunManager(Logger))
{
// Legacy HdHomeruns are IPv4 only
var ipInfo = IPAddress.Parse(uri.Host);
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs
index 6e79441da..c19552428 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs
@@ -1,12 +1,13 @@
using System;
+using System.Buffers;
using System.Collections.Generic;
using System.Net;
+using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Model.Net;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
@@ -78,35 +79,34 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
public class HdHomerunManager : IDisposable
{
- public static int HdHomeRunPort = 65001;
+ public const int HdHomeRunPort = 65001;
// Message constants
- private static byte GetSetName = 3;
- private static byte GetSetValue = 4;
- private static byte GetSetLockkey = 21;
- private static ushort GetSetRequest = 4;
- private static ushort GetSetReply = 5;
+ private const byte GetSetName = 3;
+ private const byte GetSetValue = 4;
+ private const byte GetSetLockkey = 21;
+ private const ushort GetSetRequest = 4;
+ private const ushort GetSetReply = 5;
+
+ private readonly ILogger _logger;
private uint? _lockkey = null;
private int _activeTuner = -1;
- private readonly ISocketFactory _socketFactory;
- private IPAddress _remoteIp;
+ private IPEndPoint _remoteEndPoint;
- private ILogger _logger;
- private ISocket _currentTcpSocket;
+ private TcpClient _tcpClient;
- public HdHomerunManager(ISocketFactory socketFactory, ILogger logger)
+ public HdHomerunManager(ILogger logger)
{
- _socketFactory = socketFactory;
_logger = logger;
}
public void Dispose()
{
- using (var socket = _currentTcpSocket)
+ using (var socket = _tcpClient)
{
if (socket != null)
{
- _currentTcpSocket = null;
+ _tcpClient = null;
var task = StopStreaming(socket);
Task.WaitAll(task);
@@ -116,35 +116,38 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
public async Task<bool> CheckTunerAvailability(IPAddress remoteIp, int tuner, CancellationToken cancellationToken)
{
- using (var socket = _socketFactory.CreateTcpSocket(remoteIp, HdHomeRunPort))
+ using (var client = new TcpClient(new IPEndPoint(remoteIp, HdHomeRunPort)))
+ using (var stream = client.GetStream())
{
- return await CheckTunerAvailability(socket, remoteIp, tuner, cancellationToken).ConfigureAwait(false);
+ return await CheckTunerAvailability(stream, tuner, cancellationToken).ConfigureAwait(false);
}
}
- private static async Task<bool> CheckTunerAvailability(ISocket socket, IPAddress remoteIp, int tuner, CancellationToken cancellationToken)
+ private static async Task<bool> CheckTunerAvailability(NetworkStream stream, int tuner, CancellationToken cancellationToken)
{
- var ipEndPoint = new IPEndPoint(remoteIp, HdHomeRunPort);
-
var lockkeyMsg = CreateGetMessage(tuner, "lockkey");
- await socket.SendToAsync(lockkeyMsg, 0, lockkeyMsg.Length, ipEndPoint, cancellationToken);
+ await stream.WriteAsync(lockkeyMsg, 0, lockkeyMsg.Length, cancellationToken).ConfigureAwait(false);
- var receiveBuffer = new byte[8192];
- var response = await socket.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, cancellationToken).ConfigureAwait(false);
+ byte[] buffer = ArrayPool<byte>.Shared.Rent(8192);
+ try
+ {
+ int receivedBytes = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false);
- ParseReturnMessage(response.Buffer, response.ReceivedBytes, out string returnVal);
+ ParseReturnMessage(buffer, receivedBytes, out string returnVal);
- return string.Equals(returnVal, "none", StringComparison.OrdinalIgnoreCase);
+ return string.Equals(returnVal, "none", StringComparison.OrdinalIgnoreCase);
+ }
+ finally
+ {
+ ArrayPool<byte>.Shared.Return(buffer);
+ }
}
public async Task StartStreaming(IPAddress remoteIp, IPAddress localIp, int localPort, IHdHomerunChannelCommands commands, int numTuners, CancellationToken cancellationToken)
{
- _remoteIp = remoteIp;
+ _remoteEndPoint = new IPEndPoint(remoteIp, HdHomeRunPort);
- var tcpClient = _socketFactory.CreateTcpSocket(_remoteIp, HdHomeRunPort);
- _currentTcpSocket = tcpClient;
-
- var receiveBuffer = new byte[8192];
+ _tcpClient = new TcpClient(_remoteEndPoint);
if (!_lockkey.HasValue)
{
@@ -153,51 +156,61 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
}
var lockKeyValue = _lockkey.Value;
+ var stream = _tcpClient.GetStream();
- var ipEndPoint = new IPEndPoint(_remoteIp, HdHomeRunPort);
-
- for (int i = 0; i < numTuners; ++i)
+ byte[] buffer = ArrayPool<byte>.Shared.Rent(8192);
+ try
{
- if (!await CheckTunerAvailability(tcpClient, _remoteIp, i, cancellationToken).ConfigureAwait(false))
- continue;
-
- _activeTuner = i;
- var lockKeyString = string.Format("{0:d}", lockKeyValue);
- var lockkeyMsg = CreateSetMessage(i, "lockkey", lockKeyString, null);
- await tcpClient.SendToAsync(lockkeyMsg, 0, lockkeyMsg.Length, ipEndPoint, cancellationToken).ConfigureAwait(false);
- var response = await tcpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, cancellationToken).ConfigureAwait(false);
- // parse response to make sure it worked
- if (!ParseReturnMessage(response.Buffer, response.ReceivedBytes, out var returnVal))
- continue;
-
- var commandList = commands.GetCommands();
- foreach (Tuple<string, string> command in commandList)
+ for (int i = 0; i < numTuners; ++i)
{
- var channelMsg = CreateSetMessage(i, command.Item1, command.Item2, lockKeyValue);
- await tcpClient.SendToAsync(channelMsg, 0, channelMsg.Length, ipEndPoint, cancellationToken).ConfigureAwait(false);
- response = await tcpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, cancellationToken).ConfigureAwait(false);
+ if (!await CheckTunerAvailability(stream, i, cancellationToken).ConfigureAwait(false))
+ {
+ continue;
+ }
+
+ _activeTuner = i;
+ var lockKeyString = string.Format("{0:d}", lockKeyValue);
+ var lockkeyMsg = CreateSetMessage(i, "lockkey", lockKeyString, null);
+ await stream.WriteAsync(lockkeyMsg, 0, lockkeyMsg.Length, cancellationToken).ConfigureAwait(false);
+ int receivedBytes = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false);
// parse response to make sure it worked
- if (!ParseReturnMessage(response.Buffer, response.ReceivedBytes, out returnVal))
+ if (!ParseReturnMessage(buffer, receivedBytes, out var returnVal))
{
- await ReleaseLockkey(tcpClient, lockKeyValue).ConfigureAwait(false);
continue;
}
- }
+ var commandList = commands.GetCommands();
+ foreach (Tuple<string, string> command in commandList)
+ {
+ var channelMsg = CreateSetMessage(i, command.Item1, command.Item2, lockKeyValue);
+ await stream.WriteAsync(channelMsg, 0, channelMsg.Length, cancellationToken).ConfigureAwait(false);
+ receivedBytes = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false);
+ // parse response to make sure it worked
+ if (!ParseReturnMessage(buffer, receivedBytes, out returnVal))
+ {
+ await ReleaseLockkey(_tcpClient, lockKeyValue).ConfigureAwait(false);
+ continue;
+ }
+ }
- var targetValue = string.Format("rtp://{0}:{1}", localIp, localPort);
- var targetMsg = CreateSetMessage(i, "target", targetValue, lockKeyValue);
+ var targetValue = string.Format("rtp://{0}:{1}", localIp, localPort);
+ var targetMsg = CreateSetMessage(i, "target", targetValue, lockKeyValue);
- await tcpClient.SendToAsync(targetMsg, 0, targetMsg.Length, ipEndPoint, cancellationToken).ConfigureAwait(false);
- response = await tcpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, cancellationToken).ConfigureAwait(false);
- // parse response to make sure it worked
- if (!ParseReturnMessage(response.Buffer, response.ReceivedBytes, out returnVal))
- {
- await ReleaseLockkey(tcpClient, lockKeyValue).ConfigureAwait(false);
- continue;
- }
+ await stream.WriteAsync(targetMsg, 0, targetMsg.Length, cancellationToken).ConfigureAwait(false);
+ receivedBytes = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false);
+ // parse response to make sure it worked
+ if (!ParseReturnMessage(buffer, receivedBytes, out returnVal))
+ {
+ await ReleaseLockkey(_tcpClient, lockKeyValue).ConfigureAwait(false);
+ continue;
+ }
- return;
+ return;
+ }
+ }
+ finally
+ {
+ ArrayPool<byte>.Shared.Return(buffer);
}
_activeTuner = -1;
@@ -207,53 +220,70 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
public async Task ChangeChannel(IHdHomerunChannelCommands commands, CancellationToken cancellationToken)
{
if (!_lockkey.HasValue)
+ {
return;
+ }
- using (var tcpClient = _socketFactory.CreateTcpSocket(_remoteIp, HdHomeRunPort))
+ using (var tcpClient = new TcpClient(_remoteEndPoint))
+ using (var stream = tcpClient.GetStream())
{
var commandList = commands.GetCommands();
- var receiveBuffer = new byte[8192];
-
- foreach (Tuple<string, string> command in commandList)
+ byte[] buffer = ArrayPool<byte>.Shared.Rent(8192);
+ try
{
- var channelMsg = CreateSetMessage(_activeTuner, command.Item1, command.Item2, _lockkey);
- await tcpClient.SendToAsync(channelMsg, 0, channelMsg.Length, new IPEndPoint(_remoteIp, HdHomeRunPort), cancellationToken).ConfigureAwait(false);
- var response = await tcpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, cancellationToken).ConfigureAwait(false);
- // parse response to make sure it worked
- if (!ParseReturnMessage(response.Buffer, response.ReceivedBytes, out string returnVal))
+ foreach (Tuple<string, string> command in commandList)
{
- return;
+ var channelMsg = CreateSetMessage(_activeTuner, command.Item1, command.Item2, _lockkey);
+ await stream.WriteAsync(channelMsg, 0, channelMsg.Length, cancellationToken).ConfigureAwait(false);
+ int receivedBytes = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false);
+ // parse response to make sure it worked
+ if (!ParseReturnMessage(buffer, receivedBytes, out string returnVal))
+ {
+ return;
+ }
}
}
+ finally
+ {
+ ArrayPool<byte>.Shared.Return(buffer);
+ }
}
}
- public Task StopStreaming(ISocket socket)
+ public Task StopStreaming(TcpClient client)
{
var lockKey = _lockkey;
if (!lockKey.HasValue)
+ {
return Task.CompletedTask;
+ }
- return ReleaseLockkey(socket, lockKey.Value);
+ return ReleaseLockkey(client, lockKey.Value);
}
- private async Task ReleaseLockkey(ISocket tcpClient, uint lockKeyValue)
+ private async Task ReleaseLockkey(TcpClient client, uint lockKeyValue)
{
_logger.LogInformation("HdHomerunManager.ReleaseLockkey {0}", lockKeyValue);
- var ipEndPoint = new IPEndPoint(_remoteIp, HdHomeRunPort);
+ var stream = client.GetStream();
var releaseTarget = CreateSetMessage(_activeTuner, "target", "none", lockKeyValue);
- await tcpClient.SendToAsync(releaseTarget, 0, releaseTarget.Length, ipEndPoint, CancellationToken.None).ConfigureAwait(false);
-
- var receiveBuffer = new byte[8192];
+ await stream.WriteAsync(releaseTarget, 0, releaseTarget.Length, CancellationToken.None).ConfigureAwait(false);
- await tcpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, CancellationToken.None).ConfigureAwait(false);
- var releaseKeyMsg = CreateSetMessage(_activeTuner, "lockkey", "none", lockKeyValue);
- _lockkey = null;
- await tcpClient.SendToAsync(releaseKeyMsg, 0, releaseKeyMsg.Length, ipEndPoint, CancellationToken.None).ConfigureAwait(false);
- await tcpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, CancellationToken.None).ConfigureAwait(false);
+ var buffer = ArrayPool<byte>.Shared.Rent(8192);
+ try
+ {
+ await stream.ReadAsync(buffer, 0, buffer.Length, CancellationToken.None).ConfigureAwait(false);
+ var releaseKeyMsg = CreateSetMessage(_activeTuner, "lockkey", "none", lockKeyValue);
+ _lockkey = null;
+ await stream.WriteAsync(releaseKeyMsg, 0, releaseKeyMsg.Length, CancellationToken.None).ConfigureAwait(false);
+ await stream.ReadAsync(buffer, 0, buffer.Length, CancellationToken.None).ConfigureAwait(false);
+ }
+ finally
+ {
+ ArrayPool<byte>.Shared.Return(buffer);
+ }
}
private static byte[] CreateGetMessage(int tuner, string name)
@@ -270,7 +300,10 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
// calculate crc and insert at the end of the message
var crcBytes = BitConverter.GetBytes(HdHomerunCrc.GetCrc32(message, messageLength - 4));
if (flipEndian)
+ {
Array.Reverse(crcBytes);
+ }
+
Buffer.BlockCopy(crcBytes, 0, message, offset, 4);
return message;
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
index ec708cf20..1d79a5f96 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
@@ -49,13 +49,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
EnableStreamSharing = true;
}
- private static Socket CreateSocket(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType)
- {
- var socket = new Socket(addressFamily, SocketType.Stream, ProtocolType.Tcp);
-
- return socket;
- }
-
public override async Task Open(CancellationToken openCancellationToken)
{
LiveStreamCancellationTokenSource.Token.ThrowIfCancellationRequested();
@@ -71,13 +64,13 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
var remoteAddress = IPAddress.Parse(uri.Host);
IPAddress localAddress = null;
- using (var tcpSocket = CreateSocket(remoteAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp))
+ using (var tcpClient = new TcpClient())
{
try
{
- tcpSocket.Connect(new IPEndPoint(remoteAddress, HdHomerunManager.HdHomeRunPort));
- localAddress = ((IPEndPoint)tcpSocket.LocalEndPoint).Address;
- tcpSocket.Close();
+ await tcpClient.ConnectAsync(remoteAddress, HdHomerunManager.HdHomeRunPort).ConfigureAwait(false);
+ localAddress = ((IPEndPoint)tcpClient.Client.RemoteEndPoint).Address;
+ tcpClient.Close();
}
catch (Exception ex)
{
@@ -87,7 +80,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
}
var udpClient = _socketFactory.CreateUdpSocket(localPort);
- var hdHomerunManager = new HdHomerunManager(_socketFactory, Logger);
+ var hdHomerunManager = new HdHomerunManager(Logger);
try
{
@@ -103,6 +96,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{
Logger.LogError(ex, "Error opening live stream:");
}
+
throw;
}
}
@@ -199,126 +193,5 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
}
}
}
-
- public class UdpClientStream : Stream
- {
- private static int RtpHeaderBytes = 12;
- private static int PacketSize = 1316;
- private readonly MediaBrowser.Model.Net.ISocket _udpClient;
- bool disposed;
-
- public UdpClientStream(MediaBrowser.Model.Net.ISocket udpClient) : base()
- {
- _udpClient = udpClient;
- }
-
- public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
- {
- if (buffer == null)
- throw new ArgumentNullException(nameof(buffer));
-
- if (offset + count < 0)
- throw new ArgumentOutOfRangeException(nameof(offset), "offset + count must not be negative");
-
- if (offset + count > buffer.Length)
- throw new ArgumentException("offset + count must not be greater than the length of buffer");
-
- if (disposed)
- throw new ObjectDisposedException(nameof(UdpClientStream));
-
- // This will always receive a 1328 packet size (PacketSize + RtpHeaderSize)
- // The RTP header will be stripped so see how many reads we need to make to fill the buffer.
- int numReads = count / PacketSize;
- int totalBytesRead = 0;
- byte[] receiveBuffer = new byte[81920];
-
- for (int i = 0; i < numReads; ++i)
- {
- var data = await _udpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, cancellationToken).ConfigureAwait(false);
-
- var bytesRead = data.ReceivedBytes - RtpHeaderBytes;
-
- // remove rtp header
- Buffer.BlockCopy(data.Buffer, RtpHeaderBytes, buffer, offset, bytesRead);
- offset += bytesRead;
- totalBytesRead += bytesRead;
- }
- return totalBytesRead;
- }
-
- public override int Read(byte[] buffer, int offset, int count)
- {
- if (buffer == null)
- throw new ArgumentNullException(nameof(buffer));
-
- if (offset + count < 0)
- throw new ArgumentOutOfRangeException("offset + count must not be negative", "offset+count");
-
- if (offset + count > buffer.Length)
- throw new ArgumentException("offset + count must not be greater than the length of buffer");
-
- if (disposed)
- throw new ObjectDisposedException(nameof(UdpClientStream));
-
- // This will always receive a 1328 packet size (PacketSize + RtpHeaderSize)
- // The RTP header will be stripped so see how many reads we need to make to fill the buffer.
- int numReads = count / PacketSize;
- int totalBytesRead = 0;
- byte[] receiveBuffer = new byte[81920];
-
- for (int i = 0; i < numReads; ++i)
- {
- var receivedBytes = _udpClient.Receive(receiveBuffer, 0, receiveBuffer.Length);
-
- var bytesRead = receivedBytes - RtpHeaderBytes;
-
- // remove rtp header
- Buffer.BlockCopy(receiveBuffer, RtpHeaderBytes, buffer, offset, bytesRead);
- offset += bytesRead;
- totalBytesRead += bytesRead;
- }
- return totalBytesRead;
- }
-
- protected override void Dispose(bool disposing)
- {
- disposed = true;
- }
-
- public override bool CanRead => throw new NotImplementedException();
-
- public override bool CanSeek => throw new NotImplementedException();
-
- public override bool CanWrite => throw new NotImplementedException();
-
- public override long Length => throw new NotImplementedException();
-
- public override long Position
- {
- get => throw new NotImplementedException();
-
- set => throw new NotImplementedException();
- }
-
- public override void Flush()
- {
- throw new NotImplementedException();
- }
-
- public override long Seek(long offset, SeekOrigin origin)
- {
- throw new NotImplementedException();
- }
-
- public override void SetLength(long value)
- {
- throw new NotImplementedException();
- }
-
- public override void Write(byte[] buffer, int offset, int count)
- {
- throw new NotImplementedException();
- }
- }
}
}
diff --git a/Emby.Server.Implementations/Net/SocketFactory.cs b/Emby.Server.Implementations/Net/SocketFactory.cs
index 42ffa4e22..cb53ce50c 100644
--- a/Emby.Server.Implementations/Net/SocketFactory.cs
+++ b/Emby.Server.Implementations/Net/SocketFactory.cs
@@ -2,54 +2,12 @@ using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
-using Emby.Server.Implementations.Networking;
using MediaBrowser.Model.Net;
namespace Emby.Server.Implementations.Net
{
public class SocketFactory : ISocketFactory
{
- // THIS IS A LINKED FILE - SHARED AMONGST MULTIPLE PLATFORMS
- // Be careful to check any changes compile and work for all platform projects it is shared in.
-
- // Not entirely happy with this. Would have liked to have done something more generic/reusable,
- // but that wasn't really the point so kept to YAGNI principal for now, even if the
- // interfaces are a bit ugly, specific and make assumptions.
-
- public ISocket CreateTcpSocket(IPAddress remoteAddress, int remotePort)
- {
- if (remotePort < 0)
- {
- throw new ArgumentException("remotePort cannot be less than zero.", nameof(remotePort));
- }
-
- var addressFamily = remoteAddress.AddressFamily == AddressFamily.InterNetwork
- ? AddressFamily.InterNetwork
- : AddressFamily.InterNetworkV6;
-
- var retVal = new Socket(addressFamily, System.Net.Sockets.SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp);
-
- try
- {
- retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
- }
- catch (SocketException)
- {
- // This is not supported on all operating systems (qnap)
- }
-
- try
- {
- return new UdpSocket(retVal, new IPEndPoint(remoteAddress, remotePort));
- }
- catch
- {
- retVal?.Dispose();
-
- throw;
- }
- }
-
/// <summary>
/// Creates a new UDP acceptSocket and binds it to the specified local port.
/// </summary>
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodingTempTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodingTempTask.cs
new file mode 100644
index 000000000..ad9b56535
--- /dev/null
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodingTempTask.cs
@@ -0,0 +1,156 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Tasks;
+using Microsoft.Extensions.Logging;
+
+namespace Emby.Server.Implementations.ScheduledTasks.Tasks
+{
+ /// <summary>
+ /// Deletes all transcoding temp files
+ /// </summary>
+ public class DeleteTranscodingTempTask : IScheduledTask, IConfigurableScheduledTask
+ {
+ /// <summary>
+ /// Gets or sets the application paths.
+ /// </summary>
+ /// <value>The application paths.</value>
+ protected ServerApplicationPaths ApplicationPaths { get; set; }
+
+
+ private readonly ILogger _logger;
+
+ private readonly IFileSystem _fileSystem;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DeleteTranscodingTempTask" /> class.
+ /// </summary>
+ public DeleteTranscodingTempTask(ServerApplicationPaths appPaths, ILogger logger, IFileSystem fileSystem)
+ {
+ ApplicationPaths = appPaths;
+ _logger = logger;
+ _fileSystem = fileSystem;
+ }
+
+ /// <summary>
+ /// Creates the triggers that define when the task will run
+ /// </summary>
+ /// <returns>IEnumerable{BaseTaskTrigger}.</returns>
+ public IEnumerable<TaskTriggerInfo> GetDefaultTriggers() => new List<TaskTriggerInfo>();
+
+ /// <summary>
+ /// Returns the task to be executed
+ /// </summary>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <param name="progress">The progress.</param>
+ /// <returns>Task.</returns>
+ public Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
+ {
+ var minDateModified = DateTime.UtcNow.AddDays(-1);
+ progress.Report(50);
+
+ try
+ {
+ DeleteTempFilesFromDirectory(cancellationToken, ApplicationPaths.TranscodingTempPath, minDateModified, progress);
+ }
+ catch (DirectoryNotFoundException)
+ {
+ // No biggie here. Nothing to delete
+ }
+
+ return Task.CompletedTask;
+ }
+
+
+ /// <summary>
+ /// Deletes the transcoded temp files from directory with a last write time less than a given date
+ /// </summary>
+ /// <param name="cancellationToken">The task cancellation token.</param>
+ /// <param name="directory">The directory.</param>
+ /// <param name="minDateModified">The min date modified.</param>
+ /// <param name="progress">The progress.</param>
+ private void DeleteTempFilesFromDirectory(CancellationToken cancellationToken, string directory, DateTime minDateModified, IProgress<double> progress)
+ {
+ var filesToDelete = _fileSystem.GetFiles(directory, true)
+ .Where(f => _fileSystem.GetLastWriteTimeUtc(f) < minDateModified)
+ .ToList();
+
+ var index = 0;
+
+ foreach (var file in filesToDelete)
+ {
+ double percent = index;
+ percent /= filesToDelete.Count;
+
+ progress.Report(100 * percent);
+
+ cancellationToken.ThrowIfCancellationRequested();
+
+ DeleteFile(file.FullName);
+
+ index++;
+ }
+
+ DeleteEmptyFolders(directory);
+
+ progress.Report(100);
+ }
+
+ private void DeleteEmptyFolders(string parent)
+ {
+ foreach (var directory in _fileSystem.GetDirectoryPaths(parent))
+ {
+ DeleteEmptyFolders(directory);
+ if (!_fileSystem.GetFileSystemEntryPaths(directory).Any())
+ {
+ try
+ {
+ Directory.Delete(directory, false);
+ }
+ catch (UnauthorizedAccessException ex)
+ {
+ _logger.LogError(ex, "Error deleting directory {path}", directory);
+ }
+ catch (IOException ex)
+ {
+ _logger.LogError(ex, "Error deleting directory {path}", directory);
+ }
+ }
+ }
+ }
+
+ private void DeleteFile(string path)
+ {
+ try
+ {
+ _fileSystem.DeleteFile(path);
+ }
+ catch (UnauthorizedAccessException ex)
+ {
+ _logger.LogError(ex, "Error deleting file {path}", path);
+ }
+ catch (IOException ex)
+ {
+ _logger.LogError(ex, "Error deleting file {path}", path);
+ }
+ }
+
+ public string Name => "Transcoding temp cleanup";
+
+ public string Description => "Deletes transcoding temp files older than 24 hours.";
+
+ public string Category => "Maintenance";
+
+ public string Key => "DeleteTranscodingTempFiles";
+
+ public bool IsHidden => false;
+
+ public bool IsEnabled => false;
+
+ public bool IsLogged => true;
+ }
+}
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs
index c6431c311..7afeba9dd 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs
@@ -1,4 +1,3 @@
-using MediaBrowser.Common;
using MediaBrowser.Common.Updates;
using MediaBrowser.Model.Net;
using System;
@@ -25,13 +24,10 @@ namespace Emby.Server.Implementations.ScheduledTasks
private readonly IInstallationManager _installationManager;
- private readonly IApplicationHost _appHost;
-
- public PluginUpdateTask(ILogger logger, IInstallationManager installationManager, IApplicationHost appHost)
+ public PluginUpdateTask(ILogger logger, IInstallationManager installationManager)
{
_logger = logger;
_installationManager = installationManager;
- _appHost = appHost;
}
/// <summary>
@@ -40,14 +36,11 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// <returns>IEnumerable{BaseTaskTrigger}.</returns>
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
- return new[] {
-
- // At startup
- new TaskTriggerInfo {Type = TaskTriggerInfo.TriggerStartup},
-
- // Every so often
- new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks}
- };
+ // At startup
+ yield return new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerStartup };
+
+ // Every so often
+ yield return new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks };
}
/// <summary>
@@ -72,7 +65,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
try
{
- await _installationManager.InstallPackage(package, true, new SimpleProgress<double>(), cancellationToken).ConfigureAwait(false);
+ await _installationManager.InstallPackage(package, cancellationToken).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
@@ -94,8 +87,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
// Update progress
lock (progress)
{
- numComplete++;
- progress.Report(90.0 * numComplete / packagesToInstall.Count + 10);
+ progress.Report((90.0 * ++numComplete / packagesToInstall.Count) + 10);
}
}
diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs
index 6833c20c3..2f84b91ec 100644
--- a/Emby.Server.Implementations/Updates/InstallationManager.cs
+++ b/Emby.Server.Implementations/Updates/InstallationManager.cs
@@ -3,13 +3,15 @@ using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Net.Http;
+using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common;
using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Common.Plugins;
-using MediaBrowser.Common.Progress;
using MediaBrowser.Common.Updates;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Model.Events;
@@ -33,7 +35,7 @@ namespace Emby.Server.Implementations.Updates
/// <summary>
/// The current installations
/// </summary>
- public List<Tuple<InstallationInfo, CancellationTokenSource>> CurrentInstallations { get; set; }
+ private List<(InstallationInfo info, CancellationTokenSource token)> _currentInstallations { get; set; }
/// <summary>
/// The completed installations
@@ -48,48 +50,14 @@ namespace Emby.Server.Implementations.Updates
public event EventHandler<GenericEventArgs<IPlugin>> PluginUninstalled;
/// <summary>
- /// Called when [plugin uninstalled].
- /// </summary>
- /// <param name="plugin">The plugin.</param>
- private void OnPluginUninstalled(IPlugin plugin)
- {
- PluginUninstalled?.Invoke(this, new GenericEventArgs<IPlugin> { Argument = plugin });
- }
-
- /// <summary>
/// Occurs when [plugin updated].
/// </summary>
- public event EventHandler<GenericEventArgs<Tuple<IPlugin, PackageVersionInfo>>> PluginUpdated;
- /// <summary>
- /// Called when [plugin updated].
- /// </summary>
- /// <param name="plugin">The plugin.</param>
- /// <param name="newVersion">The new version.</param>
- private void OnPluginUpdated(IPlugin plugin, PackageVersionInfo newVersion)
- {
- _logger.LogInformation("Plugin updated: {0} {1} {2}", newVersion.name, newVersion.versionStr ?? string.Empty, newVersion.classification);
-
- PluginUpdated?.Invoke(this, new GenericEventArgs<Tuple<IPlugin, PackageVersionInfo>> { Argument = new Tuple<IPlugin, PackageVersionInfo>(plugin, newVersion) });
-
- _applicationHost.NotifyPendingRestart();
- }
+ public event EventHandler<GenericEventArgs<(IPlugin, PackageVersionInfo)>> PluginUpdated;
/// <summary>
/// Occurs when [plugin updated].
/// </summary>
public event EventHandler<GenericEventArgs<PackageVersionInfo>> PluginInstalled;
- /// <summary>
- /// Called when [plugin installed].
- /// </summary>
- /// <param name="package">The package.</param>
- private void OnPluginInstalled(PackageVersionInfo package)
- {
- _logger.LogInformation("New plugin installed: {0} {1} {2}", package.name, package.versionStr ?? string.Empty, package.classification);
-
- PluginInstalled?.Invoke(this, new GenericEventArgs<PackageVersionInfo> { Argument = package });
-
- _applicationHost.NotifyPendingRestart();
- }
/// <summary>
/// The _logger
@@ -111,7 +79,7 @@ namespace Emby.Server.Implementations.Updates
private readonly IZipClient _zipClient;
public InstallationManager(
- ILoggerFactory loggerFactory,
+ ILogger<InstallationManager> logger,
IApplicationHost appHost,
IApplicationPaths appPaths,
IHttpClient httpClient,
@@ -120,15 +88,15 @@ namespace Emby.Server.Implementations.Updates
IFileSystem fileSystem,
IZipClient zipClient)
{
- if (loggerFactory == null)
+ if (logger == null)
{
- throw new ArgumentNullException(nameof(loggerFactory));
+ throw new ArgumentNullException(nameof(logger));
}
- CurrentInstallations = new List<Tuple<InstallationInfo, CancellationTokenSource>>();
+ _currentInstallations = new List<(InstallationInfo, CancellationTokenSource)>();
_completedInstallationsInternal = new ConcurrentBag<InstallationInfo>();
- _logger = loggerFactory.CreateLogger(nameof(InstallationManager));
+ _logger = logger;
_applicationHost = appHost;
_appPaths = appPaths;
_httpClient = httpClient;
@@ -138,21 +106,12 @@ namespace Emby.Server.Implementations.Updates
_zipClient = zipClient;
}
- private static Version GetPackageVersion(PackageVersionInfo version)
- {
- return new Version(ValueOrDefault(version.versionStr, "0.0.0.1"));
- }
-
- private static string ValueOrDefault(string str, string def)
- {
- return string.IsNullOrEmpty(str) ? def : str;
- }
-
/// <summary>
/// Gets all available packages.
/// </summary>
/// <returns>Task{List{PackageInfo}}.</returns>
- public async Task<List<PackageInfo>> GetAvailablePackages(CancellationToken cancellationToken,
+ public async Task<List<PackageInfo>> GetAvailablePackages(
+ CancellationToken cancellationToken,
bool withRegistration = true,
string packageType = null,
Version applicationVersion = null)
@@ -168,26 +127,21 @@ namespace Emby.Server.Implementations.Updates
/// <returns>Task{List{PackageInfo}}.</returns>
public async Task<List<PackageInfo>> GetAvailablePackagesWithoutRegistrationInfo(CancellationToken cancellationToken)
{
- using (var response = await _httpClient.SendAsync(new HttpRequestOptions
- {
- Url = "https://repo.jellyfin.org/releases/plugin/manifest.json",
- CancellationToken = cancellationToken,
- Progress = new SimpleProgress<double>(),
- CacheLength = GetCacheLength()
- }, "GET").ConfigureAwait(false))
- {
- using (var stream = response.Content)
+ using (var response = await _httpClient.SendAsync(
+ new HttpRequestOptions
{
- return FilterPackages(await _jsonSerializer.DeserializeFromStreamAsync<PackageInfo[]>(stream).ConfigureAwait(false));
- }
+ Url = "https://repo.jellyfin.org/releases/plugin/manifest.json",
+ CancellationToken = cancellationToken,
+ CacheMode = CacheMode.Unconditional,
+ CacheLength = GetCacheLength()
+ },
+ HttpMethod.Get).ConfigureAwait(false))
+ using (Stream stream = response.Content)
+ {
+ return FilterPackages(await _jsonSerializer.DeserializeFromStreamAsync<PackageInfo[]>(stream).ConfigureAwait(false));
}
}
- private PackageVersionClass GetSystemUpdateLevel()
- {
- return _applicationHost.SystemUpdateLevel;
- }
-
private static TimeSpan GetCacheLength()
{
return TimeSpan.FromMinutes(3);
@@ -211,7 +165,7 @@ namespace Emby.Server.Implementations.Updates
}
package.versions = versions
- .OrderByDescending(GetPackageVersion)
+ .OrderByDescending(x => x.Version)
.ToArray();
if (package.versions.Length == 0)
@@ -294,7 +248,7 @@ namespace Emby.Server.Implementations.Updates
return null;
}
- return package.versions.FirstOrDefault(v => GetPackageVersion(v).Equals(version) && v.classification == classification);
+ return package.versions.FirstOrDefault(v => v.Version == version && v.classification == classification);
}
/// <summary>
@@ -325,13 +279,8 @@ namespace Emby.Server.Implementations.Updates
var package = availablePackages.FirstOrDefault(p => string.Equals(p.guid, guid ?? "none", StringComparison.OrdinalIgnoreCase))
?? availablePackages.FirstOrDefault(p => p.name.Equals(name, StringComparison.OrdinalIgnoreCase));
- if (package == null)
- {
- return null;
- }
-
- return package.versions
- .OrderByDescending(GetPackageVersion)
+ return package?.versions
+ .OrderByDescending(x => x.Version)
.FirstOrDefault(v => v.classification <= classification && IsPackageVersionUpToDate(v, currentServerVersion));
}
@@ -346,40 +295,26 @@ namespace Emby.Server.Implementations.Updates
{
var catalog = await GetAvailablePackagesWithoutRegistrationInfo(cancellationToken).ConfigureAwait(false);
- var systemUpdateLevel = GetSystemUpdateLevel();
+ var systemUpdateLevel = _applicationHost.SystemUpdateLevel;
// Figure out what needs to be installed
return _applicationHost.Plugins.Select(p =>
{
var latestPluginInfo = GetLatestCompatibleVersion(catalog, p.Name, p.Id.ToString(), applicationVersion, systemUpdateLevel);
- return latestPluginInfo != null && GetPackageVersion(latestPluginInfo) > p.Version ? latestPluginInfo : null;
-
+ return latestPluginInfo != null && latestPluginInfo.Version > p.Version ? latestPluginInfo : null;
}).Where(i => i != null)
.Where(p => !string.IsNullOrEmpty(p.sourceUrl) && !CompletedInstallations.Any(i => string.Equals(i.AssemblyGuid, p.guid, StringComparison.OrdinalIgnoreCase)));
}
- /// <summary>
- /// Installs the package.
- /// </summary>
- /// <param name="package">The package.</param>
- /// <param name="isPlugin">if set to <c>true</c> [is plugin].</param>
- /// <param name="progress">The progress.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- /// <exception cref="ArgumentNullException">package</exception>
- public async Task InstallPackage(PackageVersionInfo package, bool isPlugin, IProgress<double> progress, CancellationToken cancellationToken)
+ /// <inheritdoc />
+ public async Task InstallPackage(PackageVersionInfo package, CancellationToken cancellationToken)
{
if (package == null)
{
throw new ArgumentNullException(nameof(package));
}
- if (progress == null)
- {
- throw new ArgumentNullException(nameof(progress));
- }
-
var installationInfo = new InstallationInfo
{
Id = Guid.NewGuid(),
@@ -391,24 +326,14 @@ namespace Emby.Server.Implementations.Updates
var innerCancellationTokenSource = new CancellationTokenSource();
- var tuple = new Tuple<InstallationInfo, CancellationTokenSource>(installationInfo, innerCancellationTokenSource);
+ var tuple = (installationInfo, innerCancellationTokenSource);
// Add it to the in-progress list
- lock (CurrentInstallations)
+ lock (_currentInstallations)
{
- CurrentInstallations.Add(tuple);
+ _currentInstallations.Add(tuple);
}
- var innerProgress = new ActionableProgress<double>();
-
- // Whenever the progress updates, update the outer progress object and InstallationInfo
- innerProgress.RegisterAction(percent =>
- {
- progress.Report(percent);
-
- installationInfo.PercentComplete = percent;
- });
-
var linkedToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, innerCancellationTokenSource.Token).Token;
var installationEventArgs = new InstallationEventArgs
@@ -421,11 +346,11 @@ namespace Emby.Server.Implementations.Updates
try
{
- await InstallPackageInternal(package, isPlugin, innerProgress, linkedToken).ConfigureAwait(false);
+ await InstallPackageInternal(package, linkedToken).ConfigureAwait(false);
- lock (CurrentInstallations)
+ lock (_currentInstallations)
{
- CurrentInstallations.Remove(tuple);
+ _currentInstallations.Remove(tuple);
}
_completedInstallationsInternal.Add(installationInfo);
@@ -434,9 +359,9 @@ namespace Emby.Server.Implementations.Updates
}
catch (OperationCanceledException)
{
- lock (CurrentInstallations)
+ lock (_currentInstallations)
{
- CurrentInstallations.Remove(tuple);
+ _currentInstallations.Remove(tuple);
}
_logger.LogInformation("Package installation cancelled: {0} {1}", package.name, package.versionStr);
@@ -449,9 +374,9 @@ namespace Emby.Server.Implementations.Updates
{
_logger.LogError(ex, "Package installation failed");
- lock (CurrentInstallations)
+ lock (_currentInstallations)
{
- CurrentInstallations.Remove(tuple);
+ _currentInstallations.Remove(tuple);
}
PackageInstallationFailed?.Invoke(this, new InstallationFailedEventArgs
@@ -473,110 +398,85 @@ namespace Emby.Server.Implementations.Updates
/// Installs the package internal.
/// </summary>
/// <param name="package">The package.</param>
- /// <param name="isPlugin">if set to <c>true</c> [is plugin].</param>
- /// <param name="progress">The progress.</param>
/// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- private async Task InstallPackageInternal(PackageVersionInfo package, bool isPlugin, IProgress<double> progress, CancellationToken cancellationToken)
+ /// <returns><see cref="Task" />.</returns>
+ private async Task InstallPackageInternal(PackageVersionInfo package, CancellationToken cancellationToken)
{
- IPlugin plugin = null;
-
- if (isPlugin)
- {
- // Set last update time if we were installed before
- plugin = _applicationHost.Plugins.FirstOrDefault(p => string.Equals(p.Id.ToString(), package.guid, StringComparison.OrdinalIgnoreCase))
+ // Set last update time if we were installed before
+ IPlugin plugin = _applicationHost.Plugins.FirstOrDefault(p => string.Equals(p.Id.ToString(), package.guid, StringComparison.OrdinalIgnoreCase))
?? _applicationHost.Plugins.FirstOrDefault(p => p.Name.Equals(package.name, StringComparison.OrdinalIgnoreCase));
- }
-
- string targetPath = plugin == null ? null : plugin.AssemblyFilePath;
// Do the install
- await PerformPackageInstallation(progress, targetPath, package, cancellationToken).ConfigureAwait(false);
+ await PerformPackageInstallation(package, cancellationToken).ConfigureAwait(false);
// Do plugin-specific processing
- if (isPlugin)
+ if (plugin == null)
{
- if (plugin == null)
- {
- OnPluginInstalled(package);
- }
- else
- {
- OnPluginUpdated(plugin, package);
- }
+ _logger.LogInformation("New plugin installed: {0} {1} {2}", package.name, package.versionStr ?? string.Empty, package.classification);
+
+ PluginInstalled?.Invoke(this, new GenericEventArgs<PackageVersionInfo>(package));
+ }
+ else
+ {
+ _logger.LogInformation("Plugin updated: {0} {1} {2}", package.name, package.versionStr ?? string.Empty, package.classification);
+
+ PluginUpdated?.Invoke(this, new GenericEventArgs<(IPlugin, PackageVersionInfo)>((plugin, package)));
}
+
+ _applicationHost.NotifyPendingRestart();
}
- private async Task PerformPackageInstallation(IProgress<double> progress, string target, PackageVersionInfo package, CancellationToken cancellationToken)
+ private async Task PerformPackageInstallation(PackageVersionInfo package, CancellationToken cancellationToken)
{
- // TODO: Remove the `string target` argument as it is not used any longer
-
var extension = Path.GetExtension(package.targetFilename);
- var isArchive = string.Equals(extension, ".zip", StringComparison.OrdinalIgnoreCase);
-
- if (!isArchive)
+ if (!string.Equals(extension, ".zip", StringComparison.OrdinalIgnoreCase))
{
_logger.LogError("Only zip packages are supported. {Filename} is not a zip archive.", package.targetFilename);
return;
}
// Always override the passed-in target (which is a file) and figure it out again
- target = Path.Combine(_appPaths.PluginsPath, package.name);
- _logger.LogDebug("Installing plugin to {Filename}.", target);
+ string targetDir = Path.Combine(_appPaths.PluginsPath, package.name);
- // Download to temporary file so that, if interrupted, it won't destroy the existing installation
- _logger.LogDebug("Downloading ZIP.");
- var tempFile = await _httpClient.GetTempFile(new HttpRequestOptions
- {
- Url = package.sourceUrl,
- CancellationToken = cancellationToken,
- Progress = progress
-
- }).ConfigureAwait(false);
-
- cancellationToken.ThrowIfCancellationRequested();
-
- // TODO: Validate with a checksum, *properly*
-
- // Check if the target directory already exists, and remove it if so
- if (Directory.Exists(target))
- {
- _logger.LogDebug("Deleting existing plugin at {Filename}.", target);
- Directory.Delete(target, true);
- }
+// CA5351: Do Not Use Broken Cryptographic Algorithms
+#pragma warning disable CA5351
+ using (var res = await _httpClient.SendAsync(
+ new HttpRequestOptions
+ {
+ Url = package.sourceUrl,
+ CancellationToken = cancellationToken,
+ // We need it to be buffered for setting the position
+ BufferContent = true
+ },
+ HttpMethod.Get).ConfigureAwait(false))
+ using (var stream = res.Content)
+ using (var md5 = MD5.Create())
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ var hash = HexHelper.ToHexString(md5.ComputeHash(stream));
+ if (!string.Equals(package.checksum, hash, StringComparison.OrdinalIgnoreCase))
+ {
+ _logger.LogDebug("{0}, {1}", package.checksum, hash);
+ throw new InvalidDataException($"The checksums didn't match while installing {package.name}.");
+ }
- // Success - move it to the real target
- try
- {
- _logger.LogDebug("Extracting ZIP {TempFile} to {Filename}.", tempFile, target);
- using (var stream = File.OpenRead(tempFile))
+ if (Directory.Exists(targetDir))
{
- _zipClient.ExtractAllFromZip(stream, target, true);
+ Directory.Delete(targetDir);
}
- }
- catch (IOException ex)
- {
- _logger.LogError(ex, "Error attempting to extract {TempFile} to {TargetFile}", tempFile, target);
- throw;
- }
- try
- {
- _logger.LogDebug("Deleting temporary file {Filename}.", tempFile);
- _fileSystem.DeleteFile(tempFile);
- }
- catch (IOException ex)
- {
- // Don't fail because of this
- _logger.LogError(ex, "Error deleting temp file {TempFile}", tempFile);
+ stream.Position = 0;
+ _zipClient.ExtractAllFromZip(stream, targetDir, true);
}
+
+#pragma warning restore CA5351
}
/// <summary>
/// Uninstalls a plugin
/// </summary>
/// <param name="plugin">The plugin.</param>
- /// <exception cref="ArgumentException"></exception>
public void UninstallPlugin(IPlugin plugin)
{
plugin.OnUninstalling();
@@ -622,11 +522,34 @@ namespace Emby.Server.Implementations.Updates
_config.SaveConfiguration();
}
- OnPluginUninstalled(plugin);
+ PluginUninstalled?.Invoke(this, new GenericEventArgs<IPlugin> { Argument = plugin });
_applicationHost.NotifyPendingRestart();
}
+ /// <inheritdoc/>
+ public bool CancelInstallation(Guid id)
+ {
+ lock (_currentInstallations)
+ {
+ var install = _currentInstallations.Find(x => x.Item1.Id == id);
+ if (install == default((InstallationInfo, CancellationTokenSource)))
+ {
+ return false;
+ }
+
+ install.Item2.Cancel();
+ _currentInstallations.Remove(install);
+ return true;
+ }
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
@@ -635,21 +558,16 @@ namespace Emby.Server.Implementations.Updates
{
if (dispose)
{
- lock (CurrentInstallations)
+ lock (_currentInstallations)
{
- foreach (var tuple in CurrentInstallations)
+ foreach (var tuple in _currentInstallations)
{
tuple.Item2.Dispose();
}
- CurrentInstallations.Clear();
+ _currentInstallations.Clear();
}
}
}
-
- public void Dispose()
- {
- Dispose(true);
- }
}
}
diff --git a/Emby.XmlTv/Emby.XmlTv/Emby.XmlTv.csproj b/Emby.XmlTv/Emby.XmlTv/Emby.XmlTv.csproj
index 0225be2c2..04f558173 100644
--- a/Emby.XmlTv/Emby.XmlTv/Emby.XmlTv.csproj
+++ b/Emby.XmlTv/Emby.XmlTv/Emby.XmlTv.csproj
@@ -3,6 +3,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+ <GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
<ItemGroup>
diff --git a/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj b/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj
index f023bc55d..396bdd4b7 100644
--- a/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj
+++ b/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj
@@ -3,6 +3,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+ <GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
<ItemGroup>
diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs
index b9b0cc382..8b4b61e29 100644
--- a/Jellyfin.Server/CoreAppHost.cs
+++ b/Jellyfin.Server/CoreAppHost.cs
@@ -9,8 +9,21 @@ using Microsoft.Extensions.Logging;
namespace Jellyfin.Server
{
+ /// <summary>
+ /// Implementation of the abstract <see cref="ApplicationHost" /> class.
+ /// </summary>
public class CoreAppHost : ApplicationHost
{
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CoreAppHost" /> class.
+ /// </summary>
+ /// <param name="applicationPaths">The <see cref="ServerApplicationPaths" /> to be used by the <see cref="CoreAppHost" />.</param>
+ /// <param name="loggerFactory">The <see cref="ILoggerFactory" /> to be used by the <see cref="CoreAppHost" />.</param>
+ /// <param name="options">The <see cref="StartupOptions" /> to be used by the <see cref="CoreAppHost" />.</param>
+ /// <param name="fileSystem">The <see cref="IFileSystem" /> to be used by the <see cref="CoreAppHost" />.</param>
+ /// <param name="imageEncoder">The <see cref="IImageEncoder" /> to be used by the <see cref="CoreAppHost" />.</param>
+ /// <param name="networkManager">The <see cref="INetworkManager" /> to be used by the <see cref="CoreAppHost" />.</param>
+ /// <param name="configuration">The <see cref="IConfiguration" /> to be used by the <see cref="CoreAppHost" />.</param>
public CoreAppHost(
ServerApplicationPaths applicationPaths,
ILoggerFactory loggerFactory,
@@ -30,15 +43,19 @@ namespace Jellyfin.Server
{
}
+ /// <inheritdoc />
public override bool CanSelfRestart => StartupOptions.RestartPath != null;
+ /// <inheritdoc />
protected override void RestartInternal() => Program.Restart();
+ /// <inheritdoc />
protected override IEnumerable<Assembly> GetAssembliesWithPartsInternal()
{
yield return typeof(CoreAppHost).Assembly;
}
+ /// <inheritdoc />
protected override void ShutdownInternal() => Program.Shutdown();
}
}
diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj
index 641b3f182..ec7c026e5 100644
--- a/Jellyfin.Server/Jellyfin.Server.csproj
+++ b/Jellyfin.Server/Jellyfin.Server.csproj
@@ -9,10 +9,8 @@
</PropertyGroup>
<PropertyGroup>
- <!-- We need C# 7.1 for async main-->
+ <!-- We need at least C# 7.1 for async main-->
<LangVersion>latest</LangVersion>
- <!-- Disable documentation warnings (for now) -->
- <NoWarn>SA1600;SA1601;SA1629;CS1591</NoWarn>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
@@ -26,7 +24,7 @@
<!-- Code analysers-->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
- <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.3" />
+ <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.4" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" />
<PackageReference Include="SerilogAnalyzer" Version="0.15.0" />
</ItemGroup>
@@ -36,7 +34,7 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="CommandLineParser" Version="2.5.0" />
+ <PackageReference Include="CommandLineParser" Version="2.6.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.2.4" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.2.0" />
<PackageReference Include="Serilog.AspNetCore" Version="2.1.1" />
diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs
index 952990493..5e4e36a34 100644
--- a/Jellyfin.Server/Program.cs
+++ b/Jellyfin.Server/Program.cs
@@ -28,6 +28,9 @@ using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace Jellyfin.Server
{
+ /// <summary>
+ /// Class containing the entry point of the application.
+ /// </summary>
public static class Program
{
private static readonly CancellationTokenSource _tokenSource = new CancellationTokenSource();
@@ -35,6 +38,11 @@ namespace Jellyfin.Server
private static ILogger _logger;
private static bool _restartOnShutdown;
+ /// <summary>
+ /// The entry point of the application.
+ /// </summary>
+ /// <param name="args">The command line arguments passed.</param>
+ /// <returns><see cref="Task" />.</returns>
public static Task Main(string[] args)
{
// For backwards compatibility.
@@ -53,7 +61,10 @@ namespace Jellyfin.Server
.MapResult(StartApp, _ => Task.CompletedTask);
}
- public static void Shutdown()
+ /// <summary>
+ /// Shuts down the application.
+ /// </summary>
+ internal static void Shutdown()
{
if (!_tokenSource.IsCancellationRequested)
{
@@ -61,7 +72,10 @@ namespace Jellyfin.Server
}
}
- public static void Restart()
+ /// <summary>
+ /// Restarts the application.
+ /// </summary>
+ internal static void Restart()
{
_restartOnShutdown = true;
@@ -171,11 +185,12 @@ namespace Jellyfin.Server
/// <summary>
/// Create the data, config and log paths from the variety of inputs(command line args,
/// environment variables) or decide on what default to use. For Windows it's %AppPath%
- /// for everything else the XDG approach is followed:
- /// https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
+ /// for everything else the
+ /// <a href="https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html">XDG approach</a>
+ /// is followed.
/// </summary>
- /// <param name="options">StartupOptions</param>
- /// <returns>ServerApplicationPaths</returns>
+ /// <param name="options">The <see cref="StartupOptions" /> for this instance.</param>
+ /// <returns><see cref="ServerApplicationPaths" />.</returns>
private static ServerApplicationPaths CreateApplicationPaths(StartupOptions options)
{
// dataDir
diff --git a/Jellyfin.Server/StartupOptions.cs b/Jellyfin.Server/StartupOptions.cs
index 8296d414e..bb0adaf63 100644
--- a/Jellyfin.Server/StartupOptions.cs
+++ b/Jellyfin.Server/StartupOptions.cs
@@ -8,36 +8,62 @@ namespace Jellyfin.Server
/// </summary>
public class StartupOptions : IStartupOptions
{
+ /// <summary>
+ /// Gets or sets the path to the data directory.
+ /// </summary>
+ /// <value>The path to the data directory.</value>
[Option('d', "datadir", Required = false, HelpText = "Path to use for the data folder (database files, etc.).")]
public string DataDir { get; set; }
+ /// <summary>
+ /// Gets or sets the path to the web directory.
+ /// </summary>
+ /// <value>The path to the web directory.</value>
[Option('w', "webdir", Required = false, HelpText = "Path to the Jellyfin web UI resources.")]
public string WebDir { get; set; }
+ /// <summary>
+ /// Gets or sets the path to the cache directory.
+ /// </summary>
+ /// <value>The path to the cache directory.</value>
[Option('C', "cachedir", Required = false, HelpText = "Path to use for caching.")]
public string CacheDir { get; set; }
+ /// <summary>
+ /// Gets or sets the path to the config directory.
+ /// </summary>
+ /// <value>The path to the config directory.</value>
[Option('c', "configdir", Required = false, HelpText = "Path to use for configuration data (user settings and pictures).")]
public string ConfigDir { get; set; }
+ /// <summary>
+ /// Gets or sets the path to the log directory.
+ /// </summary>
+ /// <value>The path to the log directory.</value>
[Option('l', "logdir", Required = false, HelpText = "Path to use for writing log files.")]
public string LogDir { get; set; }
+ /// <inheritdoc />
[Option("ffmpeg", Required = false, HelpText = "Path to external FFmpeg executable to use in place of default found in PATH.")]
public string FFmpegPath { get; set; }
+ /// <inheritdoc />
[Option("service", Required = false, HelpText = "Run as headless service.")]
public bool IsService { get; set; }
+ /// <inheritdoc />
[Option("noautorunwebapp", Required = false, HelpText = "Run headless if startup wizard is complete.")]
public bool NoAutoRunWebApp { get; set; }
+ /// <inheritdoc />
[Option("package-name", Required = false, HelpText = "Used when packaging Jellyfin (example, synology).")]
public string PackageName { get; set; }
+ /// <inheritdoc />
[Option("restartpath", Required = false, HelpText = "Path to restart script.")]
public string RestartPath { get; set; }
+ /// <inheritdoc />
[Option("restartargs", Required = false, HelpText = "Arguments for restart script.")]
public string RestartArgs { get; set; }
}
diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs
index 23c7339d2..6d3037b24 100644
--- a/MediaBrowser.Api/Images/ImageService.cs
+++ b/MediaBrowser.Api/Images/ImageService.cs
@@ -550,14 +550,14 @@ namespace MediaBrowser.Api.Images
}
IImageEnhancer[] supportedImageEnhancers;
- if (_imageProcessor.ImageEnhancers.Length > 0)
+ if (_imageProcessor.ImageEnhancers.Count > 0)
{
if (item == null)
{
item = _libraryManager.GetItemById(itemId);
}
- supportedImageEnhancers = request.EnableImageEnhancers ? _imageProcessor.GetSupportedEnhancers(item, request.Type) : Array.Empty<IImageEnhancer>();
+ supportedImageEnhancers = request.EnableImageEnhancers ? _imageProcessor.GetSupportedEnhancers(item, request.Type).ToArray() : Array.Empty<IImageEnhancer>();
}
else
{
@@ -606,8 +606,8 @@ namespace MediaBrowser.Api.Images
ImageRequest request,
ItemImageInfo image,
bool cropwhitespace,
- ImageFormat[] supportedFormats,
- IImageEnhancer[] enhancers,
+ IReadOnlyCollection<ImageFormat> supportedFormats,
+ IReadOnlyCollection<IImageEnhancer> enhancers,
TimeSpan? cacheDuration,
IDictionary<string, string> headers,
bool isHeadRequest)
diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj
index ba29c656b..f653270a6 100644
--- a/MediaBrowser.Api/MediaBrowser.Api.csproj
+++ b/MediaBrowser.Api/MediaBrowser.Api.csproj
@@ -12,6 +12,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+ <GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
</Project>
diff --git a/MediaBrowser.Api/PackageService.cs b/MediaBrowser.Api/PackageService.cs
index fbb876dea..baa6f7bb9 100644
--- a/MediaBrowser.Api/PackageService.cs
+++ b/MediaBrowser.Api/PackageService.cs
@@ -197,7 +197,7 @@ namespace MediaBrowser.Api
throw new ResourceNotFoundException(string.Format("Package not found: {0}", request.Name));
}
- await _installationManager.InstallPackage(package, true, new SimpleProgress<double>(), CancellationToken.None);
+ await _installationManager.InstallPackage(package);
}
/// <summary>
@@ -206,13 +206,7 @@ namespace MediaBrowser.Api
/// <param name="request">The request.</param>
public void Delete(CancelPackageInstallation request)
{
- var info = _installationManager.CurrentInstallations.FirstOrDefault(i => i.Item1.Id.Equals(request.Id));
-
- if (info != null)
- {
- info.Item2.Cancel();
- }
+ _installationManager.CancelInstallation(new Guid(request.Id));
}
}
-
}
diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs
index 77b733dc9..1f78f5afc 100644
--- a/MediaBrowser.Api/Playback/BaseStreamingService.cs
+++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs
@@ -690,8 +690,8 @@ namespace MediaBrowser.Api.Playback
request.AudioCodec = EncodingHelper.InferAudioCodec(url);
}
- var enableDlnaHeaders = !string.IsNullOrWhiteSpace(request.Params) /*||
- string.Equals(Request.Headers.Get("GetContentFeatures.DLNA.ORG"), "1", StringComparison.OrdinalIgnoreCase)*/;
+ var enableDlnaHeaders = !string.IsNullOrWhiteSpace(request.Params) ||
+ string.Equals(GetHeader("GetContentFeatures.DLNA.ORG"), "1", StringComparison.OrdinalIgnoreCase);
var state = new StreamState(MediaSourceManager, TranscodingJobType)
{
@@ -1016,11 +1016,6 @@ namespace MediaBrowser.Api.Playback
).FirstOrDefault() ?? string.Empty;
}
-
- foreach (var item in responseHeaders)
- {
- Request.Response.Headers.Add(item.Key, item.Value);
- }
}
private void AddTimeSeekResponseHeaders(StreamState state, IDictionary<string, string> responseHeaders)
diff --git a/MediaBrowser.Common/Extensions/HexHelper.cs b/MediaBrowser.Common/Extensions/HexHelper.cs
new file mode 100644
index 000000000..3d80d94ac
--- /dev/null
+++ b/MediaBrowser.Common/Extensions/HexHelper.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Globalization;
+
+namespace MediaBrowser.Common.Extensions
+{
+ public static class HexHelper
+ {
+ public static byte[] FromHexString(string str)
+ {
+ byte[] bytes = new byte[str.Length / 2];
+ for (int i = 0; i < str.Length; i += 2)
+ {
+ bytes[i / 2] = byte.Parse(str.Substring(i, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
+ }
+
+ return bytes;
+ }
+
+ public static string ToHexString(byte[] bytes)
+ => BitConverter.ToString(bytes).Replace("-", "");
+ }
+}
diff --git a/MediaBrowser.Common/IApplicationHost.cs b/MediaBrowser.Common/IApplicationHost.cs
index cb7343440..2248e9c85 100644
--- a/MediaBrowser.Common/IApplicationHost.cs
+++ b/MediaBrowser.Common/IApplicationHost.cs
@@ -75,10 +75,10 @@ namespace MediaBrowser.Common
/// <summary>
/// Gets the exports.
/// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="manageLiftime">if set to <c>true</c> [manage liftime].</param>
- /// <returns>IEnumerable{``0}.</returns>
- IEnumerable<T> GetExports<T>(bool manageLifetime = true);
+ /// <typeparam name="T">The type.</typeparam>
+ /// <param name="manageLifetime">If set to <c>true</c> [manage lifetime].</param>
+ /// <returns><see cref="IReadOnlyCollection{T}" />.</returns>
+ IReadOnlyCollection<T> GetExports<T>(bool manageLifetime = true);
/// <summary>
/// Resolves this instance.
diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj
index 05b48a2a1..91ab066f9 100644
--- a/MediaBrowser.Common/MediaBrowser.Common.csproj
+++ b/MediaBrowser.Common/MediaBrowser.Common.csproj
@@ -23,6 +23,12 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+ <GenerateDocumentationFile>true</GenerateDocumentationFile>
+ </PropertyGroup>
+
+ <PropertyGroup>
+ <!-- We need at least C# 7.1 for the "default literal" feature-->
+ <LangVersion>latest</LangVersion>
</PropertyGroup>
</Project>
diff --git a/MediaBrowser.Common/Net/HttpRequestOptions.cs b/MediaBrowser.Common/Net/HttpRequestOptions.cs
index 76bd35e57..94b972a02 100644
--- a/MediaBrowser.Common/Net/HttpRequestOptions.cs
+++ b/MediaBrowser.Common/Net/HttpRequestOptions.cs
@@ -64,12 +64,6 @@ namespace MediaBrowser.Common.Net
set => RequestHeaders[HeaderNames.Host] = value;
}
- /// <summary>
- /// Gets or sets the progress.
- /// </summary>
- /// <value>The progress.</value>
- public IProgress<double> Progress { get; set; }
-
public Dictionary<string, string> RequestHeaders { get; private set; }
public string RequestContentType { get; set; }
@@ -79,10 +73,6 @@ namespace MediaBrowser.Common.Net
public bool BufferContent { get; set; }
- public bool LogRequest { get; set; }
- public bool LogRequestAsDebug { get; set; }
- public bool LogErrors { get; set; }
-
public bool LogErrorResponseBody { get; set; }
public bool EnableKeepAlive { get; set; }
@@ -105,8 +95,6 @@ namespace MediaBrowser.Common.Net
{
RequestHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- LogRequest = true;
- LogErrors = true;
CacheMode = CacheMode.None;
DecompressionMethod = CompressionMethod.Deflate;
}
diff --git a/MediaBrowser.Common/Net/IHttpClient.cs b/MediaBrowser.Common/Net/IHttpClient.cs
index db69c6f2c..d84a4d664 100644
--- a/MediaBrowser.Common/Net/IHttpClient.cs
+++ b/MediaBrowser.Common/Net/IHttpClient.cs
@@ -47,21 +47,5 @@ namespace MediaBrowser.Common.Net
/// <param name="options">The options.</param>
/// <returns>Task{HttpResponseInfo}.</returns>
Task<HttpResponseInfo> Post(HttpRequestOptions options);
-
- /// <summary>
- /// Downloads the contents of a given url into a temporary location
- /// </summary>
- /// <param name="options">The options.</param>
- /// <returns>Task{System.String}.</returns>
- /// <exception cref="System.ArgumentNullException">progress</exception>
- /// <exception cref="Model.Net.HttpException"></exception>
- Task<string> GetTempFile(HttpRequestOptions options);
-
- /// <summary>
- /// Gets the temporary file response.
- /// </summary>
- /// <param name="options">The options.</param>
- /// <returns>Task{HttpResponseInfo}.</returns>
- Task<HttpResponseInfo> GetTempFileResponse(HttpRequestOptions options);
}
}
diff --git a/MediaBrowser.Common/Updates/IInstallationManager.cs b/MediaBrowser.Common/Updates/IInstallationManager.cs
index a263be35f..88ac7e473 100644
--- a/MediaBrowser.Common/Updates/IInstallationManager.cs
+++ b/MediaBrowser.Common/Updates/IInstallationManager.cs
@@ -16,11 +16,6 @@ namespace MediaBrowser.Common.Updates
event EventHandler<InstallationEventArgs> PackageInstallationCancelled;
/// <summary>
- /// The current installations
- /// </summary>
- List<Tuple<InstallationInfo, CancellationTokenSource>> CurrentInstallations { get; set; }
-
- /// <summary>
/// The completed installations
/// </summary>
IEnumerable<InstallationInfo> CompletedInstallations { get; }
@@ -33,7 +28,7 @@ namespace MediaBrowser.Common.Updates
/// <summary>
/// Occurs when [plugin updated].
/// </summary>
- event EventHandler<GenericEventArgs<Tuple<IPlugin, PackageVersionInfo>>> PluginUpdated;
+ event EventHandler<GenericEventArgs<(IPlugin, PackageVersionInfo)>> PluginUpdated;
/// <summary>
/// Occurs when [plugin updated].
@@ -102,12 +97,9 @@ namespace MediaBrowser.Common.Updates
/// Installs the package.
/// </summary>
/// <param name="package">The package.</param>
- /// <param name="isPlugin">if set to <c>true</c> [is plugin].</param>
- /// <param name="progress">The progress.</param>
/// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- /// <exception cref="ArgumentNullException">package</exception>
- Task InstallPackage(PackageVersionInfo package, bool isPlugin, IProgress<double> progress, CancellationToken cancellationToken);
+ /// <returns><see cref="Task" />.</returns>
+ Task InstallPackage(PackageVersionInfo package, CancellationToken cancellationToken = default);
/// <summary>
/// Uninstalls a plugin
@@ -115,5 +107,12 @@ namespace MediaBrowser.Common.Updates
/// <param name="plugin">The plugin.</param>
/// <exception cref="ArgumentException"></exception>
void UninstallPlugin(IPlugin plugin);
+
+ /// <summary>
+ /// Cancels the installation
+ /// </summary>
+ /// <param name="id">The id of the package that is being installed</param>
+ /// <returns>Returns true if the install was cancelled</returns>
+ bool CancelInstallation(Guid id);
}
}
diff --git a/MediaBrowser.Controller/Drawing/IImageEncoder.cs b/MediaBrowser.Controller/Drawing/IImageEncoder.cs
index 4eaecd0a0..a0f9ae46e 100644
--- a/MediaBrowser.Controller/Drawing/IImageEncoder.cs
+++ b/MediaBrowser.Controller/Drawing/IImageEncoder.cs
@@ -18,16 +18,6 @@ namespace MediaBrowser.Controller.Drawing
IReadOnlyCollection<ImageFormat> SupportedOutputFormats { get; }
/// <summary>
- /// Encodes the image.
- /// </summary>
- string EncodeImage(string inputPath, DateTime dateModified, string outputPath, bool autoOrient, ImageOrientation? orientation, int quality, ImageProcessingOptions options, ImageFormat outputFormat);
-
- /// <summary>
- /// Creates the image collage.
- /// </summary>
- /// <param name="options">The options.</param>
- void CreateImageCollage(ImageCollageOptions options);
- /// <summary>
/// Gets the name.
/// </summary>
/// <value>The name.</value>
@@ -46,5 +36,16 @@ namespace MediaBrowser.Controller.Drawing
bool SupportsImageEncoding { get; }
ImageDimensions GetImageSize(string path);
+
+ /// <summary>
+ /// Encodes the image.
+ /// </summary>
+ string EncodeImage(string inputPath, DateTime dateModified, string outputPath, bool autoOrient, ImageOrientation? orientation, int quality, ImageProcessingOptions options, ImageFormat outputFormat);
+
+ /// <summary>
+ /// Creates the image collage.
+ /// </summary>
+ /// <param name="options">The options.</param>
+ void CreateImageCollage(ImageCollageOptions options);
}
}
diff --git a/MediaBrowser.Controller/Drawing/IImageProcessor.cs b/MediaBrowser.Controller/Drawing/IImageProcessor.cs
index a11e2186f..a58a11bd1 100644
--- a/MediaBrowser.Controller/Drawing/IImageProcessor.cs
+++ b/MediaBrowser.Controller/Drawing/IImageProcessor.cs
@@ -24,7 +24,15 @@ namespace MediaBrowser.Controller.Drawing
/// Gets the image enhancers.
/// </summary>
/// <value>The image enhancers.</value>
- IImageEnhancer[] ImageEnhancers { get; }
+ IReadOnlyCollection<IImageEnhancer> ImageEnhancers { get; set; }
+
+ /// <summary>
+ /// Gets a value indicating whether [supports image collage creation].
+ /// </summary>
+ /// <value><c>true</c> if [supports image collage creation]; otherwise, <c>false</c>.</value>
+ bool SupportsImageCollageCreation { get; }
+
+ IImageEncoder ImageEncoder { get; set; }
/// <summary>
/// Gets the dimensions of the image.
@@ -51,18 +59,12 @@ namespace MediaBrowser.Controller.Drawing
ImageDimensions GetImageDimensions(BaseItem item, ItemImageInfo info, bool updateItem);
/// <summary>
- /// Adds the parts.
- /// </summary>
- /// <param name="enhancers">The enhancers.</param>
- void AddParts(IEnumerable<IImageEnhancer> enhancers);
-
- /// <summary>
/// Gets the supported enhancers.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="imageType">Type of the image.</param>
/// <returns>IEnumerable{IImageEnhancer}.</returns>
- IImageEnhancer[] GetSupportedEnhancers(BaseItem item, ImageType imageType);
+ IEnumerable<IImageEnhancer> GetSupportedEnhancers(BaseItem item, ImageType imageType);
/// <summary>
/// Gets the image cache tag.
@@ -80,7 +82,7 @@ namespace MediaBrowser.Controller.Drawing
/// <param name="image">The image.</param>
/// <param name="imageEnhancers">The image enhancers.</param>
/// <returns>Guid.</returns>
- string GetImageCacheTag(BaseItem item, ItemImageInfo image, IImageEnhancer[] imageEnhancers);
+ string GetImageCacheTag(BaseItem item, ItemImageInfo image, IReadOnlyCollection<IImageEnhancer> imageEnhancers);
/// <summary>
/// Processes the image.
@@ -109,7 +111,7 @@ namespace MediaBrowser.Controller.Drawing
/// <summary>
/// Gets the supported image output formats.
/// </summary>
- /// <returns>IReadOnlyCollection{ImageOutput}.</returns>
+ /// <returns><see cref="IReadOnlyCollection{ImageOutput}" />.</returns>
IReadOnlyCollection<ImageFormat> GetSupportedImageOutputFormats();
/// <summary>
@@ -118,14 +120,6 @@ namespace MediaBrowser.Controller.Drawing
/// <param name="options">The options.</param>
void CreateImageCollage(ImageCollageOptions options);
- /// <summary>
- /// Gets a value indicating whether [supports image collage creation].
- /// </summary>
- /// <value><c>true</c> if [supports image collage creation]; otherwise, <c>false</c>.</value>
- bool SupportsImageCollageCreation { get; }
-
- IImageEncoder ImageEncoder { get; set; }
-
bool SupportsTransparency(string path);
}
}
diff --git a/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs b/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs
index db432f500..29addf6e6 100644
--- a/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs
+++ b/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.IO;
using System.Linq;
using MediaBrowser.Controller.Entities;
@@ -33,9 +34,9 @@ namespace MediaBrowser.Controller.Drawing
public int Quality { get; set; }
- public IImageEnhancer[] Enhancers { get; set; }
+ public IReadOnlyCollection<IImageEnhancer> Enhancers { get; set; }
- public ImageFormat[] SupportedOutputFormats { get; set; }
+ public IReadOnlyCollection<ImageFormat> SupportedOutputFormats { get; set; }
public bool AddPlayedIndicator { get; set; }
diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
index 01893f1b5..c6bca2518 100644
--- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj
+++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
@@ -19,6 +19,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+ <GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
</Project>
diff --git a/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj b/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj
index 867b82ede..a8f8da9b8 100644
--- a/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj
+++ b/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj
@@ -12,6 +12,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+ <GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
</Project>
diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj
index c0f92ac4a..fdb20477f 100644
--- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj
+++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj
@@ -3,6 +3,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+ <GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
<ItemGroup>
@@ -18,7 +19,7 @@
<ItemGroup>
<PackageReference Include="System.Text.Encoding.CodePages" Version="4.5.1" />
- <PackageReference Include="UTF.Unknown" Version="1.0.0" />
+ <PackageReference Include="UTF.Unknown" Version="2.0.0" />
</ItemGroup>
</Project>
diff --git a/MediaBrowser.Model/Cryptography/PasswordHash.cs b/MediaBrowser.Model/Cryptography/PasswordHash.cs
index df32fdb00..4bcf0c117 100644
--- a/MediaBrowser.Model/Cryptography/PasswordHash.cs
+++ b/MediaBrowser.Model/Cryptography/PasswordHash.cs
@@ -84,6 +84,7 @@ namespace MediaBrowser.Model.Cryptography
_hash = Array.Empty<Byte>();
}
+ // TODO: move this class and use the HexHelper class
public static byte[] ConvertFromByteString(string byteString)
{
byte[] bytes = new byte[byteString.Length / 2];
diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj
index 3de2cca2d..e9f43ea56 100644
--- a/MediaBrowser.Model/MediaBrowser.Model.csproj
+++ b/MediaBrowser.Model/MediaBrowser.Model.csproj
@@ -10,6 +10,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+ <GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
<ItemGroup>
diff --git a/MediaBrowser.Model/Net/ISocketFactory.cs b/MediaBrowser.Model/Net/ISocketFactory.cs
index e58f4cc14..2f857f1af 100644
--- a/MediaBrowser.Model/Net/ISocketFactory.cs
+++ b/MediaBrowser.Model/Net/ISocketFactory.cs
@@ -17,8 +17,6 @@ namespace MediaBrowser.Model.Net
ISocket CreateUdpBroadcastSocket(int localPort);
- ISocket CreateTcpSocket(IPAddress remoteAddress, int remotePort);
-
/// <summary>
/// Creates a new unicast socket using the specified local port number.
/// </summary>
@@ -35,14 +33,4 @@ namespace MediaBrowser.Model.Net
Stream CreateNetworkStream(ISocket socket, bool ownsSocket);
}
-
- public enum SocketType
- {
- Stream
- }
-
- public enum ProtocolType
- {
- Tcp
- }
}
diff --git a/MediaBrowser.Model/Updates/InstallationInfo.cs b/MediaBrowser.Model/Updates/InstallationInfo.cs
index a3f19e236..7554e9fe2 100644
--- a/MediaBrowser.Model/Updates/InstallationInfo.cs
+++ b/MediaBrowser.Model/Updates/InstallationInfo.cs
@@ -36,11 +36,5 @@ namespace MediaBrowser.Model.Updates
/// </summary>
/// <value>The update class.</value>
public PackageVersionClass UpdateClass { get; set; }
-
- /// <summary>
- /// Gets or sets the percent complete.
- /// </summary>
- /// <value>The percent complete.</value>
- public double? PercentComplete { get; set; }
}
}
diff --git a/MediaBrowser.Model/Updates/PackageVersionInfo.cs b/MediaBrowser.Model/Updates/PackageVersionInfo.cs
index be531770d..7ef07c0df 100644
--- a/MediaBrowser.Model/Updates/PackageVersionInfo.cs
+++ b/MediaBrowser.Model/Updates/PackageVersionInfo.cs
@@ -30,23 +30,25 @@ namespace MediaBrowser.Model.Updates
/// The _version
/// </summary>
private Version _version;
+
/// <summary>
/// Gets or sets the version.
/// Had to make this an interpreted property since Protobuf can't handle Version
/// </summary>
/// <value>The version.</value>
[IgnoreDataMember]
- public Version version => _version ?? (_version = new Version(ValueOrDefault(versionStr, "0.0.0.1")));
-
- /// <summary>
- /// Values the or default.
- /// </summary>
- /// <param name="str">The STR.</param>
- /// <param name="def">The def.</param>
- /// <returns>System.String.</returns>
- private static string ValueOrDefault(string str, string def)
+ public Version Version
{
- return string.IsNullOrEmpty(str) ? def : str;
+ get
+ {
+ if (_version == null)
+ {
+ var ver = versionStr;
+ _version = new Version(string.IsNullOrEmpty(ver) ? "0.0.0.1" : ver);
+ }
+
+ return _version;
+ }
}
/// <summary>
diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
index 5941ed436..ab4759c61 100644
--- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj
+++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
@@ -21,6 +21,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+ <GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
</Project>
diff --git a/MediaBrowser.Providers/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Omdb/OmdbProvider.cs
index 19dce34d6..f8b876580 100644
--- a/MediaBrowser.Providers/Omdb/OmdbProvider.cs
+++ b/MediaBrowser.Providers/Omdb/OmdbProvider.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
+using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
@@ -347,7 +348,7 @@ namespace MediaBrowser.Providers.Omdb
CancellationToken = cancellationToken,
BufferContent = true,
EnableDefaultUserAgent = true
- }, "GET");
+ }, HttpMethod.Get);
}
internal string GetDataFilePath(string imdbId)
diff --git a/MediaBrowser.Providers/Studios/StudiosImageProvider.cs b/MediaBrowser.Providers/Studios/StudiosImageProvider.cs
index 4b41589f1..ef412db5a 100644
--- a/MediaBrowser.Providers/Studios/StudiosImageProvider.cs
+++ b/MediaBrowser.Providers/Studios/StudiosImageProvider.cs
@@ -2,10 +2,10 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Net;
-using MediaBrowser.Common.Progress;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Providers;
@@ -143,26 +143,20 @@ namespace MediaBrowser.Providers.Studios
if (!fileInfo.Exists || (DateTime.UtcNow - fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays > 1)
{
- var temp = await httpClient.GetTempFile(new HttpRequestOptions
- {
- CancellationToken = cancellationToken,
- Progress = new SimpleProgress<double>(),
- Url = url
-
- }).ConfigureAwait(false);
-
Directory.CreateDirectory(Path.GetDirectoryName(file));
- try
- {
- File.Copy(temp, file, true);
- }
- catch
+ using (var res = await httpClient.SendAsync(
+ new HttpRequestOptions
+ {
+ CancellationToken = cancellationToken,
+ Url = url
+ },
+ HttpMethod.Get).ConfigureAwait(false))
+ using (var content = res.Content)
+ using (var fileStream = new FileStream(file, FileMode.Create))
{
-
+ await content.CopyToAsync(fileStream).ConfigureAwait(false);
}
-
- return temp;
}
return file;
diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
index c099e77d6..883986894 100644
--- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
+++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
@@ -18,6 +18,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+ <GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
</Project>
diff --git a/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj b/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj
index ba29c656b..f653270a6 100644
--- a/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj
+++ b/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj
@@ -12,6 +12,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+ <GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
</Project>
diff --git a/Mono.Nat/Upnp/Messages/GetServicesMessage.cs b/Mono.Nat/Upnp/Messages/GetServicesMessage.cs
index 72b4c2b25..f619f5ca4 100644
--- a/Mono.Nat/Upnp/Messages/GetServicesMessage.cs
+++ b/Mono.Nat/Upnp/Messages/GetServicesMessage.cs
@@ -25,50 +25,37 @@
//
using System;
-using System.Diagnostics;
using System.Net;
using MediaBrowser.Common.Net;
-using Microsoft.Extensions.Logging;
namespace Mono.Nat.Upnp
{
internal class GetServicesMessage : MessageBase
{
- private string servicesDescriptionUrl;
- private EndPoint hostAddress;
- private readonly ILogger _logger;
+ private string _servicesDescriptionUrl;
+ private EndPoint _hostAddress;
- public GetServicesMessage(string description, EndPoint hostAddress, ILogger logger)
+ public GetServicesMessage(string description, EndPoint hostAddress)
: base(null)
{
if (string.IsNullOrEmpty(description))
- _logger.LogWarning("Description is null");
-
- if (hostAddress == null)
- _logger.LogWarning("hostaddress is null");
-
- this.servicesDescriptionUrl = description;
- this.hostAddress = hostAddress;
- _logger = logger;
- }
-
- public override string Method
- {
- get
{
- return "GET";
+ throw new ArgumentException("Description is null/empty", nameof(description));
}
+
+ this._servicesDescriptionUrl = description;
+ this._hostAddress = hostAddress ?? throw new ArgumentNullException(nameof(hostAddress));
}
+ public override string Method => "GET";
+
public override HttpRequestOptions Encode()
{
- var req = new HttpRequestOptions();
-
- // The periodic request logging may keep some devices awake
- req.LogRequestAsDebug = true;
- req.LogErrors = false;
+ var req = new HttpRequestOptions()
+ {
+ Url = $"http://{this._hostAddress}{this._servicesDescriptionUrl}"
+ };
- req.Url = "http://" + this.hostAddress.ToString() + this.servicesDescriptionUrl;
req.RequestHeaders.Add("ACCEPT-LANGUAGE", "en");
return req;
diff --git a/Mono.Nat/Upnp/Messages/UpnpMessage.cs b/Mono.Nat/Upnp/Messages/UpnpMessage.cs
index ade9df50b..d47241d4a 100644
--- a/Mono.Nat/Upnp/Messages/UpnpMessage.cs
+++ b/Mono.Nat/Upnp/Messages/UpnpMessage.cs
@@ -24,13 +24,8 @@
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
-using System;
-using System.Diagnostics;
using System.Xml;
-using System.Net;
-using System.IO;
using System.Text;
-using System.Globalization;
using MediaBrowser.Common.Net;
namespace Mono.Nat.Upnp
@@ -46,38 +41,31 @@ namespace Mono.Nat.Upnp
protected HttpRequestOptions CreateRequest(string upnpMethod, string methodParameters)
{
- string ss = "http://" + this.device.HostEndPoint.ToString() + this.device.ControlUrl;
+ var req = new HttpRequestOptions()
+ {
+ Url = $"http://{this.device.HostEndPoint}{this.device.ControlUrl}",
+ EnableKeepAlive = false,
+ RequestContentType = "text/xml",
+ RequestContent = "<s:Envelope "
+ + "xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
+ + "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
+ + "<s:Body>"
+ + "<u:" + upnpMethod + " "
+ + "xmlns:u=\"" + device.ServiceType + "\">"
+ + methodParameters
+ + "</u:" + upnpMethod + ">"
+ + "</s:Body>"
+ + "</s:Envelope>\r\n\r\n"
+ };
- var req = new HttpRequestOptions();
- req.LogErrors = false;
-
- // The periodic request logging may keep some devices awake
- req.LogRequestAsDebug = true;
-
- req.Url = ss;
- req.EnableKeepAlive = false;
- req.RequestContentType = "text/xml";
req.RequestHeaders.Add("SOAPACTION", "\"" + device.ServiceType + "#" + upnpMethod + "\"");
- req.RequestContent = "<s:Envelope "
- + "xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
- + "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
- + "<s:Body>"
- + "<u:" + upnpMethod + " "
- + "xmlns:u=\"" + device.ServiceType + "\">"
- + methodParameters
- + "</u:" + upnpMethod + ">"
- + "</s:Body>"
- + "</s:Envelope>\r\n\r\n";
return req;
}
public abstract HttpRequestOptions Encode();
- public virtual string Method
- {
- get { return "POST"; }
- }
+ public virtual string Method => "POST";
protected void WriteFullElement(XmlWriter writer, string element, string value)
{
diff --git a/Mono.Nat/Upnp/UpnpNatDevice.cs b/Mono.Nat/Upnp/UpnpNatDevice.cs
index fd408ee63..3ff1eeb90 100644
--- a/Mono.Nat/Upnp/UpnpNatDevice.cs
+++ b/Mono.Nat/Upnp/UpnpNatDevice.cs
@@ -27,11 +27,9 @@
//
using System;
-using System.IO;
using System.Net;
using System.Xml;
using System.Text;
-using System.Diagnostics;
using System.Threading.Tasks;
using MediaBrowser.Common.Net;
using Microsoft.Extensions.Logging;
@@ -96,7 +94,7 @@ namespace Mono.Nat.Upnp
public async Task GetServicesList()
{
// Create a HTTPWebRequest to download the list of services the device offers
- var message = new GetServicesMessage(this.serviceDescriptionUrl, this.hostEndPoint, _logger);
+ var message = new GetServicesMessage(this.serviceDescriptionUrl, this.hostEndPoint);
using (var response = await _httpClient.SendAsync(message.Encode(), message.Method).ConfigureAwait(false))
{
diff --git a/jellyfin.ruleset b/jellyfin.ruleset
index e7e02a7d5..8ea1d6b16 100644
--- a/jellyfin.ruleset
+++ b/jellyfin.ruleset
@@ -38,5 +38,7 @@
<Rule Id="CA1054" Action="None" />
<!-- disable warning CA1303: Do not pass literals as localized parameters -->
<Rule Id="CA1303" Action="None" />
+ <!-- disable warning CA2000: Dispose objects before losing scope -->
+ <Rule Id="CA2000" Action="None" />
</Rules>
</RuleSet>