mirror of
https://github.com/Tyrrrz/DiscordChatExporter.git
synced 2025-06-03 08:08:40 -04:00
Add support for selectable assets directory in GUI
This commit is contained in:
parent
95115f3e99
commit
d1647e8286
9 changed files with 159 additions and 87 deletions
|
@ -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
|
||||
|
|
|
@ -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)))
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue