diff --git a/.docs/Troubleshooting.md b/.docs/Troubleshooting.md
index 024a8dbb..cc9662d8 100644
--- a/.docs/Troubleshooting.md
+++ b/.docs/Troubleshooting.md
@@ -60,7 +60,9 @@ No, DCE is an exporter.
### Can DCE add new messages to an existing export?
-No.
+Yes, existing exports can be appended.
+In this case, the existing export file won't be changed, but a new partition file will be created, which will contain
+all newer messages.
## First steps
@@ -82,7 +84,10 @@ Check the following pages to learn how to schedule **DiscordChatExporter.CLI** r
### The exported file is too large, I can't open it
-Try opening it with a different program, try partitioning or use a different file format, like `PlainText`.
+Re-export the channel using a partition size; this will cause the exporter to split up the export into multiple smaller
+files you can open.
+
+Alternatively, you could also try to open it with a different program or use a different file format, like `PlainText`.
### I see messages in the export, but they have no content
@@ -146,6 +151,18 @@ Red Hat: `cert-sync --user /etc/pki/tls/certs/ca-bundle.crt`
If it still doesn't work, try mozroots: `mozroots --import --ask-remove`
+```yml
+System.InvalidOperationException: Error: The target export file already exists. This should never happen.
+```
+
+↳ This means that a file with the export target path somehow exists despite not having been detected.
+It's a failsafe implemented to prevent unintentionally overwriting a file.
+
+It might occur if you try to export the same channel multiple times in parallel, or if you've manually deleted some
+partition files.
+
+In that case, double-check the command, manually delete the respective export files and try again.
+
## macOS-specific
### DiscordChatExporter is damaged and can’t be opened. You should move it to the Trash.
diff --git a/.docs/Using-the-CLI.md b/.docs/Using-the-CLI.md
index 20c6315e..4ca255af 100644
--- a/.docs/Using-the-CLI.md
+++ b/.docs/Using-the-CLI.md
@@ -175,6 +175,32 @@ providing a path that ends with a slash. All of the exported media will be store
./DiscordChatExporter.Cli export -t "mfa.Ifrn" -c 53555 --media --media-dir "C:\Discord Media"
```
+#### Handling existing exports
+
+By default, the exporter aborts the current channel export if it had previously been exported to prevent accidental
+overwrites.
+But it also has the options to either overwrite or append the existing export.
+You can specify the desired behaviour by using `--export-exists` with one of the possible values `abort`, `append`
+and `overwrite`.
+
+```console
+./DiscordChatExporter.Cli export -t "mfa.Ifrn" -c 53555 --export-exists append
+```
+
+#### Search for existing exports
+
+By default, the exporter only tests whether there is an existing export at the target file path.
+But the exporter can also search in the entire target directory to determine whether one of the current channels had
+previously been exported.
+This is necessary to detect an existing export if the name of the channel, the channel parent or the guild has changed
+or if the default file formatting has changed.
+The `--search-existing-exports` option enables this.
+
+```console
+./DiscordChatExporter.Cli export -t "mfa.Ifrn" -c 53555 --search-existing-exports
+```
+
+
#### Changing the date format
You can customize how dates are formatted in the exported files by using `--locale` and inserting one of Discord's
diff --git a/.docs/Using-the-GUI.md b/.docs/Using-the-GUI.md
index d175765b..2b2b6bfe 100644
--- a/.docs/Using-the-GUI.md
+++ b/.docs/Using-the-GUI.md
@@ -103,3 +103,12 @@ Default: 1
- **Normalize to UTC** - Convert all dates to UTC before exporting.
+- **Export exists** Controls what the exporter should do if the current channel had previously been exported.
+Default: Abort
+
+- **Search for existing export** Whether the exporter should search in the entire target directory to determine whether
+one of the current channels had previously been exported.
+This is necessary to detect an existing export if the name of the channel, the channel parent or the guild has changed
+or if the default file formatting has changed.
+Default: Disabled
+
diff --git a/DiscordChatExporter.Cli/Utils/Extensions/ConsoleExtensions.cs b/DiscordChatExporter.Cli/Utils/Extensions/ConsoleExtensions.cs
index f311dc87..7e011784 100644
--- a/DiscordChatExporter.Cli/Utils/Extensions/ConsoleExtensions.cs
+++ b/DiscordChatExporter.Cli/Utils/Extensions/ConsoleExtensions.cs
@@ -127,7 +127,7 @@ public class ConsoleProgressLogger(IAnsiConsole console) : ProgressLogger
///
/// Prints a summary on all previously logged exports and their respective results to the console.
///
- /// The file exists handling of the export whose summary should be printed.
+ /// The export exists handling of the export whose summary should be printed.
public void PrintExportSummary(ExportExistsHandling updateType)
{
var exportSummary = GetExportSummary(updateType);
diff --git a/DiscordChatExporter.Core/Exporting/ChannelExporter.cs b/DiscordChatExporter.Core/Exporting/ChannelExporter.cs
index ae3a30db..08f5166c 100644
--- a/DiscordChatExporter.Core/Exporting/ChannelExporter.cs
+++ b/DiscordChatExporter.Core/Exporting/ChannelExporter.cs
@@ -228,7 +228,7 @@ public class ChannelExporter(DiscordClient discord)
}
///
- /// Handles the existing export files of the current request according to the set file exists handling.
+ /// Handles the existing export files of the current request according to the set export exists handling.
///
/// The request specifying the current channel export.
/// The logger that's used to log progress updates about the export.
@@ -268,6 +268,12 @@ public class ChannelExporter(DiscordClient discord)
case ExportExistsHandling.Overwrite:
logger.LogWarning(request, "Removing existing export files");
MessageExporter.RemoveExistingExport(existingExportFile);
+ var possibleExistingExportDir = $"{existingExportFile}_Files{Path.DirectorySeparatorChar}";
+ if (Directory.Exists(possibleExistingExportDir))
+ {
+ logger.LogWarning(request, "Removing existing export asset files");
+ Directory.Delete(possibleExistingExportDir, true);
+ }
currentPartitionIndex = 0;
return true;
case ExportExistsHandling.Append:
@@ -275,6 +281,7 @@ public class ChannelExporter(DiscordClient discord)
{
logger.LogInfo(request, "Moving existing export files to the new file names");
MessageExporter.MoveExistingExport(existingExportFile, request.OutputFilePath);
+ // The asset directory isn't renamed as the file contents still point to the old name
}
var lastMessageSnowflake = MessageExporter.GetLastMessageSnowflake(
diff --git a/DiscordChatExporter.Core/Exporting/Logging/ProgressLogger.cs b/DiscordChatExporter.Core/Exporting/Logging/ProgressLogger.cs
index c638c3d0..817f2306 100644
--- a/DiscordChatExporter.Core/Exporting/Logging/ProgressLogger.cs
+++ b/DiscordChatExporter.Core/Exporting/Logging/ProgressLogger.cs
@@ -25,7 +25,7 @@ public abstract class ProgressLogger
/// Generates and returns a summary on all previously logged exports and their respective results.
/// The summary is returned as one string for each export result that occurred at least once.
///
- /// The file exists handling of the export whose summary should be returned.
+ /// The export exists handling of the export whose summary should be returned.
/// A summary on all previously logged exports and their respective results.
protected Dictionary GetExportSummary(ExportExistsHandling updateType)
{
diff --git a/DiscordChatExporter.Gui/Framework/SnackbarManager.cs b/DiscordChatExporter.Gui/Framework/SnackbarManager.cs
index 0e502c26..f7d5ae0a 100644
--- a/DiscordChatExporter.Gui/Framework/SnackbarManager.cs
+++ b/DiscordChatExporter.Gui/Framework/SnackbarManager.cs
@@ -103,7 +103,7 @@ public class SnackbarProgressLogger(SnackbarManager snackbarManager) : ProgressL
///
/// Prints a summary on all previously logged exports and their respective results in the GUI snackbar.
///
- /// The file exists handling of the export whose summary should be printed.
+ /// The export exists handling of the export whose summary should be printed.
public void PrintExportSummary(ExportExistsHandling updateType)
{
var exportSummary = GetExportSummary(updateType);
diff --git a/Readme.md b/Readme.md
index 653a4953..000d4bf6 100644
--- a/Readme.md
+++ b/Readme.md
@@ -68,6 +68,7 @@ To learn more about the war and how you can help, [click here](https://tyrrrz.me
- Multiple output formats: HTML (dark/light), TXT, CSV, JSON
- Support for markdown, attachments, embeds, emoji, and other rich media features
- File partitioning, date ranges, message filtering, and other export options
+- Automatic detection and appending of existing exports
- Self-contained exports that can be viewed offline
## Screenshots