diff --git a/docs/release-notes.md b/docs/release-notes.md
index 8ca2e884..1df0af0e 100644
--- a/docs/release-notes.md
+++ b/docs/release-notes.md
@@ -13,6 +13,7 @@
* Improved translations. Thanks to ChulkyBow (added Ukrainian)!
* For the web UI:
+ * Added log download option.
* Fixed log parser not correctly handling multiple mods having the exact same name.
## 3.13.2
diff --git a/src/SMAPI.Web/Controllers/LogParserController.cs b/src/SMAPI.Web/Controllers/LogParserController.cs
index 39de4b5d..db53d942 100644
--- a/src/SMAPI.Web/Controllers/LogParserController.cs
+++ b/src/SMAPI.Web/Controllers/LogParserController.cs
@@ -1,5 +1,6 @@
using System;
using System.Linq;
+using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using StardewModdingAPI.Toolkit.Utilities;
@@ -39,24 +40,42 @@ namespace StardewModdingAPI.Web.Controllers
***/
/// Render the log parser UI.
/// The stored file ID.
- /// Whether to display the raw unparsed log.
+ /// How to render the log view.
/// Whether to reset the log expiry.
[HttpGet]
[Route("log")]
[Route("log/{id}")]
- public async Task Index(string id = null, bool raw = false, bool renew = false)
+ public async Task Index(string id = null, LogViewFormat format = LogViewFormat.Default, bool renew = false)
{
// fresh page
if (string.IsNullOrWhiteSpace(id))
return this.View("Index", this.GetModel(id));
- // log page
+ // fetch log
StoredFileInfo file = await this.Storage.GetAsync(id, renew);
- ParsedLog log = file.Success
- ? new LogParser().Parse(file.Content)
- : new ParsedLog { IsValid = false, Error = file.Error };
- return this.View("Index", this.GetModel(id, uploadWarning: file.Warning, expiry: file.Expiry).SetResult(log, raw));
+ // render view
+ switch (format)
+ {
+ case LogViewFormat.Default:
+ case LogViewFormat.RawView:
+ {
+ ParsedLog log = file.Success
+ ? new LogParser().Parse(file.Content)
+ : new ParsedLog { IsValid = false, Error = file.Error };
+
+ return this.View("Index", this.GetModel(id, uploadWarning: file.Warning, expiry: file.Expiry).SetResult(log, showRaw: format == LogViewFormat.RawView));
+ }
+
+ case LogViewFormat.RawDownload:
+ {
+ string content = file.Error ?? file.Content;
+ return this.File(Encoding.UTF8.GetBytes(content), "plain/text", $"SMAPI log ({id}).txt");
+ }
+
+ default:
+ throw new InvalidOperationException($"Unknown log view format '{format}'.");
+ }
}
/***
diff --git a/src/SMAPI.Web/ViewModels/LogViewFormat.cs b/src/SMAPI.Web/ViewModels/LogViewFormat.cs
new file mode 100644
index 00000000..7ef79319
--- /dev/null
+++ b/src/SMAPI.Web/ViewModels/LogViewFormat.cs
@@ -0,0 +1,15 @@
+namespace StardewModdingAPI.Web.ViewModels
+{
+ /// How a log file should be displayed.
+ public enum LogViewFormat
+ {
+ /// Render a parsed log and metadata.
+ Default,
+
+ /// Render a raw log with parsed metadata.
+ RawView,
+
+ /// Render directly as a text file.
+ RawDownload
+ }
+}
diff --git a/src/SMAPI.Web/Views/LogParser/Index.cshtml b/src/SMAPI.Web/Views/LogParser/Index.cshtml
index 06d46c9e..eeff776c 100644
--- a/src/SMAPI.Web/Views/LogParser/Index.cshtml
+++ b/src/SMAPI.Web/Views/LogParser/Index.cshtml
@@ -2,6 +2,7 @@
@using StardewModdingAPI.Toolkit.Utilities
@using StardewModdingAPI.Web.Framework
@using StardewModdingAPI.Web.Framework.LogParsing.Models
+@using StardewModdingAPI.Web.ViewModels
@model StardewModdingAPI.Web.ViewModels.LogParserModel
@{
@@ -338,14 +339,24 @@ else if (Model.ParsedLog?.IsValid == true)
}
}
-
- view raw log
}
else
{
@Model.ParsedLog.RawText
- view parsed log
}
+
+
+ @if (Model.ShowRaw)
+ {
+ view parsed log
+ }
+ else
+ {
+ view raw log
+ }
+
+ | download
+
}
else if (Model.ParsedLog?.IsValid == false)