mirror of
https://github.com/C9Glax/tranga.git
synced 2025-04-13 03:43:17 +02:00
Custom Chapter-Naming scheme
https://github.com/C9Glax/tranga/issues/92
This commit is contained in:
parent
c94c55300c
commit
9350de0ae9
@ -1,5 +1,6 @@
|
|||||||
using API.MangaDownloadClients;
|
using API.MangaDownloadClients;
|
||||||
using API.Schema;
|
using API.Schema;
|
||||||
|
using API.Schema.Jobs;
|
||||||
using Asp.Versioning;
|
using Asp.Versioning;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
@ -199,4 +200,67 @@ public class SettingsController(PgsqlContext context) : Controller
|
|||||||
TrangaSettings.UpdateAprilFoolsMode(enabled);
|
TrangaSettings.UpdateAprilFoolsMode(enabled);
|
||||||
return Ok();
|
return Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the Chapter Naming Scheme
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// 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
|
||||||
|
/// </remarks>
|
||||||
|
/// <response code="200"></response>
|
||||||
|
[HttpGet("ChapterNamingScheme")]
|
||||||
|
[ProducesResponseType<string>(Status200OK, "text/plain")]
|
||||||
|
public IActionResult GetCustomNamingScheme()
|
||||||
|
{
|
||||||
|
return Ok(TrangaSettings.chapterNamingScheme);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the Chapter Naming Scheme
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// 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
|
||||||
|
/// </remarks>
|
||||||
|
/// <response code="200"></response>
|
||||||
|
/// <response code="500">Error during Database Operation</response>
|
||||||
|
[HttpPatch("ChapterNamingScheme")]
|
||||||
|
[ProducesResponseType(Status200OK)]
|
||||||
|
[ProducesResponseType<string>(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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,5 +1,7 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
using API.Schema.Jobs;
|
using API.Schema.Jobs;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
@ -86,7 +88,7 @@ public class Chapter : IComparable<Chapter>
|
|||||||
return UpdateArchiveFileName();
|
return UpdateArchiveFileName();
|
||||||
}
|
}
|
||||||
|
|
||||||
private MoveFileOrFolderJob? UpdateArchiveFileName()
|
internal MoveFileOrFolderJob? UpdateArchiveFileName()
|
||||||
{
|
{
|
||||||
string? oldPath = FullArchiveFilePath;
|
string? oldPath = FullArchiveFilePath;
|
||||||
if (oldPath is null)
|
if (oldPath is null)
|
||||||
@ -106,9 +108,76 @@ public class Chapter : IComparable<Chapter>
|
|||||||
return File.Exists(path);
|
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)
|
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)
|
private static int CompareChapterNumbers(string ch1, string ch2)
|
||||||
|
@ -3,7 +3,6 @@ using API.MangaDownloadClients;
|
|||||||
using API.Schema;
|
using API.Schema;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using static System.IO.UnixFileMode;
|
|
||||||
|
|
||||||
namespace API;
|
namespace API;
|
||||||
|
|
||||||
@ -16,6 +15,21 @@ public static class TrangaSettings
|
|||||||
public static string userAgent { get; private set; } = DefaultUserAgent;
|
public static string userAgent { get; private set; } = DefaultUserAgent;
|
||||||
public static int compression{ get; private set; } = 40;
|
public static int compression{ get; private set; } = 40;
|
||||||
public static bool bwImages { get; private set; } = false;
|
public static bool bwImages { get; private set; } = false;
|
||||||
|
/// <summary>
|
||||||
|
/// 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
|
||||||
|
/// </summary>
|
||||||
|
public static string chapterNamingScheme { get; private set; } = "%M - ?V(Vol.%V )Ch.%C?T( - %T)";
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public static string settingsFilePath => Path.Join(workingDirectory, "settings.json");
|
public static string settingsFilePath => Path.Join(workingDirectory, "settings.json");
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
@ -70,42 +84,6 @@ public static class TrangaSettings
|
|||||||
ExportSettings();
|
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)
|
public static void UpdateUserAgent(string? customUserAgent)
|
||||||
{
|
{
|
||||||
userAgent = customUserAgent ?? DefaultUserAgent;
|
userAgent = customUserAgent ?? DefaultUserAgent;
|
||||||
@ -118,6 +96,12 @@ public static class TrangaSettings
|
|||||||
ExportSettings();
|
ExportSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void UpdateChapterNamingScheme(string namingScheme)
|
||||||
|
{
|
||||||
|
chapterNamingScheme = namingScheme;
|
||||||
|
ExportSettings();
|
||||||
|
}
|
||||||
|
|
||||||
public static void ResetRequestLimits()
|
public static void ResetRequestLimits()
|
||||||
{
|
{
|
||||||
requestLimits = DefaultRequestLimits;
|
requestLimits = DefaultRequestLimits;
|
||||||
@ -154,7 +138,7 @@ public static class TrangaSettings
|
|||||||
|
|
||||||
public static JObject AsJObject()
|
public static JObject AsJObject()
|
||||||
{
|
{
|
||||||
JObject jobj = new JObject();
|
JObject jobj = new ();
|
||||||
jobj.Add("downloadLocation", JToken.FromObject(downloadLocation));
|
jobj.Add("downloadLocation", JToken.FromObject(downloadLocation));
|
||||||
jobj.Add("workingDirectory", JToken.FromObject(workingDirectory));
|
jobj.Add("workingDirectory", JToken.FromObject(workingDirectory));
|
||||||
jobj.Add("userAgent", JToken.FromObject(userAgent));
|
jobj.Add("userAgent", JToken.FromObject(userAgent));
|
||||||
@ -163,6 +147,7 @@ public static class TrangaSettings
|
|||||||
jobj.Add("compression", JToken.FromObject(compression));
|
jobj.Add("compression", JToken.FromObject(compression));
|
||||||
jobj.Add("bwImages", JToken.FromObject(bwImages));
|
jobj.Add("bwImages", JToken.FromObject(bwImages));
|
||||||
jobj.Add("startNewJobTimeoutMs", JToken.FromObject(startNewJobTimeoutMs));
|
jobj.Add("startNewJobTimeoutMs", JToken.FromObject(startNewJobTimeoutMs));
|
||||||
|
jobj.Add("chapterNamingScheme", JToken.FromObject(chapterNamingScheme));
|
||||||
return jobj;
|
return jobj;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,5 +172,7 @@ public static class TrangaSettings
|
|||||||
bwImages = bwi.Value<bool>()!;
|
bwImages = bwi.Value<bool>()!;
|
||||||
if (jobj.TryGetValue("startNewJobTimeoutMs", out JToken? snjt))
|
if (jobj.TryGetValue("startNewJobTimeoutMs", out JToken? snjt))
|
||||||
startNewJobTimeoutMs = snjt.Value<int>()!;
|
startNewJobTimeoutMs = snjt.Value<int>()!;
|
||||||
|
if (jobj.TryGetValue("chapterNamingScheme", out JToken? cns))
|
||||||
|
chapterNamingScheme = cns.Value<string>()!;
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user