Merge pull request #7840 from adrez99/gzip

This commit is contained in:
Claus Vium 2022-10-11 20:46:31 +02:00 committed by GitHub
commit a274f4a688
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 33 additions and 146 deletions

View File

@ -22,7 +22,6 @@ using Emby.Drawing;
using Emby.Naming.Common; using Emby.Naming.Common;
using Emby.Notifications; using Emby.Notifications;
using Emby.Photos; using Emby.Photos;
using Emby.Server.Implementations.Archiving;
using Emby.Server.Implementations.Channels; using Emby.Server.Implementations.Channels;
using Emby.Server.Implementations.Collections; using Emby.Server.Implementations.Collections;
using Emby.Server.Implementations.Configuration; using Emby.Server.Implementations.Configuration;
@ -561,8 +560,6 @@ namespace Emby.Server.Implementations
serviceCollection.AddSingleton<IInstallationManager, InstallationManager>(); serviceCollection.AddSingleton<IInstallationManager, InstallationManager>();
serviceCollection.AddSingleton<IZipClient, ZipClient>();
serviceCollection.AddSingleton<IServerApplicationHost>(this); serviceCollection.AddSingleton<IServerApplicationHost>(this);
serviceCollection.AddSingleton(ApplicationPaths); serviceCollection.AddSingleton(ApplicationPaths);

View File

@ -1,46 +0,0 @@
using System.IO;
using MediaBrowser.Model.IO;
using SharpCompress.Common;
using SharpCompress.Readers;
using SharpCompress.Readers.GZip;
namespace Emby.Server.Implementations.Archiving
{
/// <summary>
/// Class DotNetZipClient.
/// </summary>
public class ZipClient : IZipClient
{
/// <inheritdoc />
public void ExtractAllFromGz(Stream source, string targetPath, bool overwriteExistingFiles)
{
using var reader = GZipReader.Open(source);
var options = new ExtractionOptions
{
ExtractFullPath = true,
Overwrite = overwriteExistingFiles
};
Directory.CreateDirectory(targetPath);
reader.WriteAllToDirectory(targetPath, options);
}
/// <inheritdoc />
public void ExtractFirstFileFromGz(Stream source, string targetPath, string defaultFileName)
{
using var reader = GZipReader.Open(source);
if (reader.MoveToNextEntry())
{
var entry = reader.Entry;
var filename = entry.Key;
if (string.IsNullOrWhiteSpace(filename))
{
filename = defaultFileName;
}
reader.WriteEntryToFile(Path.Combine(targetPath, filename));
}
}
}
}

View File

@ -32,7 +32,6 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.9" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.9" />
<PackageReference Include="Mono.Nat" Version="3.0.3" /> <PackageReference Include="Mono.Nat" Version="3.0.3" />
<PackageReference Include="prometheus-net.DotNetRuntime" Version="4.2.4" /> <PackageReference Include="prometheus-net.DotNetRuntime" Version="4.2.4" />
<PackageReference Include="sharpcompress" Version="0.32.2" />
<PackageReference Include="SQLitePCL.pretty.netstandard" Version="3.1.0" /> <PackageReference Include="SQLitePCL.pretty.netstandard" Version="3.1.0" />
<PackageReference Include="DotNet.Glob" Version="3.1.3" /> <PackageReference Include="DotNet.Glob" Version="3.1.3" />
</ItemGroup> </ItemGroup>

View File

