diff --git a/API/Controllers/SettingsController.cs b/API/Controllers/SettingsController.cs
index 951416a..d974eef 100644
--- a/API/Controllers/SettingsController.cs
+++ b/API/Controllers/SettingsController.cs
@@ -1,5 +1,6 @@
using API.MangaDownloadClients;
using API.Schema;
+using API.Schema.Jobs;
using Asp.Versioning;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json.Linq;
@@ -199,4 +200,67 @@ public class SettingsController(PgsqlContext context) : Controller
TrangaSettings.UpdateAprilFoolsMode(enabled);
return Ok();
}
+
+ ///
+ /// Gets the Chapter Naming Scheme
+ ///
+ ///
+ /// Placeholders:
+ /// %M Manga Name
+ /// %V Volume
+ /// %C Chapter
+ /// %T Title
+ /// %A Author (first in list)
+ /// %I Chapter Internal ID
+ /// %i Manga Internal ID
+ /// %Y Year (Manga)
+ ///
+ /// ?_(...) replace _ with a value from above:
+ /// Everything inside the braces will only be added if the value of %_ is not null
+ ///
+ ///
+ [HttpGet("ChapterNamingScheme")]
+ [ProducesResponseType(Status200OK, "text/plain")]
+ public IActionResult GetCustomNamingScheme()
+ {
+ return Ok(TrangaSettings.chapterNamingScheme);
+ }
+
+ ///
+ /// Sets the Chapter Naming Scheme
+ ///
+ ///
+ /// Placeholders:
+ /// %M Manga Name
+ /// %V Volume
+ /// %C Chapter
+ /// %T Title
+ /// %A Author (first in list)
+ /// %I Chapter Internal ID
+ /// %i Manga Internal ID
+ /// %Y Year (Manga)
+ ///
+ /// ?_(...) replace _ with a value from above:
+ /// Everything inside the braces will only be added if the value of %_ is not null
+ ///
+ ///
+ /// Error during Database Operation
+ [HttpPatch("ChapterNamingScheme")]
+ [ProducesResponseType(Status200OK)]
+ [ProducesResponseType(Status500InternalServerError, "text/plain")]
+ public IActionResult SetCustomNamingScheme([FromBody]string namingScheme)
+ {
+ try
+ {
+ TrangaSettings.UpdateChapterNamingScheme(namingScheme);
+ MoveFileOrFolderJob[] newJobs =
+ context.Chapters.Where(c => c.Downloaded).Select(c => c.UpdateArchiveFileName()).Where(x => x != null).ToArray()!;
+ context.Jobs.AddRange(newJobs);
+ return Ok();
+ }
+ catch (Exception e)
+ {
+ return StatusCode(500, e);
+ }
+ }
}
\ No newline at end of file
diff --git a/API/Schema/Chapter.cs b/API/Schema/Chapter.cs
index 87e25de..b844c87 100644
--- a/API/Schema/Chapter.cs
+++ b/API/Schema/Chapter.cs
@@ -1,5 +1,7 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
+using System.Text;
+using System.Text.RegularExpressions;
using System.Xml.Linq;
using API.Schema.Jobs;
using Microsoft.EntityFrameworkCore;
@@ -86,7 +88,7 @@ public class Chapter : IComparable
return UpdateArchiveFileName();
}
- private MoveFileOrFolderJob? UpdateArchiveFileName()
+ internal MoveFileOrFolderJob? UpdateArchiveFileName()
{
string? oldPath = FullArchiveFilePath;
if (oldPath is null)
@@ -106,9 +108,76 @@ public class Chapter : IComparable
return File.Exists(path);
}
+ /// Placeholders:
+ /// %M Manga Name
+ /// %V Volume
+ /// %C Chapter
+ /// %T Title
+ /// %A Author (first in list)
+ /// %I Chapter Internal ID
+ /// %i Manga Internal ID
+ /// %Y Year (Manga)
+ private static readonly Regex NullableRex = new(@"\?([a-zA-Z])\(([^\)]*)\)|(.+?)");
+ private static readonly Regex ReplaceRexx = new(@"%([a-zA-Z])|(.+?)");
private string GetArchiveFilePath(string? parentMangaName = null)
{
- return $"{parentMangaName ?? ParentManga?.Name ?? ""} - Vol.{VolumeNumber ?? 0} Ch.{ChapterNumber}{(Title is null ? "" : $" - {Title}")}.cbz";
+ string archiveNamingScheme = TrangaSettings.chapterNamingScheme;
+ StringBuilder stringBuilder = new();
+ foreach (Match nullable in NullableRex.Matches(archiveNamingScheme))
+ {
+ if (nullable.Groups[3].Success)
+ {
+ stringBuilder.Append(nullable.Groups[3].Value);
+ continue;
+ }
+
+ char placeholder = nullable.Groups[1].Value[0];
+ bool isNull = placeholder switch
+ {
+ 'M' => ParentManga?.Name is null && parentMangaName is null,
+ 'V' => VolumeNumber is null,
+ 'C' => ChapterNumber is null,
+ 'T' => Title is null,
+ 'A' => ParentManga?.Authors?.FirstOrDefault()?.AuthorName is null,
+ 'I' => ChapterId is null,
+ 'i' => ParentMangaId is null,
+ 'Y' => ParentManga?.Year is null,
+ _ => true
+ };
+ if(!isNull)
+ stringBuilder.Append(nullable.Groups[2].Value);
+ }
+
+ string checkedString = stringBuilder.ToString();
+ stringBuilder = new();
+
+ foreach (Match replace in ReplaceRexx.Matches(checkedString))
+ {
+ if (replace.Groups[2].Success)
+ {
+ stringBuilder.Append(replace.Groups[2].Value);
+ continue;
+ }
+
+ char placeholder = replace.Groups[1].Value[0];
+ string? value = placeholder switch
+ {
+ 'M' => ParentManga?.Name ?? parentMangaName,
+ 'V' => VolumeNumber?.ToString(),
+ 'C' => ChapterNumber,
+ 'T' => Title,
+ 'A' => ParentManga?.Authors?.FirstOrDefault()?.AuthorName,
+ 'I' => ChapterId,
+ 'i' => ParentMangaId,
+ 'Y' => ParentManga?.Year.ToString(),
+ _ => null
+ };
+ stringBuilder.Append(value);
+ }
+
+ stringBuilder.Append(".cbz");
+
+ return stringBuilder.ToString();
}
private static int CompareChapterNumbers(string ch1, string ch2)
diff --git a/API/TrangaSettings.cs b/API/TrangaSettings.cs
index f0c4e42..5fbbb7a 100644
--- a/API/TrangaSettings.cs
+++ b/API/TrangaSettings.cs
@@ -3,7 +3,6 @@ using API.MangaDownloadClients;
using API.Schema;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
-using static System.IO.UnixFileMode;
namespace API;
@@ -16,6 +15,21 @@ public static class TrangaSettings
public static string userAgent { get; private set; } = DefaultUserAgent;
public static int compression{ get; private set; } = 40;
public static bool bwImages { get; private set; } = false;
+ ///
+ /// Placeholders:
+ /// %M Manga Name
+ /// %V Volume
+ /// %C Chapter
+ /// %T Title
+ /// %A Author (first in list)
+ /// %I Chapter Internal ID
+ /// %i Manga Internal ID
+ /// %Y Year (Manga)
+ ///
+ /// ?_(...) replace _ with a value from above:
+ /// Everything inside the braces will only be added if the value of %_ is not null
+ ///
+ public static string chapterNamingScheme { get; private set; } = "%M - ?V(Vol.%V )Ch.%C?T( - %T)";
[JsonIgnore]
public static string settingsFilePath => Path.Join(workingDirectory, "settings.json");
[JsonIgnore]
@@ -70,42 +84,6 @@ public static class TrangaSettings
ExportSettings();
}
- public static void UpdateDownloadLocation(string newPath, bool moveFiles = true)
- {
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
- Directory.CreateDirectory(newPath, GroupRead | GroupWrite | None | OtherRead | OtherWrite | UserRead | UserWrite);
- else
- Directory.CreateDirectory(newPath);
-
- if (moveFiles)
- MoveContentsOfDirectoryTo(TrangaSettings.downloadLocation, newPath);
-
- TrangaSettings.downloadLocation = newPath;
- ExportSettings();
- }
-
- private static void MoveContentsOfDirectoryTo(string oldDir, string newDir)
- {
- string[] directoryPaths = Directory.GetDirectories(oldDir);
- string[] filePaths = Directory.GetFiles(oldDir);
- foreach (string file in filePaths)
- {
- string newPath = Path.Join(newDir, Path.GetFileName(file));
- File.Move(file, newPath, true);
- }
- foreach(string directory in directoryPaths)
- {
- string? dirName = Path.GetDirectoryName(directory);
- if(dirName is null)
- continue;
- string newPath = Path.Join(newDir, dirName);
- if(Directory.Exists(newPath))
- MoveContentsOfDirectoryTo(directory, newPath);
- else
- Directory.Move(directory, newPath);
- }
- }
-
public static void UpdateUserAgent(string? customUserAgent)
{
userAgent = customUserAgent ?? DefaultUserAgent;
@@ -118,6 +96,12 @@ public static class TrangaSettings
ExportSettings();
}
+ public static void UpdateChapterNamingScheme(string namingScheme)
+ {
+ chapterNamingScheme = namingScheme;
+ ExportSettings();
+ }
+
public static void ResetRequestLimits()
{
requestLimits = DefaultRequestLimits;
@@ -154,7 +138,7 @@ public static class TrangaSettings
public static JObject AsJObject()
{
- JObject jobj = new JObject();
+ JObject jobj = new ();
jobj.Add("downloadLocation", JToken.FromObject(downloadLocation));
jobj.Add("workingDirectory", JToken.FromObject(workingDirectory));
jobj.Add("userAgent", JToken.FromObject(userAgent));
@@ -163,6 +147,7 @@ public static class TrangaSettings
jobj.Add("compression", JToken.FromObject(compression));
jobj.Add("bwImages", JToken.FromObject(bwImages));
jobj.Add("startNewJobTimeoutMs", JToken.FromObject(startNewJobTimeoutMs));
+ jobj.Add("chapterNamingScheme", JToken.FromObject(chapterNamingScheme));
return jobj;
}
@@ -187,5 +172,7 @@ public static class TrangaSettings
bwImages = bwi.Value()!;
if (jobj.TryGetValue("startNewJobTimeoutMs", out JToken? snjt))
startNewJobTimeoutMs = snjt.Value()!;
+ if (jobj.TryGetValue("chapterNamingScheme", out JToken? cns))
+ chapterNamingScheme = cns.Value()!;
}
}
\ No newline at end of file