From 511af1e35cfb4d30e9f0df87f51799978416d2ec Mon Sep 17 00:00:00 2001 From: Tyrrrz Date: Fri, 16 Apr 2021 23:09:08 +0300 Subject: [PATCH] Refactor --- .../Commands/Base/ExportCommandBase.cs | 8 ++-- .../Commands/Base/PartitionConverter.cs | 28 ------------ .../Commands/ExportChannelsCommand.cs | 2 +- .../DiscordChatExporter.Cli.csproj | 1 - .../Discord/Data/Attachment.cs | 1 - .../Discord/Data/Common/FileSize.cs | 36 +++++++++++++++- DiscordChatExporter.Core/Discord/Snowflake.cs | 2 +- .../DiscordChatExporter.Core.csproj | 1 - .../Exporting/ExportRequest.cs | 7 +-- .../Exporting/MessageExporter.cs | 25 ++--------- .../Partitioners/ExportPartitioningContext.cs | 18 -------- .../Partitioners/FileSizePartitioner.cs | 21 --------- .../Exporting/Partitioners/IPartitioner.cs | 12 ------ .../Partitioners/MessageCountPartitioner.cs | 25 ----------- .../Exporting/Partitioners/NullPartitioner.cs | 16 ------- .../Partitioning/FileSizePartitionLimit.cs | 12 ++++++ .../MessageCountPartitionLimit.cs | 12 ++++++ .../Partitioning/NullPartitionLimit.cs | 9 ++++ .../Exporting/Partitioning/PartitionLimit.cs | 22 ++++++++++ .../Exporting/Writers/CsvMessageWriter.cs | 2 + .../Writers/Html/PostambleTemplate.cshtml | 4 +- .../Writers/Html/PostambleTemplateContext.cs | 15 +++++++ .../Writers/Html/PreambleTemplate.cshtml | 2 +- ...eContext.cs => PreambleTemplateContext.cs} | 7 +-- .../Exporting/Writers/HtmlMessageWriter.cs | 11 ++--- .../Exporting/Writers/JsonMessageWriter.cs | 8 ++-- .../Exporting/Writers/MessageWriter.cs | 12 ++++-- .../Writers/PlainTextMessageWriter.cs | 11 ++--- .../ExportFormatToStringConverter.cs | 2 +- .../PartitionFormatToStringConverter.cs | 27 ------------ .../PartitionFormatToTextBoxHintConverter.cs | 33 -------------- .../PartitionFormatToTooltipConverter.cs | 33 -------------- .../Services/SettingsService.cs | 6 +-- .../Utils/PartitionFormat.cs | 22 ---------- .../Dialogs/ExportSetupViewModel.cs | 17 ++------ .../ViewModels/RootViewModel.cs | 35 ++++++--------- .../Views/Dialogs/ExportSetupView.xaml | 43 +++---------------- .../Views/Dialogs/ExportSetupView.xaml.cs | 2 +- 38 files changed, 173 insertions(+), 377 deletions(-) delete mode 100644 DiscordChatExporter.Cli/Commands/Base/PartitionConverter.cs delete mode 100644 DiscordChatExporter.Core/Exporting/Partitioners/ExportPartitioningContext.cs delete mode 100644 DiscordChatExporter.Core/Exporting/Partitioners/FileSizePartitioner.cs delete mode 100644 DiscordChatExporter.Core/Exporting/Partitioners/IPartitioner.cs delete mode 100644 DiscordChatExporter.Core/Exporting/Partitioners/MessageCountPartitioner.cs delete mode 100644 DiscordChatExporter.Core/Exporting/Partitioners/NullPartitioner.cs create mode 100644 DiscordChatExporter.Core/Exporting/Partitioning/FileSizePartitionLimit.cs create mode 100644 DiscordChatExporter.Core/Exporting/Partitioning/MessageCountPartitionLimit.cs create mode 100644 DiscordChatExporter.Core/Exporting/Partitioning/NullPartitionLimit.cs create mode 100644 DiscordChatExporter.Core/Exporting/Partitioning/PartitionLimit.cs create mode 100644 DiscordChatExporter.Core/Exporting/Writers/Html/PostambleTemplateContext.cs rename DiscordChatExporter.Core/Exporting/Writers/Html/{LayoutTemplateContext.cs => PreambleTemplateContext.cs} (53%) delete mode 100644 DiscordChatExporter.Gui/Converters/PartitionFormatToStringConverter.cs delete mode 100644 DiscordChatExporter.Gui/Converters/PartitionFormatToTextBoxHintConverter.cs delete mode 100644 DiscordChatExporter.Gui/Converters/PartitionFormatToTooltipConverter.cs delete mode 100644 DiscordChatExporter.Gui/Utils/PartitionFormat.cs diff --git a/DiscordChatExporter.Cli/Commands/Base/ExportCommandBase.cs b/DiscordChatExporter.Cli/Commands/Base/ExportCommandBase.cs index d4ff651..25356cb 100644 --- a/DiscordChatExporter.Cli/Commands/Base/ExportCommandBase.cs +++ b/DiscordChatExporter.Cli/Commands/Base/ExportCommandBase.cs @@ -12,6 +12,7 @@ using DiscordChatExporter.Core.Discord; using DiscordChatExporter.Core.Discord.Data; using DiscordChatExporter.Core.Exceptions; using DiscordChatExporter.Core.Exporting; +using DiscordChatExporter.Core.Exporting.Partitioning; using DiscordChatExporter.Core.Utils.Extensions; using Tyrrrz.Extensions; @@ -31,9 +32,8 @@ namespace DiscordChatExporter.Cli.Commands.Base [CommandOption("before", Description = "Only include messages sent before this date or message ID.")] public Snowflake? Before { get; init; } - [CommandOption("partition", 'p', Converter = typeof(PartitionConverter), - Description = "Split output into partitions limited to this number of messages or a maximum file size (e.g. \"25mb\").")] - public IPartitioner Partitoner { get; init; } = new NullPartitioner(); + [CommandOption("partition", 'p', Description = "Split output into partitions, each limited to this number of message (e.g. 100) or file size (e.g. 10mb).")] + public PartitionLimit PartitionLimit { get; init; } = NullPartitionLimit.Instance; [CommandOption("parallel", Description = "Limits how many channels can be exported in parallel.")] public int ParallelLimit { get; init; } = 1; @@ -75,7 +75,7 @@ namespace DiscordChatExporter.Cli.Commands.Base ExportFormat, After, Before, - Partitoner, + PartitionLimit, ShouldDownloadMedia, ShouldReuseMedia, DateFormat diff --git a/DiscordChatExporter.Cli/Commands/Base/PartitionConverter.cs b/DiscordChatExporter.Cli/Commands/Base/PartitionConverter.cs deleted file mode 100644 index bac1007..0000000 --- a/DiscordChatExporter.Cli/Commands/Base/PartitionConverter.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using ByteSizeLib; -using CliFx.Extensibility; -using DiscordChatExporter.Core; -using DiscordChatExporter.Core.Exporting; - -namespace DiscordChatExporter.Cli.Commands.Base -{ - public class PartitionConverter : BindingConverter - { - public override IPartitioner Convert(string? rawValue) - { - if (rawValue == null) return new NullPartitioner(); - - if (ByteSize.TryParse(rawValue, out ByteSize filesize)) - { - return new FileSizePartitioner((long)filesize.Bytes); - } - else - { - int messageLimit = int.Parse(rawValue); - return new MessageCountPartitioner(messageLimit); - } - } - } -} diff --git a/DiscordChatExporter.Cli/Commands/ExportChannelsCommand.cs b/DiscordChatExporter.Cli/Commands/ExportChannelsCommand.cs index 5c219b8..33c71b4 100644 --- a/DiscordChatExporter.Cli/Commands/ExportChannelsCommand.cs +++ b/DiscordChatExporter.Cli/Commands/ExportChannelsCommand.cs @@ -12,7 +12,7 @@ namespace DiscordChatExporter.Cli.Commands [Command("export", Description = "Export one or multiple channels.")] public class ExportChannelsCommand : ExportCommandBase { - // TODO: change this to plural with a breaking change + // TODO: change this to plural (breaking change) [CommandOption("channel", 'c', IsRequired = true, Description = "Channel ID(s).")] public IReadOnlyList ChannelIds { get; init; } = Array.Empty(); diff --git a/DiscordChatExporter.Cli/DiscordChatExporter.Cli.csproj b/DiscordChatExporter.Cli/DiscordChatExporter.Cli.csproj index 0f79b9b..c98f66c 100644 --- a/DiscordChatExporter.Cli/DiscordChatExporter.Cli.csproj +++ b/DiscordChatExporter.Cli/DiscordChatExporter.Cli.csproj @@ -9,7 +9,6 @@ - diff --git a/DiscordChatExporter.Core/Discord/Data/Attachment.cs b/DiscordChatExporter.Core/Discord/Data/Attachment.cs index 9a445a2..799b344 100644 --- a/DiscordChatExporter.Core/Discord/Data/Attachment.cs +++ b/DiscordChatExporter.Core/Discord/Data/Attachment.cs @@ -5,7 +5,6 @@ using System.Text.Json; using DiscordChatExporter.Core.Discord.Data.Common; using DiscordChatExporter.Core.Utils.Extensions; using JsonExtensions.Reading; -using FileSize = DiscordChatExporter.Core.Discord.Data.Common.FileSize; namespace DiscordChatExporter.Core.Discord.Data { diff --git a/DiscordChatExporter.Core/Discord/Data/Common/FileSize.cs b/DiscordChatExporter.Core/Discord/Data/Common/FileSize.cs index 5141bb9..0335ceb 100644 --- a/DiscordChatExporter.Core/Discord/Data/Common/FileSize.cs +++ b/DiscordChatExporter.Core/Discord/Data/Common/FileSize.cs @@ -1,4 +1,6 @@ using System; +using System.Globalization; +using System.Text.RegularExpressions; namespace DiscordChatExporter.Core.Discord.Data.Common { @@ -61,5 +63,37 @@ namespace DiscordChatExporter.Core.Discord.Data.Common public partial struct FileSize { public static FileSize FromBytes(long bytes) => new(bytes); + + public static FileSize? TryParse(string value) + { + var match = Regex.Match(value, @"^(\d+[\.,]?\d*)\s*(\w)?b$", RegexOptions.IgnoreCase); + + // Number part + if (!double.TryParse( + match.Groups[1].Value, + NumberStyles.Float, + CultureInfo.InvariantCulture, + out var number)) + { + return null; + } + + // Magnitude part + var magnitude = match.Groups[2].Value.ToUpperInvariant() switch + { + "G" => 1_000_000_000, + "M" => 1_000_000, + "K" => 1_000, + "" => 1, + _ => -1 + }; + + if (magnitude < 0) + { + return null; + } + + return FromBytes((long) (number * magnitude)); + } } -} +} \ No newline at end of file diff --git a/DiscordChatExporter.Core/Discord/Snowflake.cs b/DiscordChatExporter.Core/Discord/Snowflake.cs index 34037b2..b7c15e6 100644 --- a/DiscordChatExporter.Core/Discord/Snowflake.cs +++ b/DiscordChatExporter.Core/Discord/Snowflake.cs @@ -48,7 +48,7 @@ namespace DiscordChatExporter.Core.Discord } public static Snowflake Parse(string str, IFormatProvider? formatProvider) => - TryParse(str, formatProvider) ?? throw new FormatException($"Invalid snowflake: {str}."); + TryParse(str, formatProvider) ?? throw new FormatException($"Invalid snowflake '{str}'."); public static Snowflake Parse(string str) => Parse(str, null); } diff --git a/DiscordChatExporter.Core/DiscordChatExporter.Core.csproj b/DiscordChatExporter.Core/DiscordChatExporter.Core.csproj index acda34c..b3b8fc8 100644 --- a/DiscordChatExporter.Core/DiscordChatExporter.Core.csproj +++ b/DiscordChatExporter.Core/DiscordChatExporter.Core.csproj @@ -5,7 +5,6 @@ - diff --git a/DiscordChatExporter.Core/Exporting/ExportRequest.cs b/DiscordChatExporter.Core/Exporting/ExportRequest.cs index d71aa5f..319d451 100644 --- a/DiscordChatExporter.Core/Exporting/ExportRequest.cs +++ b/DiscordChatExporter.Core/Exporting/ExportRequest.cs @@ -4,6 +4,7 @@ using System.Text; using System.Text.RegularExpressions; using DiscordChatExporter.Core.Discord; using DiscordChatExporter.Core.Discord.Data; +using DiscordChatExporter.Core.Exporting.Partitioning; using DiscordChatExporter.Core.Utils; namespace DiscordChatExporter.Core.Exporting @@ -28,7 +29,7 @@ namespace DiscordChatExporter.Core.Exporting public Snowflake? Before { get; } - public IPartitioner Partitoner { get; } + public PartitionLimit PartitionLimit { get; } public bool ShouldDownloadMedia { get; } @@ -43,7 +44,7 @@ namespace DiscordChatExporter.Core.Exporting ExportFormat format, Snowflake? after, Snowflake? before, - IPartitioner partitioner, + PartitionLimit partitionLimit, bool shouldDownloadMedia, bool shouldReuseMedia, string dateFormat) @@ -54,7 +55,7 @@ namespace DiscordChatExporter.Core.Exporting Format = format; After = after; Before = before; - Partitoner = partitioner; + PartitionLimit = partitionLimit; ShouldDownloadMedia = shouldDownloadMedia; ShouldReuseMedia = shouldReuseMedia; DateFormat = dateFormat; diff --git a/DiscordChatExporter.Core/Exporting/MessageExporter.cs b/DiscordChatExporter.Core/Exporting/MessageExporter.cs index 66cd503..4743a45 100644 --- a/DiscordChatExporter.Core/Exporting/MessageExporter.cs +++ b/DiscordChatExporter.Core/Exporting/MessageExporter.cs @@ -1,21 +1,15 @@ using System; using System.IO; -using System.Text.RegularExpressions; using System.Threading.Tasks; -using ByteSizeLib; using DiscordChatExporter.Core.Discord.Data; -using DiscordChatExporter.Core.Exporting; -using DiscordChatExporter.Core.Exporting.Partitioners; using DiscordChatExporter.Core.Exporting.Writers; namespace DiscordChatExporter.Core.Exporting { internal partial class MessageExporter : IAsyncDisposable { - private readonly ExportContext _context; - private long _messageCount; private int _partitionIndex; private MessageWriter? _writer; @@ -24,17 +18,6 @@ namespace DiscordChatExporter.Core.Exporting _context = context; } - private bool IsPartitionLimitReached() - { - if (_writer is null) - { - return false; - } - - return _context.Request.Partitoner.IsLimitReached( - new ExportPartitioningContext(_messageCount, _writer.SizeInBytes)); - } - private async ValueTask ResetWriterAsync() { if (_writer is not null) @@ -48,7 +31,8 @@ namespace DiscordChatExporter.Core.Exporting private async ValueTask GetWriterAsync() { // Ensure partition limit has not been exceeded - if (_writer != null && IsPartitionLimitReached()) + if (_writer is not null && + _context.Request.PartitionLimit.IsReached(_writer.MessagesWritten, _writer.BytesWritten)) { await ResetWriterAsync(); _partitionIndex++; @@ -74,7 +58,6 @@ namespace DiscordChatExporter.Core.Exporting { var writer = await GetWriterAsync(); await writer.WriteMessageAsync(message); - _messageCount++; } public async ValueTask DisposeAsync() => await ResetWriterAsync(); @@ -82,9 +65,7 @@ namespace DiscordChatExporter.Core.Exporting internal partial class MessageExporter { - private static string GetPartitionFilePath( - string baseFilePath, - int partitionIndex) + private static string GetPartitionFilePath(string baseFilePath, int partitionIndex) { // First partition - don't change file name if (partitionIndex <= 0) diff --git a/DiscordChatExporter.Core/Exporting/Partitioners/ExportPartitioningContext.cs b/DiscordChatExporter.Core/Exporting/Partitioners/ExportPartitioningContext.cs deleted file mode 100644 index 5deb81e..0000000 --- a/DiscordChatExporter.Core/Exporting/Partitioners/ExportPartitioningContext.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace DiscordChatExporter.Core.Exporting.Partitioners -{ - public class ExportPartitioningContext - { - public long MessageCount { get; } - public long SizeInBytes { get; } - - public ExportPartitioningContext(long messageCount, long sizeInBytes) - { - MessageCount = messageCount; - SizeInBytes = sizeInBytes; - } - } -} diff --git a/DiscordChatExporter.Core/Exporting/Partitioners/FileSizePartitioner.cs b/DiscordChatExporter.Core/Exporting/Partitioners/FileSizePartitioner.cs deleted file mode 100644 index b1b8f17..0000000 --- a/DiscordChatExporter.Core/Exporting/Partitioners/FileSizePartitioner.cs +++ /dev/null @@ -1,21 +0,0 @@ -using DiscordChatExporter.Core.Exporting.Partitioners; -using System; -using System.Collections.Generic; -using System.Text; - -namespace DiscordChatExporter.Core.Exporting -{ - public class FileSizePartitioner : IPartitioner - { - private long _bytesPerFile; - - public FileSizePartitioner(long bytesPerFile) - { - _bytesPerFile = bytesPerFile; - } - public bool IsLimitReached(ExportPartitioningContext context) - { - return context.SizeInBytes >= _bytesPerFile; - } - } -} diff --git a/DiscordChatExporter.Core/Exporting/Partitioners/IPartitioner.cs b/DiscordChatExporter.Core/Exporting/Partitioners/IPartitioner.cs deleted file mode 100644 index 47bc5e2..0000000 --- a/DiscordChatExporter.Core/Exporting/Partitioners/IPartitioner.cs +++ /dev/null @@ -1,12 +0,0 @@ -using DiscordChatExporter.Core.Exporting.Partitioners; -using System; -using System.Collections.Generic; -using System.Text; - -namespace DiscordChatExporter.Core.Exporting -{ - public interface IPartitioner - { - bool IsLimitReached(ExportPartitioningContext context); - } -} diff --git a/DiscordChatExporter.Core/Exporting/Partitioners/MessageCountPartitioner.cs b/DiscordChatExporter.Core/Exporting/Partitioners/MessageCountPartitioner.cs deleted file mode 100644 index 53e0737..0000000 --- a/DiscordChatExporter.Core/Exporting/Partitioners/MessageCountPartitioner.cs +++ /dev/null @@ -1,25 +0,0 @@ -using DiscordChatExporter.Core.Exporting.Partitioners; -using System; -using System.Collections.Generic; -using System.Text; - -namespace DiscordChatExporter.Core.Exporting -{ - public class MessageCountPartitioner : IPartitioner - { - - private int _messagesPerPartition; - - public MessageCountPartitioner(int messagesPerPartition) - { - _messagesPerPartition = messagesPerPartition; - } - - public bool IsLimitReached(ExportPartitioningContext context) - { - return context.MessageCount > 0 && - _messagesPerPartition != 0 && - context.MessageCount % _messagesPerPartition == 0; - } - } -} diff --git a/DiscordChatExporter.Core/Exporting/Partitioners/NullPartitioner.cs b/DiscordChatExporter.Core/Exporting/Partitioners/NullPartitioner.cs deleted file mode 100644 index dc3de63..0000000 --- a/DiscordChatExporter.Core/Exporting/Partitioners/NullPartitioner.cs +++ /dev/null @@ -1,16 +0,0 @@ -using DiscordChatExporter.Core.Exporting; -using DiscordChatExporter.Core.Exporting.Partitioners; -using System; -using System.Collections.Generic; -using System.Text; - -namespace DiscordChatExporter.Core.Exporting -{ - public class NullPartitioner : IPartitioner - { - public bool IsLimitReached(ExportPartitioningContext context) - { - return false; - } - } -} diff --git a/DiscordChatExporter.Core/Exporting/Partitioning/FileSizePartitionLimit.cs b/DiscordChatExporter.Core/Exporting/Partitioning/FileSizePartitionLimit.cs new file mode 100644 index 0000000..1baffbb --- /dev/null +++ b/DiscordChatExporter.Core/Exporting/Partitioning/FileSizePartitionLimit.cs @@ -0,0 +1,12 @@ +namespace DiscordChatExporter.Core.Exporting.Partitioning +{ + public class FileSizePartitionLimit : PartitionLimit + { + private readonly long _limit; + + public FileSizePartitionLimit(long limit) => _limit = limit; + + public override bool IsReached(long messagesWritten, long bytesWritten) => + bytesWritten >= _limit; + } +} \ No newline at end of file diff --git a/DiscordChatExporter.Core/Exporting/Partitioning/MessageCountPartitionLimit.cs b/DiscordChatExporter.Core/Exporting/Partitioning/MessageCountPartitionLimit.cs new file mode 100644 index 0000000..318ffc6 --- /dev/null +++ b/DiscordChatExporter.Core/Exporting/Partitioning/MessageCountPartitionLimit.cs @@ -0,0 +1,12 @@ +namespace DiscordChatExporter.Core.Exporting.Partitioning +{ + public class MessageCountPartitionLimit : PartitionLimit + { + private readonly long _limit; + + public MessageCountPartitionLimit(long limit) => _limit = limit; + + public override bool IsReached(long messagesWritten, long bytesWritten) => + messagesWritten >= _limit; + } +} \ No newline at end of file diff --git a/DiscordChatExporter.Core/Exporting/Partitioning/NullPartitionLimit.cs b/DiscordChatExporter.Core/Exporting/Partitioning/NullPartitionLimit.cs new file mode 100644 index 0000000..d38546f --- /dev/null +++ b/DiscordChatExporter.Core/Exporting/Partitioning/NullPartitionLimit.cs @@ -0,0 +1,9 @@ +namespace DiscordChatExporter.Core.Exporting.Partitioning +{ + public class NullPartitionLimit : PartitionLimit + { + public static NullPartitionLimit Instance { get; } = new(); + + public override bool IsReached(long messagesWritten, long bytesWritten) => false; + } +} \ No newline at end of file diff --git a/DiscordChatExporter.Core/Exporting/Partitioning/PartitionLimit.cs b/DiscordChatExporter.Core/Exporting/Partitioning/PartitionLimit.cs new file mode 100644 index 0000000..3b8eceb --- /dev/null +++ b/DiscordChatExporter.Core/Exporting/Partitioning/PartitionLimit.cs @@ -0,0 +1,22 @@ +using DiscordChatExporter.Core.Discord.Data.Common; + +namespace DiscordChatExporter.Core.Exporting.Partitioning +{ + public abstract partial class PartitionLimit + { + public abstract bool IsReached(long messagesWritten, long bytesWritten); + } + + public partial class PartitionLimit + { + public static PartitionLimit Parse(string value) + { + var fileSize = FileSize.TryParse(value); + if (fileSize is not null) + return new FileSizePartitionLimit(fileSize.Value.TotalBytes); + + var messageCount = int.Parse(value); + return new MessageCountPartitionLimit(messageCount); + } + } +} \ No newline at end of file diff --git a/DiscordChatExporter.Core/Exporting/Writers/CsvMessageWriter.cs b/DiscordChatExporter.Core/Exporting/Writers/CsvMessageWriter.cs index 7729ef6..0b8ce54 100644 --- a/DiscordChatExporter.Core/Exporting/Writers/CsvMessageWriter.cs +++ b/DiscordChatExporter.Core/Exporting/Writers/CsvMessageWriter.cs @@ -58,6 +58,8 @@ namespace DiscordChatExporter.Core.Exporting.Writers public override async ValueTask WriteMessageAsync(Message message) { + await base.WriteMessageAsync(message); + // Author ID await _writer.WriteAsync(CsvEncode(message.Author.Id.ToString())); await _writer.WriteAsync(','); diff --git a/DiscordChatExporter.Core/Exporting/Writers/Html/PostambleTemplate.cshtml b/DiscordChatExporter.Core/Exporting/Writers/Html/PostambleTemplate.cshtml index 1a276a1..a65ff0c 100644 --- a/DiscordChatExporter.Core/Exporting/Writers/Html/PostambleTemplate.cshtml +++ b/DiscordChatExporter.Core/Exporting/Writers/Html/PostambleTemplate.cshtml @@ -1,10 +1,10 @@ @namespace DiscordChatExporter.Core.Exporting.Writers.Html -@inherits MiniRazor.TemplateBase +@inherits MiniRazor.TemplateBase
-
Exported @Model.MessageCount.ToString("N0") message(s)
+
Exported @Model.MessagesWritten.ToString("N0") message(s)
diff --git a/DiscordChatExporter.Core/Exporting/Writers/Html/PostambleTemplateContext.cs b/DiscordChatExporter.Core/Exporting/Writers/Html/PostambleTemplateContext.cs new file mode 100644 index 0000000..79b3b02 --- /dev/null +++ b/DiscordChatExporter.Core/Exporting/Writers/Html/PostambleTemplateContext.cs @@ -0,0 +1,15 @@ +namespace DiscordChatExporter.Core.Exporting.Writers.Html +{ + internal class PostambleTemplateContext + { + public ExportContext ExportContext { get; } + + public long MessagesWritten { get; } + + public PostambleTemplateContext(ExportContext exportContext, long messagesWritten) + { + ExportContext = exportContext; + MessagesWritten = messagesWritten; + } + } +} \ No newline at end of file diff --git a/DiscordChatExporter.Core/Exporting/Writers/Html/PreambleTemplate.cshtml b/DiscordChatExporter.Core/Exporting/Writers/Html/PreambleTemplate.cshtml index fe20df9..517373f 100644 --- a/DiscordChatExporter.Core/Exporting/Writers/Html/PreambleTemplate.cshtml +++ b/DiscordChatExporter.Core/Exporting/Writers/Html/PreambleTemplate.cshtml @@ -2,7 +2,7 @@ @using System.Threading.Tasks @using Tyrrrz.Extensions @namespace DiscordChatExporter.Core.Exporting.Writers.Html -@inherits MiniRazor.TemplateBase +@inherits MiniRazor.TemplateBase @{ string FormatDate(DateTimeOffset date) => Model.ExportContext.FormatDate(date); diff --git a/DiscordChatExporter.Core/Exporting/Writers/Html/LayoutTemplateContext.cs b/DiscordChatExporter.Core/Exporting/Writers/Html/PreambleTemplateContext.cs similarity index 53% rename from DiscordChatExporter.Core/Exporting/Writers/Html/LayoutTemplateContext.cs rename to DiscordChatExporter.Core/Exporting/Writers/Html/PreambleTemplateContext.cs index 4f5f59d..6dc335b 100644 --- a/DiscordChatExporter.Core/Exporting/Writers/Html/LayoutTemplateContext.cs +++ b/DiscordChatExporter.Core/Exporting/Writers/Html/PreambleTemplateContext.cs @@ -1,18 +1,15 @@ namespace DiscordChatExporter.Core.Exporting.Writers.Html { - internal class LayoutTemplateContext + internal class PreambleTemplateContext { public ExportContext ExportContext { get; } public string ThemeName { get; } - public long MessageCount { get; } - - public LayoutTemplateContext(ExportContext exportContext, string themeName, long messageCount) + public PreambleTemplateContext(ExportContext exportContext, string themeName) { ExportContext = exportContext; ThemeName = themeName; - MessageCount = messageCount; } } } \ No newline at end of file diff --git a/DiscordChatExporter.Core/Exporting/Writers/HtmlMessageWriter.cs b/DiscordChatExporter.Core/Exporting/Writers/HtmlMessageWriter.cs index cde055f..c547029 100644 --- a/DiscordChatExporter.Core/Exporting/Writers/HtmlMessageWriter.cs +++ b/DiscordChatExporter.Core/Exporting/Writers/HtmlMessageWriter.cs @@ -14,8 +14,6 @@ namespace DiscordChatExporter.Core.Exporting.Writers private readonly List _messageGroupBuffer = new(); - private long _messageCount; - public HtmlMessageWriter(Stream stream, ExportContext context, string themeName) : base(stream, context) { @@ -25,7 +23,7 @@ namespace DiscordChatExporter.Core.Exporting.Writers public override async ValueTask WritePreambleAsync() { - var templateContext = new LayoutTemplateContext(Context, _themeName, _messageCount); + var templateContext = new PreambleTemplateContext(Context, _themeName); await _writer.WriteLineAsync( await PreambleTemplate.RenderAsync(templateContext) @@ -43,6 +41,8 @@ namespace DiscordChatExporter.Core.Exporting.Writers public override async ValueTask WriteMessageAsync(Message message) { + await base.WriteMessageAsync(message); + // If message group is empty or the given message can be grouped, buffer the given message if (!_messageGroupBuffer.Any() || MessageGroup.CanJoin(_messageGroupBuffer.Last(), message)) { @@ -56,9 +56,6 @@ namespace DiscordChatExporter.Core.Exporting.Writers _messageGroupBuffer.Clear(); _messageGroupBuffer.Add(message); } - - // Increment message count - _messageCount++; } public override async ValueTask WritePostambleAsync() @@ -67,7 +64,7 @@ namespace DiscordChatExporter.Core.Exporting.Writers if (_messageGroupBuffer.Any()) await WriteMessageGroupAsync(MessageGroup.Join(_messageGroupBuffer)); - var templateContext = new LayoutTemplateContext(Context, _themeName, _messageCount); + var templateContext = new PostambleTemplateContext(Context, MessagesWritten); await _writer.WriteLineAsync( await PostambleTemplate.RenderAsync(templateContext) diff --git a/DiscordChatExporter.Core/Exporting/Writers/JsonMessageWriter.cs b/DiscordChatExporter.Core/Exporting/Writers/JsonMessageWriter.cs index 0ec3428..b193da5 100644 --- a/DiscordChatExporter.Core/Exporting/Writers/JsonMessageWriter.cs +++ b/DiscordChatExporter.Core/Exporting/Writers/JsonMessageWriter.cs @@ -13,8 +13,6 @@ namespace DiscordChatExporter.Core.Exporting.Writers { private readonly Utf8JsonWriter _writer; - private long _messageCount; - public JsonMessageWriter(Stream stream, ExportContext context) : base(stream, context) { @@ -211,6 +209,8 @@ namespace DiscordChatExporter.Core.Exporting.Writers public override async ValueTask WriteMessageAsync(Message message) { + await base.WriteMessageAsync(message); + _writer.WriteStartObject(); // Metadata @@ -279,8 +279,6 @@ namespace DiscordChatExporter.Core.Exporting.Writers _writer.WriteEndObject(); await _writer.FlushAsync(); - - _messageCount++; } public override async ValueTask WritePostambleAsync() @@ -288,7 +286,7 @@ namespace DiscordChatExporter.Core.Exporting.Writers // Message array (end) _writer.WriteEndArray(); - _writer.WriteNumber("messageCount", _messageCount); + _writer.WriteNumber("messageCount", MessagesWritten); // Root object (end) _writer.WriteEndObject(); diff --git a/DiscordChatExporter.Core/Exporting/Writers/MessageWriter.cs b/DiscordChatExporter.Core/Exporting/Writers/MessageWriter.cs index f8ca7ad..e5cf49b 100644 --- a/DiscordChatExporter.Core/Exporting/Writers/MessageWriter.cs +++ b/DiscordChatExporter.Core/Exporting/Writers/MessageWriter.cs @@ -11,17 +11,23 @@ namespace DiscordChatExporter.Core.Exporting.Writers protected ExportContext Context { get; } + public long MessagesWritten { get; private set; } + + public long BytesWritten => Stream.Length; + protected MessageWriter(Stream stream, ExportContext context) { Stream = stream; Context = context; } - public long SizeInBytes => Stream.Length; - public virtual ValueTask WritePreambleAsync() => default; - public abstract ValueTask WriteMessageAsync(Message message); + public virtual ValueTask WriteMessageAsync(Message message) + { + MessagesWritten++; + return default; + } public virtual ValueTask WritePostambleAsync() => default; diff --git a/DiscordChatExporter.Core/Exporting/Writers/PlainTextMessageWriter.cs b/DiscordChatExporter.Core/Exporting/Writers/PlainTextMessageWriter.cs index b33287b..fe9f3b9 100644 --- a/DiscordChatExporter.Core/Exporting/Writers/PlainTextMessageWriter.cs +++ b/DiscordChatExporter.Core/Exporting/Writers/PlainTextMessageWriter.cs @@ -12,8 +12,6 @@ namespace DiscordChatExporter.Core.Exporting.Writers { private readonly TextWriter _writer; - private long _messageCount; - public PlainTextMessageWriter(Stream stream, ExportContext context) : base(stream, context) { @@ -130,26 +128,29 @@ namespace DiscordChatExporter.Core.Exporting.Writers public override async ValueTask WriteMessageAsync(Message message) { + await base.WriteMessageAsync(message); + + // Header await WriteMessageHeaderAsync(message); + // Content if (!string.IsNullOrWhiteSpace(message.Content)) await _writer.WriteLineAsync(FormatMarkdown(message.Content)); await _writer.WriteLineAsync(); + // Attachments, embeds, reactions await WriteAttachmentsAsync(message.Attachments); await WriteEmbedsAsync(message.Embeds); await WriteReactionsAsync(message.Reactions); await _writer.WriteLineAsync(); - - _messageCount++; } public override async ValueTask WritePostambleAsync() { await _writer.WriteLineAsync('='.Repeat(62)); - await _writer.WriteLineAsync($"Exported {_messageCount:N0} message(s)"); + await _writer.WriteLineAsync($"Exported {MessagesWritten:N0} message(s)"); await _writer.WriteLineAsync('='.Repeat(62)); } diff --git a/DiscordChatExporter.Gui/Converters/ExportFormatToStringConverter.cs b/DiscordChatExporter.Gui/Converters/ExportFormatToStringConverter.cs index d910530..99563b4 100644 --- a/DiscordChatExporter.Gui/Converters/ExportFormatToStringConverter.cs +++ b/DiscordChatExporter.Gui/Converters/ExportFormatToStringConverter.cs @@ -15,7 +15,7 @@ namespace DiscordChatExporter.Gui.Converters if (value is ExportFormat exportFormatValue) return exportFormatValue.GetDisplayName(); - return default(string); + return default(string?); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => diff --git a/DiscordChatExporter.Gui/Converters/PartitionFormatToStringConverter.cs b/DiscordChatExporter.Gui/Converters/PartitionFormatToStringConverter.cs deleted file mode 100644 index 8fafab5..0000000 --- a/DiscordChatExporter.Gui/Converters/PartitionFormatToStringConverter.cs +++ /dev/null @@ -1,27 +0,0 @@ -using DiscordChatExporter.Core.Exporting; -using DiscordChatExporter.Gui.Internal; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Text; -using System.Windows.Data; - -namespace DiscordChatExporter.Gui.Converters -{ - [ValueConversion(typeof(ExportFormat), typeof(string))] - public class PartitionFormatToStringConverter : IValueConverter - { - public static PartitionFormatToStringConverter Instance { get; } = new(); - - public object? Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - if (value is PartitionFormat partitionFormatValue) - return partitionFormatValue.GetDisplayName(); - - return default(string); - } - - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => - throw new NotSupportedException(); - } -} diff --git a/DiscordChatExporter.Gui/Converters/PartitionFormatToTextBoxHintConverter.cs b/DiscordChatExporter.Gui/Converters/PartitionFormatToTextBoxHintConverter.cs deleted file mode 100644 index 86ec281..0000000 --- a/DiscordChatExporter.Gui/Converters/PartitionFormatToTextBoxHintConverter.cs +++ /dev/null @@ -1,33 +0,0 @@ -using DiscordChatExporter.Gui.Internal; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Text; -using System.Windows.Data; - -namespace DiscordChatExporter.Gui.Converters -{ - [ValueConversion(typeof(DateTimeOffset?), typeof(DateTime?))] - public class PartitionFormatToTextBoxHintConverter : IValueConverter - { - public static PartitionFormatToTextBoxHintConverter Instance { get; } = new(); - - public object? Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - if (value is PartitionFormat partitionFormat) - return partitionFormat switch - { - PartitionFormat.FileSize => "MB per partition", - PartitionFormat.MessageCount => "Messages per partition", - _ => default(string) - }; - - return default(DateTime?); - } - - public object? ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - throw new NotSupportedException(); - } - } -} diff --git a/DiscordChatExporter.Gui/Converters/PartitionFormatToTooltipConverter.cs b/DiscordChatExporter.Gui/Converters/PartitionFormatToTooltipConverter.cs deleted file mode 100644 index a4b96aa..0000000 --- a/DiscordChatExporter.Gui/Converters/PartitionFormatToTooltipConverter.cs +++ /dev/null @@ -1,33 +0,0 @@ -using DiscordChatExporter.Gui.Internal; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Text; -using System.Windows.Data; - -namespace DiscordChatExporter.Gui.Converters -{ - [ValueConversion(typeof(DateTimeOffset?), typeof(DateTime?))] - public class PartitionFormatToTooltipConverter : IValueConverter - { - public static PartitionFormatToTextBoxHintConverter Instance { get; } = new(); - - public object? Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - if (value is PartitionFormat partitionFormat) - return partitionFormat switch - { - PartitionFormat.FileSize => "Split output into partitions close to this file size", - PartitionFormat.MessageCount => "Split output into partitions limited to this number of messages", - _ => default(string) - }; - - return default(DateTime?); - } - - public object? ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - throw new NotSupportedException(); - } - } -} diff --git a/DiscordChatExporter.Gui/Services/SettingsService.cs b/DiscordChatExporter.Gui/Services/SettingsService.cs index 9bfa709..06b65f7 100644 --- a/DiscordChatExporter.Gui/Services/SettingsService.cs +++ b/DiscordChatExporter.Gui/Services/SettingsService.cs @@ -1,6 +1,6 @@ using DiscordChatExporter.Core.Discord; using DiscordChatExporter.Core.Exporting; -using DiscordChatExporter.Gui.Internal; +using DiscordChatExporter.Core.Exporting.Partitioning; using Tyrrrz.Settings; namespace DiscordChatExporter.Gui.Services @@ -23,9 +23,7 @@ namespace DiscordChatExporter.Gui.Services public ExportFormat LastExportFormat { get; set; } = ExportFormat.HtmlDark; - public PartitionFormat LastPartitionFormat { get; set; } = PartitionFormat.MessageCount; - - public int? LastPartitionLimit { get; set; } + public string? LastPartitionLimitValue { get; set; } public bool LastShouldDownloadMedia { get; set; } diff --git a/DiscordChatExporter.Gui/Utils/PartitionFormat.cs b/DiscordChatExporter.Gui/Utils/PartitionFormat.cs deleted file mode 100644 index ae36395..0000000 --- a/DiscordChatExporter.Gui/Utils/PartitionFormat.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace DiscordChatExporter.Gui.Internal -{ - public enum PartitionFormat - { - MessageCount, - FileSize, - } - - public static class PartitionFormatExtensions - { - public static string GetDisplayName(this PartitionFormat format) => format switch - { - PartitionFormat.MessageCount => "Message count", - PartitionFormat.FileSize => "File size (MB)", - _ => throw new ArgumentOutOfRangeException(nameof(format)) - }; - } -} diff --git a/DiscordChatExporter.Gui/ViewModels/Dialogs/ExportSetupViewModel.cs b/DiscordChatExporter.Gui/ViewModels/Dialogs/ExportSetupViewModel.cs index 9785b26..654b9cb 100644 --- a/DiscordChatExporter.Gui/ViewModels/Dialogs/ExportSetupViewModel.cs +++ b/DiscordChatExporter.Gui/ViewModels/Dialogs/ExportSetupViewModel.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using DiscordChatExporter.Gui.Internal; using DiscordChatExporter.Core.Discord; using DiscordChatExporter.Core.Discord.Data; using DiscordChatExporter.Core.Exporting; @@ -47,12 +46,7 @@ namespace DiscordChatExporter.Gui.ViewModels.Dialogs public DateTimeOffset? Before => BeforeDate?.Add(BeforeTime ?? TimeSpan.Zero); - public IReadOnlyList AvailablePartitionFormats => - Enum.GetValues(typeof(PartitionFormat)).Cast().ToArray(); - - public PartitionFormat SelectedPartitionFormat { get; set; } - - public int? PartitionLimit { get; set; } + public string? PartitionLimitValue { get; set; } public bool ShouldDownloadMedia { get; set; } @@ -61,7 +55,7 @@ namespace DiscordChatExporter.Gui.ViewModels.Dialogs public bool IsAdvancedSectionDisplayedByDefault => After != default || Before != default || - PartitionLimit != default || + !string.IsNullOrWhiteSpace(PartitionLimitValue) || ShouldDownloadMedia != default; public ExportSetupViewModel(DialogManager dialogManager, SettingsService settingsService) @@ -71,18 +65,15 @@ namespace DiscordChatExporter.Gui.ViewModels.Dialogs // Persist preferences SelectedFormat = _settingsService.LastExportFormat; - PartitionLimit = _settingsService.LastPartitionLimit; + PartitionLimitValue = _settingsService.LastPartitionLimitValue; ShouldDownloadMedia = _settingsService.LastShouldDownloadMedia; - SelectedPartitionFormat = _settingsService.LastPartitionFormat; - } public void Confirm() { // Persist preferences _settingsService.LastExportFormat = SelectedFormat; - _settingsService.LastPartitionLimit = PartitionLimit; - _settingsService.LastPartitionFormat = SelectedPartitionFormat; + _settingsService.LastPartitionLimitValue = PartitionLimitValue; _settingsService.LastShouldDownloadMedia = ShouldDownloadMedia; // If single channel - prompt file path diff --git a/DiscordChatExporter.Gui/ViewModels/RootViewModel.cs b/DiscordChatExporter.Gui/ViewModels/RootViewModel.cs index d839f5c..4ecb095 100644 --- a/DiscordChatExporter.Gui/ViewModels/RootViewModel.cs +++ b/DiscordChatExporter.Gui/ViewModels/RootViewModel.cs @@ -7,8 +7,8 @@ using DiscordChatExporter.Core.Discord; using DiscordChatExporter.Core.Discord.Data; using DiscordChatExporter.Core.Exceptions; using DiscordChatExporter.Core.Exporting; +using DiscordChatExporter.Core.Exporting.Partitioning; using DiscordChatExporter.Core.Utils.Extensions; -using DiscordChatExporter.Gui.Internal; using DiscordChatExporter.Gui.Services; using DiscordChatExporter.Gui.Utils; using DiscordChatExporter.Gui.ViewModels.Dialogs; @@ -65,18 +65,16 @@ namespace DiscordChatExporter.Gui.ViewModels DisplayName = $"{App.Name} v{App.VersionString}"; // Update busy state when progress manager changes - ProgressManager.Bind(o => o.IsActive, - (sender, args) => IsBusy = ProgressManager.IsActive + ProgressManager.Bind(o => o.IsActive, (_, _) => + IsBusy = ProgressManager.IsActive ); - ProgressManager.Bind(o => o.IsActive, - (sender, args) => IsProgressIndeterminate = - ProgressManager.IsActive && ProgressManager.Progress.IsEither(0, 1) + ProgressManager.Bind(o => o.IsActive, (_, _) => + IsProgressIndeterminate = ProgressManager.IsActive && ProgressManager.Progress.IsEither(0, 1) ); - ProgressManager.Bind(o => o.Progress, - (sender, args) => IsProgressIndeterminate = - ProgressManager.IsActive && ProgressManager.Progress.IsEither(0, 1) + ProgressManager.Bind(o => o.Progress, (_, _) => + IsProgressIndeterminate = ProgressManager.IsActive && ProgressManager.Progress.IsEither(0, 1) ); } @@ -207,6 +205,10 @@ namespace DiscordChatExporter.Gui.ViewModels try { + var partitionLimit = !string.IsNullOrWhiteSpace(dialog.PartitionLimitValue) + ? PartitionLimit.Parse(dialog.PartitionLimitValue) + : NullPartitionLimit.Instance; + var request = new ExportRequest( dialog.Guild!, channel!, @@ -214,7 +216,7 @@ namespace DiscordChatExporter.Gui.ViewModels dialog.SelectedFormat, dialog.After?.Pipe(Snowflake.FromDate), dialog.Before?.Pipe(Snowflake.FromDate), - CreatePartitioner(), + partitionLimit, dialog.ShouldDownloadMedia, _settingsService.ShouldReuseMedia, _settingsService.DateFormat @@ -237,19 +239,6 @@ namespace DiscordChatExporter.Gui.ViewModels // Notify of overall completion if (successfulExportCount > 0) Notifications.Enqueue($"Successfully exported {successfulExportCount} channel(s)"); - - IPartitioner CreatePartitioner() - { - var partitionFormat = dialog.SelectedPartitionFormat; - var partitionLimit = dialog.PartitionLimit; - - return (partitionFormat, partitionLimit) switch - { - (PartitionFormat.MessageCount, int messageLimit) => new MessageCountPartitioner(messageLimit), - (PartitionFormat.FileSize, int fileSizeLimit) => new FileSizePartitioner(fileSizeLimit), - _ => new NullPartitioner() - }; - } } } } \ No newline at end of file diff --git a/DiscordChatExporter.Gui/Views/Dialogs/ExportSetupView.xaml b/DiscordChatExporter.Gui/Views/Dialogs/ExportSetupView.xaml index 34d371d..4831bda 100644 --- a/DiscordChatExporter.Gui/Views/Dialogs/ExportSetupView.xaml +++ b/DiscordChatExporter.Gui/Views/Dialogs/ExportSetupView.xaml @@ -126,43 +126,12 @@ - - - - - - - - - - - - - - - - - - - - + diff --git a/DiscordChatExporter.Gui/Views/Dialogs/ExportSetupView.xaml.cs b/DiscordChatExporter.Gui/Views/Dialogs/ExportSetupView.xaml.cs index bd0022f..d6195cd 100644 --- a/DiscordChatExporter.Gui/Views/Dialogs/ExportSetupView.xaml.cs +++ b/DiscordChatExporter.Gui/Views/Dialogs/ExportSetupView.xaml.cs @@ -7,4 +7,4 @@ namespace DiscordChatExporter.Gui.Views.Dialogs InitializeComponent(); } } -} +} \ No newline at end of file