Add support for selectable assets directory in GUI

This commit is contained in:
Tyrrrz 2023-02-17 21:30:10 +02:00
parent 95115f3e99
commit d1647e8286
9 changed files with 159 additions and 87 deletions

View file

@ -29,7 +29,7 @@ internal class ExportContext
Request = request;
_assetDownloader = new ExportAssetDownloader(
request.OutputAssetsDirPath,
request.AssetsDirPath,
request.ShouldReuseAssets
);
}
@ -92,34 +92,25 @@ internal class ExportContext
try
{
var absoluteFilePath = await _assetDownloader.DownloadAsync(url, cancellationToken);
var filePath = await _assetDownloader.DownloadAsync(url, cancellationToken);
var relativeFilePath = Path.GetRelativePath(Request.OutputDirPath, filePath);
// We want relative path so that the output files can be copied around without breaking.
// Base directory path may be null if the file is stored at the root or relative to working directory.
var relativeFilePath = !string.IsNullOrWhiteSpace(Request.OutputBaseDirPath)
? Path.GetRelativePath(Request.OutputBaseDirPath, absoluteFilePath)
: absoluteFilePath;
// 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 the absolute path instead.
var optimalFilePath =
relativeFilePath.StartsWith(".." + Path.DirectorySeparatorChar, StringComparison.Ordinal) ||
relativeFilePath.StartsWith(".." + Path.AltDirectorySeparatorChar, StringComparison.Ordinal)
? filePath
: relativeFilePath;
// If the assets path is outside of the export directory, fall back to absolute path
var filePath = relativeFilePath.StartsWith("..")
? absoluteFilePath
: relativeFilePath;
// HACK: for HTML, we need to format the URL properly
// For HTML, the path needs to be properly formatted
if (Request.Format is ExportFormat.HtmlDark or ExportFormat.HtmlLight)
{
// Need to escape each path segment while keeping the directory separators intact
return string.Join(
Path.AltDirectorySeparatorChar,
filePath
.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)
.Select(Uri.EscapeDataString)
.Select(x => x.Replace("%3A", ":"))
);
// Create a 'file:///' URI and then strip the 'file:///' prefix to allow for relative paths
return new Uri(new Uri("file:///"), optimalFilePath).ToString()[8..];
}
return filePath;
return optimalFilePath;
}
// Try to catch only exceptions related to failed HTTP requests
// https://github.com/Tyrrrz/DiscordChatExporter/issues/332

View file

@ -10,48 +10,87 @@ using DiscordChatExporter.Core.Utils;
namespace DiscordChatExporter.Core.Exporting;
public partial record ExportRequest(
Guild Guild,
Channel Channel,
string OutputPath,
string? AssetsPath,
ExportFormat Format,
Snowflake? After,
Snowflake? Before,
PartitionLimit PartitionLimit,
MessageFilter MessageFilter,
bool ShouldFormatMarkdown,
bool ShouldDownloadAssets,
bool ShouldReuseAssets,
string DateFormat)
public partial class ExportRequest
{
private string? _outputBaseFilePath;
public string OutputBaseFilePath => _outputBaseFilePath ??= GetOutputBaseFilePath(
Guild,
Channel,
OutputPath,
Format,
After,
Before
);
public Guild Guild { get; }
public string OutputBaseDirPath => Path.GetDirectoryName(OutputBaseFilePath) ?? OutputPath;
public Channel Channel { get; }
private string? _outputAssetsDirPath;
public string OutputAssetsDirPath => _outputAssetsDirPath ??= (
AssetsPath is not null
? EvaluateTemplateTokens(
AssetsPath,
Guild,
Channel,
After,
Before
)
: $"{OutputBaseFilePath}_Files{Path.DirectorySeparatorChar}"
public string OutputFilePath { get; }
public string OutputDirPath { get; }
public string AssetsDirPath { get; }
public ExportFormat Format { get; }
public Snowflake? After { get; }
public Snowflake? Before { get; }
public PartitionLimit PartitionLimit { get; }
public MessageFilter MessageFilter { get; }
public bool ShouldFormatMarkdown { get; }
public bool ShouldDownloadAssets { get; }
public bool ShouldReuseAssets { get; }
public string DateFormat { get; }
public ExportRequest(
Guild guild,
Channel channel,
string outputPath,
string? assetsDirPath,
ExportFormat format,
Snowflake? after,
Snowflake? before,
PartitionLimit partitionLimit,
MessageFilter messageFilter,
bool shouldFormatMarkdown,
bool shouldDownloadAssets,
bool shouldReuseAssets,
string dateFormat)
{
Guild = guild;
Channel = channel;
Format = format;
After = after;
Before = before;
PartitionLimit = partitionLimit;
MessageFilter = messageFilter;
ShouldFormatMarkdown = shouldFormatMarkdown;
ShouldDownloadAssets = shouldDownloadAssets;
ShouldReuseAssets = shouldReuseAssets;
DateFormat = dateFormat;
OutputFilePath = GetOutputBaseFilePath(
Guild,
Channel,
outputPath,
Format,
After,
Before
);
OutputDirPath = Path.GetDirectoryName(OutputFilePath)!;
AssetsDirPath = !string.IsNullOrWhiteSpace(assetsDirPath)
? FormatPath(
assetsDirPath,
Guild,
Channel,
After,
Before
)
: $"{OutputFilePath}_Files{Path.DirectorySeparatorChar}";
}
}
public partial record ExportRequest
public partial class ExportRequest
{
public static string GetDefaultOutputFileName(
Guild guild,
@ -95,7 +134,7 @@ public partial record ExportRequest
return PathEx.EscapeFileName(buffer.ToString());
}
private static string EvaluateTemplateTokens(
private static string FormatPath(
string path,
Guild guild,
Channel channel,
@ -120,7 +159,8 @@ public partial record ExportRequest
"%d" => DateTimeOffset.Now.ToString("yyyy-MM-dd"),
"%%" => "%",
_ => m.Value
}));
})
);
}
private static string GetOutputBaseFilePath(
@ -131,7 +171,7 @@ public partial record ExportRequest
Snowflake? after = null,
Snowflake? before = null)
{
var actualOutputPath = EvaluateTemplateTokens(outputPath, guild, channel, after, before);
var actualOutputPath = FormatPath(outputPath, guild, channel, after, before);
// Output is a directory
if (Directory.Exists(actualOutputPath) || string.IsNullOrWhiteSpace(Path.GetExtension(actualOutputPath)))

View file

@ -51,8 +51,8 @@ internal partial class MessageExporter : IAsyncDisposable
if (_writer is not null)
return _writer;
Directory.CreateDirectory(_context.Request.OutputBaseDirPath);
var filePath = GetPartitionFilePath(_context.Request.OutputBaseFilePath, _partitionIndex);
Directory.CreateDirectory(_context.Request.OutputDirPath);
var filePath = GetPartitionFilePath(_context.Request.OutputFilePath, _partitionIndex);
var writer = CreateMessageWriter(filePath, _context.Request.Format, _context);
await writer.WritePreambleAsync(cancellationToken);