Skip to content

Commit

Permalink
Merge pull request #230 from C9Glax/cuttingedge-merge-candidate
Browse files Browse the repository at this point in the history
Cuttingedge merge candidate
  • Loading branch information
C9Glax authored Aug 31, 2024
2 parents f5da2f8 + fccaf9f commit 398b6ff
Show file tree
Hide file tree
Showing 26 changed files with 640 additions and 251 deletions.
33 changes: 7 additions & 26 deletions CLI/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,37 +47,18 @@ public override int Execute([NotNull] CommandContext context, [NotNull] Settings
string? logFolderPath = settings.fileLoggerPath ?? "";
Logger logger = new(enabledLoggers.ToArray(), Console.Out, Console.OutputEncoding, logFolderPath);

TrangaSettings? trangaSettings = null;

if (settings.downloadLocation is not null && settings.workingDirectory is not null)
{
trangaSettings = new TrangaSettings(settings.downloadLocation, settings.workingDirectory);
}else if (settings.downloadLocation is not null)
{
if (trangaSettings is null)
trangaSettings = new TrangaSettings(downloadLocation: settings.downloadLocation);
else
trangaSettings = new TrangaSettings(downloadLocation: settings.downloadLocation, settings.workingDirectory);
}else if (settings.workingDirectory is not null)
{
if (trangaSettings is null)
trangaSettings = new TrangaSettings(downloadLocation: settings.workingDirectory);
else
trangaSettings = new TrangaSettings(settings.downloadLocation, settings.workingDirectory);
}
if(settings.workingDirectory is not null)
TrangaSettings.LoadFromWorkingDirectory(settings.workingDirectory);
else
{
trangaSettings = new TrangaSettings();
}

Directory.CreateDirectory(trangaSettings.downloadLocation);
Directory.CreateDirectory(trangaSettings.workingDirectory);
TrangaSettings.CreateOrUpdate();
if(settings.downloadLocation is not null)
TrangaSettings.CreateOrUpdate(downloadDirectory: settings.downloadLocation);

Tranga.Tranga? api = null;

Thread trangaApi = new Thread(() =>
{
api = new(logger, trangaSettings);
api = new(logger);
});
trangaApi.Start();

Expand Down Expand Up @@ -120,7 +101,7 @@ public override int Execute([NotNull] CommandContext context, [NotNull] Settings
parameters.Add(new ValueTuple<string, string>(name, value));
}

string requestString = $"http://localhost:{trangaSettings.apiPortNumber}/{requestPath}";
string requestString = $"http://localhost:{TrangaSettings.apiPortNumber}/{requestPath}";
if (parameters.Any())
{
requestString += "?";
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ Tranga can download Chapters and Metadata from "Scanlation" sites such as
- [Mangaworld.bz](https://www.mangaworld.bz/) (it)
- [Bato.to](https://bato.to/v3x) (en)
- [Manga4Life](https://manga4life.com) (en)
- [ManhuaPlus](https://manhuaplus.org/) (en)
- [MangaHere](https://www.mangahere.cc/) (en) (Their covers suck)
- ❓ Open an [issue](https://github.com/C9Glax/tranga/issues)

and trigger a library-scan with [Komga](https://komga.org/) and [Kavita](https://www.kavitareader.com/).
Expand Down
12 changes: 6 additions & 6 deletions Tranga/Chapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,15 +87,15 @@ public int CompareTo(object? obj)
/// Checks if a chapter-archive is already present
/// </summary>
/// <returns>true if chapter is present</returns>
internal bool CheckChapterIsDownloaded(string downloadLocation)
internal bool CheckChapterIsDownloaded()
{
if (!Directory.Exists(Path.Join(downloadLocation, parentManga.folderName)))
if (!Directory.Exists(Path.Join(TrangaSettings.downloadLocation, parentManga.folderName)))
return false;
FileInfo[] archives = new DirectoryInfo(Path.Join(downloadLocation, parentManga.folderName)).GetFiles().Where(file => file.Name.Split('.')[^1] == "cbz").ToArray();
FileInfo[] archives = new DirectoryInfo(Path.Join(TrangaSettings.downloadLocation, parentManga.folderName)).GetFiles().Where(file => file.Name.Split('.')[^1] == "cbz").ToArray();
Regex volChRex = new(@"(?:Vol(?:ume)?\.([0-9]+)\D*)?Ch(?:apter)?\.([0-9]+(?:\.[0-9]+)*)");

Chapter t = this;
string thisPath = GetArchiveFilePath(downloadLocation);
string thisPath = GetArchiveFilePath();
FileInfo? archive = archives.FirstOrDefault(archive =>
{
Match m = volChRex.Match(archive.Name);
Expand All @@ -112,9 +112,9 @@ internal bool CheckChapterIsDownloaded(string downloadLocation)
/// Creates full file path of chapter-archive
/// </summary>
/// <returns>Filepath</returns>
internal string GetArchiveFilePath(string downloadLocation)
internal string GetArchiveFilePath()
{
return Path.Join(downloadLocation, parentManga.folderName, $"{parentManga.folderName} - {this.fileName}.cbz");
return Path.Join(TrangaSettings.downloadLocation, parentManga.folderName, $"{parentManga.folderName} - {this.fileName}.cbz");
}

/// <summary>
Expand Down
25 changes: 11 additions & 14 deletions Tranga/GlobalBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ public abstract class GlobalBase
{
[JsonIgnore]
public Logger? logger { get; init; }
protected TrangaSettings settings { get; init; }
protected HashSet<NotificationConnector> notificationConnectors { get; init; }
protected HashSet<LibraryConnector> libraryConnectors { get; init; }
private Dictionary<string, Manga> cachedPublications { get; init; }
Expand All @@ -21,18 +20,16 @@ public abstract class GlobalBase
protected GlobalBase(GlobalBase clone)
{
this.logger = clone.logger;
this.settings = clone.settings;
this.notificationConnectors = clone.notificationConnectors;
this.libraryConnectors = clone.libraryConnectors;
this.cachedPublications = clone.cachedPublications;
}

protected GlobalBase(Logger? logger, TrangaSettings settings)
protected GlobalBase(Logger? logger)
{
this.logger = logger;
this.settings = settings;
this.notificationConnectors = settings.LoadNotificationConnectors(this);
this.libraryConnectors = settings.LoadLibraryConnectors(this);
this.notificationConnectors = TrangaSettings.LoadNotificationConnectors(this);
this.libraryConnectors = TrangaSettings.LoadLibraryConnectors(this);
this.cachedPublications = new();
}

Expand Down Expand Up @@ -81,20 +78,20 @@ protected void AddNotificationConnector(NotificationConnector notificationConnec
notificationConnectors.RemoveWhere(nc => nc.notificationConnectorType == notificationConnector.notificationConnectorType);
notificationConnectors.Add(notificationConnector);

while(IsFileInUse(settings.notificationConnectorsFilePath))
while(IsFileInUse(TrangaSettings.notificationConnectorsFilePath))
Thread.Sleep(100);
Log("Exporting notificationConnectors");
File.WriteAllText(settings.notificationConnectorsFilePath, JsonConvert.SerializeObject(notificationConnectors));
File.WriteAllText(TrangaSettings.notificationConnectorsFilePath, JsonConvert.SerializeObject(notificationConnectors));
}

protected void DeleteNotificationConnector(NotificationConnector.NotificationConnectorType notificationConnectorType)
{
Log($"Removing {notificationConnectorType}");
notificationConnectors.RemoveWhere(nc => nc.notificationConnectorType == notificationConnectorType);
while(IsFileInUse(settings.notificationConnectorsFilePath))
while(IsFileInUse(TrangaSettings.notificationConnectorsFilePath))
Thread.Sleep(100);
Log("Exporting notificationConnectors");
File.WriteAllText(settings.notificationConnectorsFilePath, JsonConvert.SerializeObject(notificationConnectors));
File.WriteAllText(TrangaSettings.notificationConnectorsFilePath, JsonConvert.SerializeObject(notificationConnectors));
}

protected void UpdateLibraries()
Expand All @@ -109,20 +106,20 @@ protected void AddLibraryConnector(LibraryConnector libraryConnector)
libraryConnectors.RemoveWhere(lc => lc.libraryType == libraryConnector.libraryType);
libraryConnectors.Add(libraryConnector);

while(IsFileInUse(settings.libraryConnectorsFilePath))
while(IsFileInUse(TrangaSettings.libraryConnectorsFilePath))
Thread.Sleep(100);
Log("Exporting libraryConnectors");
File.WriteAllText(settings.libraryConnectorsFilePath, JsonConvert.SerializeObject(libraryConnectors, Formatting.Indented));
File.WriteAllText(TrangaSettings.libraryConnectorsFilePath, JsonConvert.SerializeObject(libraryConnectors, Formatting.Indented));
}

protected void DeleteLibraryConnector(LibraryConnector.LibraryType libraryType)
{
Log($"Removing {libraryType}");
libraryConnectors.RemoveWhere(lc => lc.libraryType == libraryType);
while(IsFileInUse(settings.libraryConnectorsFilePath))
while(IsFileInUse(TrangaSettings.libraryConnectorsFilePath))
Thread.Sleep(100);
Log("Exporting libraryConnectors");
File.WriteAllText(settings.libraryConnectorsFilePath, JsonConvert.SerializeObject(libraryConnectors, Formatting.Indented));
File.WriteAllText(TrangaSettings.libraryConnectorsFilePath, JsonConvert.SerializeObject(libraryConnectors, Formatting.Indented));
}

protected bool IsFileInUse(string filePath) => IsFileInUse(filePath, this.logger);
Expand Down
2 changes: 1 addition & 1 deletion Tranga/Jobs/DownloadNewChapters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public override string ToString()

protected override IEnumerable<Job> ExecuteReturnSubTasksInternal(JobBoss jobBoss)
{
manga.SaveSeriesInfoJson(settings.downloadLocation);
manga.SaveSeriesInfoJson();
Chapter[] chapters = mangaConnector.GetNewChapters(manga, this.translatedLanguage);
this.progressToken.increments = chapters.Length;
List<Job> jobs = new();
Expand Down
12 changes: 6 additions & 6 deletions Tranga/Jobs/JobBoss.cs
Original file line number Diff line number Diff line change
Expand Up @@ -140,15 +140,15 @@ private void AddJobsToQueue(IEnumerable<Job> newJobs)

private void LoadJobsList(HashSet<MangaConnector> connectors)
{
if (!Directory.Exists(settings.jobsFolderPath)) //No jobs to load
if (!Directory.Exists(TrangaSettings.jobsFolderPath)) //No jobs to load
{
Directory.CreateDirectory(settings.jobsFolderPath);
Directory.CreateDirectory(TrangaSettings.jobsFolderPath);
return;
}
Regex idRex = new (@"(.*)\.json");

//Load json-job-files
foreach (FileInfo file in new DirectoryInfo(settings.jobsFolderPath).EnumerateFiles().Where(fileInfo => idRex.IsMatch(fileInfo.Name)))
foreach (FileInfo file in new DirectoryInfo(TrangaSettings.jobsFolderPath).EnumerateFiles().Where(fileInfo => idRex.IsMatch(fileInfo.Name)))
{
Log($"Adding {file.Name}");
Job? job = JsonConvert.DeserializeObject<Job>(File.ReadAllText(file.FullName),
Expand Down Expand Up @@ -180,14 +180,14 @@ private void LoadJobsList(HashSet<MangaConnector> connectors)
AddMangaToCache(dncJob.manga);
}

string[] coverFiles = Directory.GetFiles(settings.coverImageCache);
string[] coverFiles = Directory.GetFiles(TrangaSettings.coverImageCache);
foreach(string fileName in coverFiles.Where(fileName => !GetAllCachedManga().Any(manga => manga.coverFileNameInCache == fileName)))
File.Delete(fileName);
}

internal void UpdateJobFile(Job job, string? oldFile = null)
{
string newJobFilePath = Path.Join(settings.jobsFolderPath, $"{job.id}.json");
string newJobFilePath = Path.Join(TrangaSettings.jobsFolderPath, $"{job.id}.json");

if (!this.jobs.Any(jjob => jjob.id == job.id))
{
Expand Down Expand Up @@ -234,7 +234,7 @@ private void UpdateAllJobFiles()

//Remove files with jobs not in this.jobs-list
Regex idRex = new (@"(.*)\.json");
foreach (FileInfo file in new DirectoryInfo(settings.jobsFolderPath).EnumerateFiles())
foreach (FileInfo file in new DirectoryInfo(TrangaSettings.jobsFolderPath).EnumerateFiles())
{
if (idRex.IsMatch(file.Name))
{
Expand Down
2 changes: 1 addition & 1 deletion Tranga/Jobs/UpdateMetadata.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ protected override IEnumerable<Job> ExecuteReturnSubTasksInternal(JobBoss jobBos
}

this.manga = manga.WithMetadata(updatedManga);
this.manga.SaveSeriesInfoJson(settings.downloadLocation, true);
this.manga.SaveSeriesInfoJson(true);
this.mangaConnector.CopyCoverFromCacheToDownloadLocation(manga);
foreach (Job job in jobBoss.GetJobsLike(publication: this.manga))
{
Expand Down
17 changes: 9 additions & 8 deletions Tranga/Manga.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using Newtonsoft.Json;
using static System.IO.UnixFileMode;

Expand Down Expand Up @@ -51,18 +52,18 @@ public enum ReleaseStatusByte : byte
[JsonConstructor]
public Manga(string sortName, List<string> authors, string? description, Dictionary<string,string> altTitles, string[] tags, string? coverUrl, string? coverFileNameInCache, Dictionary<string,string>? links, int? year, string? originalLanguage, string publicationId, ReleaseStatusByte releaseStatus, string? websiteUrl = null, string? folderName = null, float? ignoreChaptersBelow = 0)
{
this.sortName = sortName;
this.authors = authors;
this.description = description;
this.altTitles = altTitles;
this.tags = tags;
this.sortName = HttpUtility.HtmlDecode(sortName);
this.authors = authors.Select(HttpUtility.HtmlDecode).ToList()!;
this.description = HttpUtility.HtmlDecode(description);
this.altTitles = altTitles.ToDictionary(a => HttpUtility.HtmlDecode(a.Key), a => HttpUtility.HtmlDecode(a.Value));
this.tags = tags.Select(HttpUtility.HtmlDecode).ToArray()!;
this.coverFileNameInCache = coverFileNameInCache;
this.coverUrl = coverUrl;
this.links = links ?? new Dictionary<string, string>();
this.year = year;
this.originalLanguage = originalLanguage;
this.publicationId = publicationId;
this.folderName = folderName ?? string.Concat(LegalCharacters.Matches(sortName));
this.folderName = folderName ?? string.Concat(LegalCharacters.Matches(HttpUtility.HtmlDecode(sortName)));
while (this.folderName.EndsWith('.'))
this.folderName = this.folderName.Substring(0, this.folderName.Length - 1);
string onlyLowerLetters = string.Concat(this.sortName.ToLower().Where(Char.IsLetter));
Expand Down Expand Up @@ -139,9 +140,9 @@ public void MovePublicationFolder(string downloadDirectory, string newFolderName
latestChapterDownloaded = latestChapterDownloaded < chapterNumber ? chapterNumber : latestChapterDownloaded;
}

public void SaveSeriesInfoJson(string downloadDirectory, bool overwrite = false)
public void SaveSeriesInfoJson(bool overwrite = false)
{
string publicationFolder = CreatePublicationFolder(downloadDirectory);
string publicationFolder = CreatePublicationFolder(TrangaSettings.downloadLocation);
string seriesInfoPath = Path.Join(publicationFolder, "series.json");
if(overwrite || (!overwrite && !File.Exists(seriesInfoPath)))
File.WriteAllText(seriesInfoPath,this.GetSeriesInfoJson());
Expand Down
2 changes: 1 addition & 1 deletion Tranga/MangaConnectors/Bato.cs
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ public override HttpStatusCode DownloadChapter(Chapter chapter, ProgressToken? p
string comicInfoPath = Path.GetTempFileName();
File.WriteAllText(comicInfoPath, chapter.GetComicInfoXmlString());

return DownloadChapterImages(imageUrls, chapter.GetArchiveFilePath(settings.downloadLocation), RequestType.MangaImage, comicInfoPath, "https://mangakatana.com/", progressToken:progressToken);
return DownloadChapterImages(imageUrls, chapter.GetArchiveFilePath(), RequestType.MangaImage, comicInfoPath, "https://mangakatana.com/", progressToken:progressToken);
}

private string[] ParseImageUrlsFromHtml(string mangaUrl)
Expand Down
16 changes: 13 additions & 3 deletions Tranga/MangaConnectors/ChromiumDownloadClient.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using HtmlAgilityPack;
using PuppeteerSharp;
using PuppeteerSharp.Input;

namespace Tranga.MangaConnectors;

Expand All @@ -11,10 +11,11 @@ internal class ChromiumDownloadClient : DownloadClient
private IBrowser browser { get; set; }
private const string ChromiumVersion = "1154303";
private const int StartTimeoutMs = 30000;
private readonly HttpDownloadClient _httpDownloadClient;

private async Task<IBrowser> DownloadBrowser()
{
BrowserFetcher browserFetcher = new BrowserFetcher();
BrowserFetcher browserFetcher = new ();
foreach(string rev in browserFetcher.LocalRevisions().Where(rev => rev != ChromiumVersion))
browserFetcher.Remove(rev);
if (!browserFetcher.LocalRevisions().Contains(ChromiumVersion))
Expand Down Expand Up @@ -58,9 +59,18 @@ private async Task<IBrowser> DownloadBrowser()
public ChromiumDownloadClient(GlobalBase clone) : base(clone)
{
this.browser = DownloadBrowser().Result;
_httpDownloadClient = new(this);
}

protected override RequestResult MakeRequestInternal(string url, string? referrer = null, string? clickButton = null)
private readonly Regex _imageUrlRex = new(@"https?:\/\/.*\.(?:p?jpe?g|gif|a?png|bmp|avif|webp)(\?.*)?");
internal override RequestResult MakeRequestInternal(string url, string? referrer = null, string? clickButton = null)
{
return _imageUrlRex.IsMatch(url)
? _httpDownloadClient.MakeRequestInternal(url, referrer)
: MakeRequestBrowser(url, referrer, clickButton);
}

private RequestResult MakeRequestBrowser(string url, string? referrer = null, string? clickButton = null)
{
IPage page = this.browser.NewPageAsync().Result;
page.DefaultTimeout = 10000;
Expand Down
8 changes: 4 additions & 4 deletions Tranga/MangaConnectors/DownloadClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ protected DownloadClient(GlobalBase clone) : base(clone)

public RequestResult MakeRequest(string url, RequestType requestType, string? referrer = null, string? clickButton = null)
{
if (!settings.requestLimits.ContainsKey(requestType))
if (!TrangaSettings.requestLimits.ContainsKey(requestType))
{
Log("RequestType not configured for rate-limit.");
return new RequestResult(HttpStatusCode.NotAcceptable, null, Stream.Null);
}

int rateLimit = settings.userAgent == TrangaSettings.DefaultUserAgent
int rateLimit = TrangaSettings.userAgent == TrangaSettings.DefaultUserAgent
? TrangaSettings.DefaultRequestLimits[requestType]
: settings.requestLimits[requestType];
: TrangaSettings.requestLimits[requestType];

TimeSpan timeBetweenRequests = TimeSpan.FromMinutes(1).Divide(rateLimit);
_lastExecutedRateLimit.TryAdd(requestType, DateTime.Now.Subtract(timeBetweenRequests));
Expand All @@ -40,6 +40,6 @@ public RequestResult MakeRequest(string url, RequestType requestType, string? re
return result;
}

protected abstract RequestResult MakeRequestInternal(string url, string? referrer = null, string? clickButton = null);
internal abstract RequestResult MakeRequestInternal(string url, string? referrer = null, string? clickButton = null);
public abstract void Close();
}
4 changes: 2 additions & 2 deletions Tranga/MangaConnectors/HttpDownloadClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ internal class HttpDownloadClient : DownloadClient

public HttpDownloadClient(GlobalBase clone) : base(clone)
{
Client.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", settings.userAgent);
Client.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", TrangaSettings.userAgent);
}

protected override RequestResult MakeRequestInternal(string url, string? referrer = null, string? clickButton = null)
internal override RequestResult MakeRequestInternal(string url, string? referrer = null, string? clickButton = null)
{
if(clickButton is not null)
Log("Can not click button on static site.");
Expand Down
Loading

0 comments on commit 398b6ff

Please sign in to comment.