This commit is contained in:
Tyrrrz 2023-07-03 18:35:28 +03:00
parent 768fb88c8c
commit c69211797f
3 changed files with 65 additions and 77 deletions

View file

@ -48,23 +48,22 @@ public static class ExportWrapper
var fileName = channelId.ToString() + '.' + format.GetFileExtension(); var fileName = channelId.ToString() + '.' + format.GetFileExtension();
var filePath = Path.Combine(DirPath, fileName); var filePath = Path.Combine(DirPath, fileName);
// Lock separately for each channel and format using var _ = await Locker.LockAsync(filePath);
using (await Locker.LockAsync(filePath)) using var console = new FakeConsole();
{
// Perform the export only if it hasn't been done before
if (!File.Exists(filePath))
{
await new ExportChannelsCommand
{
Token = Secrets.DiscordToken,
ChannelIds = new[] { channelId },
ExportFormat = format,
OutputPath = filePath
}.ExecuteAsync(new FakeConsole());
}
return await File.ReadAllTextAsync(filePath); // Perform the export only if it hasn't been done before
if (!File.Exists(filePath))
{
await new ExportChannelsCommand
{
Token = Secrets.DiscordToken,
ChannelIds = new[] { channelId },
ExportFormat = format,
OutputPath = filePath
}.ExecuteAsync(console);
} }
return await File.ReadAllTextAsync(filePath);
} }
public static async ValueTask<IHtmlDocument> ExportAsHtmlAsync(Snowflake channelId) => Html.Parse( public static async ValueTask<IHtmlDocument> ExportAsHtmlAsync(Snowflake channelId) => Html.Parse(

View file

@ -38,50 +38,49 @@ internal partial class ExportAssetDownloader
var fileName = GetFileNameFromUrl(url); var fileName = GetFileNameFromUrl(url);
var filePath = Path.Combine(_workingDirPath, fileName); var filePath = Path.Combine(_workingDirPath, fileName);
using (await Locker.LockAsync(filePath, cancellationToken)) using var _ = await Locker.LockAsync(filePath, cancellationToken);
{
if (_pathCache.TryGetValue(url, out var cachedFilePath))
return cachedFilePath;
// Reuse existing files if we're allowed to if (_pathCache.TryGetValue(url, out var cachedFilePath))
if (_reuse && File.Exists(filePath)) return cachedFilePath;
return _pathCache[url] = filePath;
Directory.CreateDirectory(_workingDirPath);
await Http.ResiliencePolicy.ExecuteAsync(async () =>
{
// Download the file
using var response = await Http.Client.GetAsync(url, cancellationToken);
await using (var output = File.Create(filePath))
await response.Content.CopyToAsync(output, cancellationToken);
// Try to set the file date according to the last-modified header
try
{
var lastModified = response.Content.Headers.TryGetValue("Last-Modified")?.Pipe(s =>
DateTimeOffset.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.None, out var instant)
? instant
: (DateTimeOffset?)null
);
if (lastModified is not null)
{
File.SetCreationTimeUtc(filePath, lastModified.Value.UtcDateTime);
File.SetLastWriteTimeUtc(filePath, lastModified.Value.UtcDateTime);
File.SetLastAccessTimeUtc(filePath, lastModified.Value.UtcDateTime);
}
}
catch
{
// This can apparently fail for some reason.
// Updating the file date is not a critical task, so we'll just ignore exceptions thrown here.
// https://github.com/Tyrrrz/DiscordChatExporter/issues/585
}
});
// Reuse existing files if we're allowed to
if (_reuse && File.Exists(filePath))
return _pathCache[url] = filePath; return _pathCache[url] = filePath;
}
Directory.CreateDirectory(_workingDirPath);
await Http.ResiliencePolicy.ExecuteAsync(async () =>
{
// Download the file
using var response = await Http.Client.GetAsync(url, cancellationToken);
await using (var output = File.Create(filePath))
await response.Content.CopyToAsync(output, cancellationToken);
// Try to set the file date according to the last-modified header
try
{
var lastModified = response.Content.Headers.TryGetValue("Last-Modified")?.Pipe(s =>
DateTimeOffset.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.None, out var instant)
? instant
: (DateTimeOffset?)null
);
if (lastModified is not null)
{
File.SetCreationTimeUtc(filePath, lastModified.Value.UtcDateTime);
File.SetLastWriteTimeUtc(filePath, lastModified.Value.UtcDateTime);
File.SetLastAccessTimeUtc(filePath, lastModified.Value.UtcDateTime);
}
}
catch
{
// This can apparently fail for some reason.
// Updating the file date is not a critical task, so we'll just ignore exceptions thrown here.
// https://github.com/Tyrrrz/DiscordChatExporter/issues/585
}
});
return _pathCache[url] = filePath;
} }
} }

View file

@ -89,27 +89,17 @@ internal class ExportContext
public Role? TryGetRole(Snowflake id) => _roles.GetValueOrDefault(id); public Role? TryGetRole(Snowflake id) => _roles.GetValueOrDefault(id);
public Color? TryGetUserColor(Snowflake id) public IReadOnlyList<Role> GetUserRoles(Snowflake id) => TryGetMember(id)?
{ .RoleIds
var memberRoles = GetUserRoles(id); .Select(TryGetRole)
.WhereNotNull()
.OrderByDescending(r => r.Position)
.ToArray() ?? Array.Empty<Role>();
return memberRoles? public Color? TryGetUserColor(Snowflake id) => GetUserRoles(id)
.Where(r => r.Color is not null) .Where(r => r.Color is not null)
.Select(r => r.Color) .Select(r => r.Color)
.FirstOrDefault(); .FirstOrDefault();
}
public IReadOnlyList<Role> GetUserRoles(Snowflake id)
{
var member = TryGetMember(id);
return member?
.RoleIds
.Select(TryGetRole)
.WhereNotNull()
.OrderByDescending(r => r.Position)
.ToArray() ?? Array.Empty<Role>();
}
public async ValueTask<string> ResolveAssetUrlAsync(string url, CancellationToken cancellationToken = default) public async ValueTask<string> ResolveAssetUrlAsync(string url, CancellationToken cancellationToken = default)
{ {
@ -122,7 +112,7 @@ internal class ExportContext
var relativeFilePath = Path.GetRelativePath(Request.OutputDirPath, filePath); var relativeFilePath = Path.GetRelativePath(Request.OutputDirPath, filePath);
// Prefer relative paths so that the output files can be copied around without breaking references. // Prefer relative paths so that the output files can be copied around without breaking references.
// If the assets path is outside of the export directory, use an absolute path instead. // If the asset directory is outside of the export directory, use an absolute path instead.
var optimalFilePath = var optimalFilePath =
relativeFilePath.StartsWith(".." + Path.DirectorySeparatorChar, StringComparison.Ordinal) || relativeFilePath.StartsWith(".." + Path.DirectorySeparatorChar, StringComparison.Ordinal) ||
relativeFilePath.StartsWith(".." + Path.AltDirectorySeparatorChar, StringComparison.Ordinal) relativeFilePath.StartsWith(".." + Path.AltDirectorySeparatorChar, StringComparison.Ordinal)