mirror of
https://github.com/Tyrrrz/DiscordChatExporter.git
synced 2025-05-22 10:55:15 -04:00
Add option to specify download directory for assets (#989)
Co-authored-by: Oleksii Holub <1935960+Tyrrrz@users.noreply.github.com>
This commit is contained in:
parent
53ff8ba430
commit
95115f3e99
4 changed files with 67 additions and 16 deletions
|
@ -98,6 +98,20 @@ public abstract class ExportCommandBase : TokenCommandBase
|
||||||
)]
|
)]
|
||||||
public bool ShouldReuseAssets { get; init; }
|
public bool ShouldReuseAssets { get; init; }
|
||||||
|
|
||||||
|
private readonly string? _assetsPath;
|
||||||
|
|
||||||
|
[CommandOption(
|
||||||
|
"media-dir",
|
||||||
|
Description = "Download assets to this directory."
|
||||||
|
)]
|
||||||
|
public string? AssetsPath
|
||||||
|
{
|
||||||
|
get => _assetsPath;
|
||||||
|
// Handle ~/ in paths on Unix systems
|
||||||
|
// https://github.com/Tyrrrz/DiscordChatExporter/pull/903
|
||||||
|
init => _assetsPath = value is not null ? Path.GetFullPath(value) : null;
|
||||||
|
}
|
||||||
|
|
||||||
[CommandOption(
|
[CommandOption(
|
||||||
"dateformat",
|
"dateformat",
|
||||||
Description = "Format used when writing dates."
|
Description = "Format used when writing dates."
|
||||||
|
@ -124,6 +138,14 @@ public abstract class ExportCommandBase : TokenCommandBase
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Assets directory should only be specified when the download assets option is set
|
||||||
|
if (!string.IsNullOrWhiteSpace(AssetsPath) && !ShouldDownloadAssets)
|
||||||
|
{
|
||||||
|
throw new CommandException(
|
||||||
|
"Option --media-dir cannot be used without --media."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Make sure the user does not try to export all channels into a single file.
|
// Make sure the user does not try to export all channels into a single file.
|
||||||
// Output path must either be a directory, or contain template tokens.
|
// Output path must either be a directory, or contain template tokens.
|
||||||
// https://github.com/Tyrrrz/DiscordChatExporter/issues/799
|
// https://github.com/Tyrrrz/DiscordChatExporter/issues/799
|
||||||
|
@ -172,6 +194,7 @@ public abstract class ExportCommandBase : TokenCommandBase
|
||||||
guild,
|
guild,
|
||||||
channel,
|
channel,
|
||||||
OutputPath,
|
OutputPath,
|
||||||
|
AssetsPath,
|
||||||
ExportFormat,
|
ExportFormat,
|
||||||
After,
|
After,
|
||||||
Before,
|
Before,
|
||||||
|
|
|
@ -92,13 +92,19 @@ internal class ExportContext
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var filePath = await _assetDownloader.DownloadAsync(url, cancellationToken);
|
var absoluteFilePath = await _assetDownloader.DownloadAsync(url, cancellationToken);
|
||||||
|
|
||||||
// We want relative path so that the output files can be copied around without breaking.
|
// 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.
|
// 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)
|
var relativeFilePath = !string.IsNullOrWhiteSpace(Request.OutputBaseDirPath)
|
||||||
? Path.GetRelativePath(Request.OutputBaseDirPath, filePath)
|
? Path.GetRelativePath(Request.OutputBaseDirPath, absoluteFilePath)
|
||||||
: filePath;
|
: absoluteFilePath;
|
||||||
|
|
||||||
|
// 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
|
// HACK: for HTML, we need to format the URL properly
|
||||||
if (Request.Format is ExportFormat.HtmlDark or ExportFormat.HtmlLight)
|
if (Request.Format is ExportFormat.HtmlDark or ExportFormat.HtmlLight)
|
||||||
|
@ -106,13 +112,14 @@ internal class ExportContext
|
||||||
// Need to escape each path segment while keeping the directory separators intact
|
// Need to escape each path segment while keeping the directory separators intact
|
||||||
return string.Join(
|
return string.Join(
|
||||||
Path.AltDirectorySeparatorChar,
|
Path.AltDirectorySeparatorChar,
|
||||||
relativeFilePath
|
filePath
|
||||||
.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)
|
.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)
|
||||||
.Select(Uri.EscapeDataString)
|
.Select(Uri.EscapeDataString)
|
||||||
|
.Select(x => x.Replace("%3A", ":"))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return relativeFilePath;
|
return filePath;
|
||||||
}
|
}
|
||||||
// Try to catch only exceptions related to failed HTTP requests
|
// Try to catch only exceptions related to failed HTTP requests
|
||||||
// https://github.com/Tyrrrz/DiscordChatExporter/issues/332
|
// https://github.com/Tyrrrz/DiscordChatExporter/issues/332
|
||||||
|
|
|
@ -14,6 +14,7 @@ public partial record ExportRequest(
|
||||||
Guild Guild,
|
Guild Guild,
|
||||||
Channel Channel,
|
Channel Channel,
|
||||||
string OutputPath,
|
string OutputPath,
|
||||||
|
string? AssetsPath,
|
||||||
ExportFormat Format,
|
ExportFormat Format,
|
||||||
Snowflake? After,
|
Snowflake? After,
|
||||||
Snowflake? Before,
|
Snowflake? Before,
|
||||||
|
@ -36,7 +37,18 @@ public partial record ExportRequest(
|
||||||
|
|
||||||
public string OutputBaseDirPath => Path.GetDirectoryName(OutputBaseFilePath) ?? OutputPath;
|
public string OutputBaseDirPath => Path.GetDirectoryName(OutputBaseFilePath) ?? OutputPath;
|
||||||
|
|
||||||
public string OutputAssetsDirPath => $"{OutputBaseFilePath}_Files{Path.DirectorySeparatorChar}";
|
private string? _outputAssetsDirPath;
|
||||||
|
public string OutputAssetsDirPath => _outputAssetsDirPath ??= (
|
||||||
|
AssetsPath is not null
|
||||||
|
? EvaluateTemplateTokens(
|
||||||
|
AssetsPath,
|
||||||
|
Guild,
|
||||||
|
Channel,
|
||||||
|
After,
|
||||||
|
Before
|
||||||
|
)
|
||||||
|
: $"{OutputBaseFilePath}_Files{Path.DirectorySeparatorChar}"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public partial record ExportRequest
|
public partial record ExportRequest
|
||||||
|
@ -83,17 +95,15 @@ public partial record ExportRequest
|
||||||
return PathEx.EscapeFileName(buffer.ToString());
|
return PathEx.EscapeFileName(buffer.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetOutputBaseFilePath(
|
private static string EvaluateTemplateTokens(
|
||||||
|
string path,
|
||||||
Guild guild,
|
Guild guild,
|
||||||
Channel channel,
|
Channel channel,
|
||||||
string outputPath,
|
Snowflake? after,
|
||||||
ExportFormat format,
|
Snowflake? before)
|
||||||
Snowflake? after = null,
|
|
||||||
Snowflake? before = null)
|
|
||||||
{
|
{
|
||||||
// Format path
|
return Regex.Replace(
|
||||||
var actualOutputPath = Regex.Replace(
|
path,
|
||||||
outputPath,
|
|
||||||
"%.",
|
"%.",
|
||||||
m => PathEx.EscapeFileName(m.Value switch
|
m => PathEx.EscapeFileName(m.Value switch
|
||||||
{
|
{
|
||||||
|
@ -110,8 +120,18 @@ public partial record ExportRequest
|
||||||
"%d" => DateTimeOffset.Now.ToString("yyyy-MM-dd"),
|
"%d" => DateTimeOffset.Now.ToString("yyyy-MM-dd"),
|
||||||
"%%" => "%",
|
"%%" => "%",
|
||||||
_ => m.Value
|
_ => m.Value
|
||||||
})
|
}));
|
||||||
);
|
}
|
||||||
|
|
||||||
|
private static string GetOutputBaseFilePath(
|
||||||
|
Guild guild,
|
||||||
|
Channel channel,
|
||||||
|
string outputPath,
|
||||||
|
ExportFormat format,
|
||||||
|
Snowflake? after = null,
|
||||||
|
Snowflake? before = null)
|
||||||
|
{
|
||||||
|
var actualOutputPath = EvaluateTemplateTokens(outputPath, guild, channel, after, before);
|
||||||
|
|
||||||
// Output is a directory
|
// Output is a directory
|
||||||
if (Directory.Exists(actualOutputPath) || string.IsNullOrWhiteSpace(Path.GetExtension(actualOutputPath)))
|
if (Directory.Exists(actualOutputPath) || string.IsNullOrWhiteSpace(Path.GetExtension(actualOutputPath)))
|
||||||
|
|
|
@ -186,6 +186,7 @@ public class DashboardViewModel : PropertyChangedBase
|
||||||
dialog.Guild!,
|
dialog.Guild!,
|
||||||
channel,
|
channel,
|
||||||
dialog.OutputPath!,
|
dialog.OutputPath!,
|
||||||
|
null,
|
||||||
dialog.SelectedFormat,
|
dialog.SelectedFormat,
|
||||||
dialog.After?.Pipe(Snowflake.FromDate),
|
dialog.After?.Pipe(Snowflake.FromDate),
|
||||||
dialog.Before?.Pipe(Snowflake.FromDate),
|
dialog.Before?.Pipe(Snowflake.FromDate),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue