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