@ -6,9 +6,9 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.IO.Compression;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Security.Cryptography;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Jellyfin.Extensions; using Jellyfin.Extensions;
@ -33,20 +33,17 @@ namespace Emby.Server.Implementations.LiveTv.Listings
private readonly IHttpClientFactory _httpClientFactory; private readonly IHttpClientFactory _httpClientFactory;
private readonly ILogger<XmlTvListingsProvider> _logger; private readonly ILogger<XmlTvListingsProvider> _logger;
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private readonly IZipClient _zipClient;
public XmlTvListingsProvider( public XmlTvListingsProvider(
IServerConfigurationManager config, IServerConfigurationManager config,
IHttpClientFactory httpClientFactory, IHttpClientFactory httpClientFactory,
ILogger<XmlTvListingsProvider> logger, ILogger<XmlTvListingsProvider> logger,
IFileSystem fileSystem, IFileSystem fileSystem)
IZipClient zipClient)
{ {
_config = config; _config = config;
_httpClientFactory = httpClientFactory; _httpClientFactory = httpClientFactory;
_logger = logger; _logger = logger;
_fileSystem = fileSystem; _fileSystem = fileSystem;
_zipClient = zipClient;
} }
public string Name => "XmlTV"; public string Name => "XmlTV";
@ -67,16 +64,12 @@ namespace Emby.Server.Implementations.LiveTv.Listings
{ {
_logger.LogInformation("xmltv path: {Path}", info.Path); _logger.LogInformation("xmltv path: {Path}", info.Path);
if (!info.Path.StartsWith("http", StringComparison.OrdinalIgnoreCase))
{
return UnzipIfNeeded(info.Path, info.Path);
}
string cacheFilename = info.Id + ".xml"; string cacheFilename = info.Id + ".xml";
string cacheFile = Path.Combine(_config.ApplicationPaths.CachePath, "xmltv", cacheFilename); string cacheFile = Path.Combine(_config.ApplicationPaths.CachePath, "xmltv", cacheFilename);
if (File.Exists(cacheFile) && File.GetLastWriteTimeUtc(cacheFile) >= DateTime.UtcNow.Subtract(_maxCacheAge)) if (File.Exists(cacheFile) && File.GetLastWriteTimeUtc(cacheFile) >= DateTime.UtcNow.Subtract(_maxCacheAge))
{ {
return UnzipIfNeeded(info.Path, cacheFile); return cacheFile;
} }
// Must check if file exists as parent directory may not exist. // Must check if file exists as parent directory may not exist.
@ -84,95 +77,55 @@ namespace Emby.Server.Implementations.LiveTv.Listings
{ {
File.Delete(cacheFile); File.Delete(cacheFile);
} }
else
_logger.LogInformation("Downloading xmltv listings from {Path}", info.Path);
Directory.CreateDirectory(Path.GetDirectoryName(cacheFile));
using var response = await _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(info.Path, cancellationToken).ConfigureAwait(false);
await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
await using (var fileStream = new FileStream(cacheFile, FileMode.CreateNew, FileAccess.Write, FileShare.None, IODefaults.CopyToBufferSize, FileOptions.Asynchronous))
{ {
await stream.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false); Directory.CreateDirectory(Path.GetDirectoryName(cacheFile));
} }
return UnzipIfNeeded(info.Path, cacheFile); if (info.Path.StartsWith("http", StringComparison.OrdinalIgnoreCase))
{
_logger.LogInformation("Downloading xmltv listings from {Path}", info.Path);
using var response = await _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(info.Path, cancellationToken).ConfigureAwait(false);
await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
return await UnzipIfNeededAndCopy(info.Path, stream, cacheFile, cancellationToken).ConfigureAwait(false);
}
else
{
await using var stream = new FileStream(info.Path, FileMode.Open, FileAccess.Read, FileShare.Read, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous);
return await UnzipIfNeededAndCopy(info.Path, stream, cacheFile, cancellationToken).ConfigureAwait(false);
}
} }
private string UnzipIfNeeded(ReadOnlySpan<char> originalUrl, string file) private async Task<string> UnzipIfNeededAndCopy(string originalUrl, Stream stream, string file, CancellationToken cancellationToken)
{ {
ReadOnlySpan<char> ext = Path.GetExtension(originalUrl.LeftPart('?')); int index = originalUrl.IndexOf('?', StringComparison.CurrentCulture);
string ext = Path.GetExtension(index > -1 ? originalUrl.Remove(index) : originalUrl);
await using var fileStream = new FileStream(file, FileMode.CreateNew, FileAccess.Write, FileShare.None, IODefaults.CopyToBufferSize, FileOptions.Asynchronous);
if (ext.Equals(".gz", StringComparison.OrdinalIgnoreCase)) if (ext.Equals(".gz", StringComparison.OrdinalIgnoreCase))
{ {
try try
{ {
string tempFolder = ExtractGz(file); using var reader = new GZipStream(stream, CompressionMode.Decompress);
return FindXmlFile(tempFolder); await reader.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError(ex, "Error extracting from gz file {File}", file); _logger.LogError(ex, "Error extracting from gz file {File}", originalUrl);
}
try
{
string tempFolder = ExtractFirstFileFromGz(file);
return FindXmlFile(tempFolder);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error extracting from zip file {File}", file);
} }
} }
else
{
await stream.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false);
}
return file; return file;
} }
private string ExtractFirstFileFromGz(string file)
{
using (var stream = File.OpenRead(file))
{
string tempFolder = GetTempFolderPath(stream);
Directory.CreateDirectory(tempFolder);
_zipClient.ExtractFirstFileFromGz(stream, tempFolder, "data.xml");
return tempFolder;
}
}
private string ExtractGz(string file)
{
using (var stream = File.OpenRead(file))
{
string tempFolder = GetTempFolderPath(stream);
Directory.CreateDirectory(tempFolder);
_zipClient.ExtractAllFromGz(stream, tempFolder, true);
return tempFolder;
}
}
private string GetTempFolderPath(Stream stream)
{
#pragma warning disable CA5351
using var md5 = MD5.Create();
#pragma warning restore CA5351
var checksum = Convert.ToHexString(md5.ComputeHash(stream));
stream.Position = 0;
return Path.Combine(_config.ApplicationPaths.TempDirectory, checksum);
}
private string FindXmlFile(string directory)
{
return _fileSystem.GetFiles(directory, true)
.Where(i => string.Equals(i.Extension, ".xml", StringComparison.OrdinalIgnoreCase))
.Select(i => i.FullName)
.FirstOrDefault();
}
public async Task<IEnumerable<ProgramInfo>> GetProgramsAsync(ListingsProviderInfo info, string channelId, DateTime startDateUtc, DateTime endDateUtc, CancellationToken cancellationToken) public async Task<IEnumerable<ProgramInfo>> GetProgramsAsync(ListingsProviderInfo info, string channelId, DateTime startDateUtc, DateTime endDateUtc, CancellationToken cancellationToken)
{ {
if (string.IsNullOrWhiteSpace(channelId)) if (string.IsNullOrWhiteSpace(channelId))

View File

@ -1,16 +0,0 @@
#pragma warning disable CS1591
using System.IO;
namespace MediaBrowser.Model.IO
{
/// <summary>
/// Interface IZipClient.
/// </summary>
public interface IZipClient
{
void ExtractAllFromGz(Stream source, string targetPath, bool overwriteExistingFiles);
void ExtractFirstFileFromGz(Stream source, string targetPath, string defaultFileName);
}
}