2023-06-27 23:22:23 +02:00
|
|
|
|
using System.Text.RegularExpressions;
|
|
|
|
|
using System.Xml.Linq;
|
2023-05-18 20:06:29 +02:00
|
|
|
|
|
|
|
|
|
namespace Tranga;
|
2023-05-17 23:23:01 +02:00
|
|
|
|
|
2023-05-19 19:52:24 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Has to be Part of a publication
|
|
|
|
|
/// Includes the Chapter-Name, -VolumeNumber, -ChapterNumber, the location of the chapter on the internet and the saveName of the local file.
|
|
|
|
|
/// </summary>
|
2024-01-03 18:37:12 +01:00
|
|
|
|
public readonly struct Chapter : IComparable
|
2023-05-17 23:23:01 +02:00
|
|
|
|
{
|
2023-07-30 17:25:04 +02:00
|
|
|
|
// ReSharper disable once MemberCanBePrivate.Global
|
2023-08-31 12:16:02 +02:00
|
|
|
|
public Manga parentManga { get; }
|
2023-05-18 16:03:00 +02:00
|
|
|
|
public string? name { get; }
|
2024-01-04 17:04:08 +01:00
|
|
|
|
public string volumeNumber { get; }
|
2023-06-27 23:22:23 +02:00
|
|
|
|
public string chapterNumber { get; }
|
2023-05-18 16:21:21 +02:00
|
|
|
|
public string url { get; }
|
2023-07-30 17:25:04 +02:00
|
|
|
|
// ReSharper disable once MemberCanBePrivate.Global
|
2023-05-18 19:51:26 +02:00
|
|
|
|
public string fileName { get; }
|
2023-05-25 16:55:58 +02:00
|
|
|
|
|
2023-06-27 23:08:29 +02:00
|
|
|
|
private static readonly Regex LegalCharacters = new (@"([A-z]*[0-9]* *\.*-*,*\]*\[*'*\'*\)*\(*~*!*)*");
|
|
|
|
|
private static readonly Regex IllegalStrings = new(@"Vol(ume)?.?", RegexOptions.IgnoreCase);
|
2023-08-31 12:16:02 +02:00
|
|
|
|
public Chapter(Manga parentManga, string? name, string? volumeNumber, string chapterNumber, string url)
|
2023-05-17 23:23:01 +02:00
|
|
|
|
{
|
2023-08-31 12:16:02 +02:00
|
|
|
|
this.parentManga = parentManga;
|
2023-05-17 23:23:01 +02:00
|
|
|
|
this.name = name;
|
2024-01-04 17:04:08 +01:00
|
|
|
|
this.volumeNumber = volumeNumber ?? "0";
|
2023-05-17 23:23:01 +02:00
|
|
|
|
this.chapterNumber = chapterNumber;
|
2023-05-18 16:21:21 +02:00
|
|
|
|
this.url = url;
|
2024-02-07 15:34:20 +01:00
|
|
|
|
|
|
|
|
|
string chapterVolNumStr;
|
|
|
|
|
if (volumeNumber is not null && volumeNumber.Length > 0)
|
|
|
|
|
chapterVolNumStr = $"Vol.{volumeNumber} Ch.{chapterNumber}";
|
|
|
|
|
else
|
|
|
|
|
chapterVolNumStr = $"Ch.{chapterNumber}";
|
2023-06-03 22:25:24 +02:00
|
|
|
|
|
2024-02-07 15:34:20 +01:00
|
|
|
|
if (name is not null && name.Length > 0)
|
|
|
|
|
{
|
|
|
|
|
string chapterName = IllegalStrings.Replace(string.Concat(LegalCharacters.Matches(name)), "");
|
|
|
|
|
this.fileName = $"{chapterVolNumStr} - {chapterName}";
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
this.fileName = chapterVolNumStr;
|
2023-05-17 23:23:01 +02:00
|
|
|
|
}
|
2023-08-27 01:21:23 +02:00
|
|
|
|
|
|
|
|
|
public override string ToString()
|
|
|
|
|
{
|
2023-08-31 12:16:02 +02:00
|
|
|
|
return $"Chapter {parentManga.sortName} {parentManga.internalId} {chapterNumber} {name}";
|
2023-08-27 01:21:23 +02:00
|
|
|
|
}
|
|
|
|
|
|
2024-01-11 20:19:04 +01:00
|
|
|
|
public override bool Equals(object? obj)
|
|
|
|
|
{
|
|
|
|
|
if (obj is not Chapter)
|
|
|
|
|
return false;
|
|
|
|
|
return CompareTo(obj) == 0;
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-03 18:37:12 +01:00
|
|
|
|
public int CompareTo(object? obj)
|
|
|
|
|
{
|
|
|
|
|
if (obj is Chapter otherChapter)
|
|
|
|
|
{
|
|
|
|
|
if (float.TryParse(volumeNumber, GlobalBase.numberFormatDecimalPoint, out float volumeNumberFloat) &&
|
|
|
|
|
float.TryParse(chapterNumber, GlobalBase.numberFormatDecimalPoint, out float chapterNumberFloat) &&
|
|
|
|
|
float.TryParse(otherChapter.volumeNumber, GlobalBase.numberFormatDecimalPoint,
|
|
|
|
|
out float otherVolumeNumberFloat) &&
|
|
|
|
|
float.TryParse(otherChapter.chapterNumber, GlobalBase.numberFormatDecimalPoint,
|
|
|
|
|
out float otherChapterNumberFloat))
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
switch (volumeNumberFloat.CompareTo(otherVolumeNumberFloat))
|
|
|
|
|
{
|
|
|
|
|
case < 0:
|
|
|
|
|
return -1;
|
|
|
|
|
case > 0:
|
|
|
|
|
return 1;
|
|
|
|
|
default:
|
|
|
|
|
return chapterNumberFloat.CompareTo(otherChapterNumberFloat);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else throw new FormatException($"Value could not be parsed");
|
|
|
|
|
}
|
|
|
|
|
throw new ArgumentException($"{obj} can not be compared to {this}");
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-27 23:22:23 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Checks if a chapter-archive is already present
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns>true if chapter is present</returns>
|
|
|
|
|
internal bool CheckChapterIsDownloaded(string downloadLocation)
|
|
|
|
|
{
|
2023-08-31 12:16:02 +02:00
|
|
|
|
if (!Directory.Exists(Path.Join(downloadLocation, parentManga.folderName)))
|
2023-06-27 23:22:23 +02:00
|
|
|
|
return false;
|
2023-08-31 12:16:02 +02:00
|
|
|
|
FileInfo[] archives = new DirectoryInfo(Path.Join(downloadLocation, parentManga.folderName)).GetFiles();
|
2024-02-07 19:40:07 +01:00
|
|
|
|
Regex volChRex = new(@"(?:Vol(?:ume)?\.([0-9]+)\D*)?Ch(?:apter)?\.([0-9]+(?:\.[0-9]+)*)");
|
2023-06-27 23:22:23 +02:00
|
|
|
|
|
2024-02-07 15:50:26 +01:00
|
|
|
|
Chapter t = this;
|
|
|
|
|
return archives.Select(archive => archive.Name).Any(archiveFileName =>
|
2023-06-27 23:22:23 +02:00
|
|
|
|
{
|
2024-02-07 15:50:26 +01:00
|
|
|
|
Match m = volChRex.Match(archiveFileName);
|
2024-02-07 18:08:57 +01:00
|
|
|
|
string archiveVolNum = m.Groups[1].Success ? m.Groups[1].Value : "0";
|
2024-02-07 15:50:26 +01:00
|
|
|
|
string archiveChNum = m.Groups[2].Value;
|
|
|
|
|
return archiveVolNum == t.volumeNumber &&
|
|
|
|
|
archiveChNum == t.chapterNumber;
|
|
|
|
|
});
|
2023-06-27 23:22:23 +02:00
|
|
|
|
}
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Creates full file path of chapter-archive
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns>Filepath</returns>
|
|
|
|
|
internal string GetArchiveFilePath(string downloadLocation)
|
|
|
|
|
{
|
2023-08-31 12:16:02 +02:00
|
|
|
|
return Path.Join(downloadLocation, parentManga.folderName, $"{parentManga.folderName} - {this.fileName}.cbz");
|
2023-06-27 23:22:23 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Creates a string containing XML of publication and chapter.
|
|
|
|
|
/// See ComicInfo.xml
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns>XML-string</returns>
|
|
|
|
|
internal string GetComicInfoXmlString()
|
|
|
|
|
{
|
|
|
|
|
XElement comicInfo = new XElement("ComicInfo",
|
2023-08-31 12:16:02 +02:00
|
|
|
|
new XElement("Tags", string.Join(',', parentManga.tags)),
|
|
|
|
|
new XElement("LanguageISO", parentManga.originalLanguage),
|
2023-06-27 23:22:23 +02:00
|
|
|
|
new XElement("Title", this.name),
|
2023-08-31 12:16:02 +02:00
|
|
|
|
new XElement("Writer", string.Join(',', parentManga.authors)),
|
2023-06-27 23:22:23 +02:00
|
|
|
|
new XElement("Volume", this.volumeNumber),
|
|
|
|
|
new XElement("Number", this.chapterNumber)
|
|
|
|
|
);
|
|
|
|
|
return comicInfo.ToString();
|
|
|
|
|
}
|
2023-05-17 23:23:01 +02:00
|
|
|
|
}
|