Add more documentation
This commit is contained in:
parent
a988d54619
commit
e499062fd5
@ -62,6 +62,12 @@ public abstract class Connector
|
||||
File.WriteAllText(seriesInfoPath,publication.GetSeriesInfo());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Downloads Image from URL and saves it to the given path(incl. fileName)
|
||||
/// </summary>
|
||||
/// <param name="imageUrl"></param>
|
||||
/// <param name="fullPath"></param>
|
||||
/// <param name="downloadClient">DownloadClient of the connector</param>
|
||||
protected static void DownloadImage(string imageUrl, string fullPath, DownloadClient downloadClient)
|
||||
{
|
||||
DownloadClient.RequestResult requestResult = downloadClient.MakeRequest(imageUrl);
|
||||
@ -70,22 +76,31 @@ public abstract class Connector
|
||||
File.WriteAllBytes(fullPath, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Downloads all Images from URLs, Compresses to zip(cbz) and saves.
|
||||
/// </summary>
|
||||
/// <param name="imageUrls">List of URLs to download Images from</param>
|
||||
/// <param name="saveArchiveFilePath">Full path to save archive to (without file ending .cbz)</param>
|
||||
/// <param name="downloadClient">DownloadClient of the connector</param>
|
||||
protected static void DownloadChapterImages(string[] imageUrls, string saveArchiveFilePath, DownloadClient downloadClient)
|
||||
{
|
||||
//Check if Publication Directory already exists
|
||||
string[] splitPath = saveArchiveFilePath.Split(Path.DirectorySeparatorChar);
|
||||
string directoryPath = Path.Combine(splitPath.Take(splitPath.Length - 1).ToArray());
|
||||
if (!Directory.Exists(directoryPath))
|
||||
Directory.CreateDirectory(directoryPath);
|
||||
|
||||
string fullPath = $"{saveArchiveFilePath}.cbz";
|
||||
if (File.Exists(fullPath))
|
||||
if (File.Exists(fullPath)) //Don't download twice.
|
||||
return;
|
||||
|
||||
//Create a temporary folder to store images
|
||||
string tempFolder = Path.GetTempFileName();
|
||||
File.Delete(tempFolder);
|
||||
Directory.CreateDirectory(tempFolder);
|
||||
|
||||
int chapter = 0;
|
||||
//Download all Images to temporary Folder
|
||||
foreach (string imageUrl in imageUrls)
|
||||
{
|
||||
string[] split = imageUrl.Split('.');
|
||||
@ -93,23 +108,32 @@ public abstract class Connector
|
||||
DownloadImage(imageUrl, Path.Join(tempFolder, $"{chapter++}.{extension}"), downloadClient);
|
||||
}
|
||||
|
||||
//ZIP-it and ship-it
|
||||
ZipFile.CreateFromDirectory(tempFolder, fullPath);
|
||||
Directory.Delete(tempFolder); //Cleanup
|
||||
}
|
||||
|
||||
|
||||
protected class DownloadClient
|
||||
{
|
||||
private readonly TimeSpan _requestSpeed;
|
||||
private DateTime _lastRequest;
|
||||
private static readonly HttpClient Client = new();
|
||||
|
||||
/// <summary>
|
||||
/// Creates a httpClient
|
||||
/// </summary>
|
||||
/// <param name="delay">minimum delay between requests (to avoid spam)</param>
|
||||
public DownloadClient(uint delay)
|
||||
{
|
||||
_requestSpeed = TimeSpan.FromMilliseconds(delay);
|
||||
_lastRequest = DateTime.Now.Subtract(_requestSpeed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Request Webpage
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <returns>RequestResult with StatusCode and Stream of received data</returns>
|
||||
public RequestResult MakeRequest(string url)
|
||||
{
|
||||
while((DateTime.Now - _lastRequest) < _requestSpeed)
|
||||
|
@ -20,24 +20,28 @@ public class MangaDex : Connector
|
||||
|
||||
public override Publication[] GetPublications(string publicationTitle = "")
|
||||
{
|
||||
const int limit = 100;
|
||||
int offset = 0;
|
||||
int total = int.MaxValue;
|
||||
const int limit = 100; //How many values we want returned at once
|
||||
int offset = 0; //"Page"
|
||||
int total = int.MaxValue; //How many total results are there, is updated on first request
|
||||
HashSet<Publication> publications = new();
|
||||
while (offset < total)
|
||||
while (offset < total) //As long as we haven't requested all "Pages"
|
||||
{
|
||||
//Request next Page
|
||||
DownloadClient.RequestResult requestResult =
|
||||
downloadClient.MakeRequest(
|
||||
$"https://api.mangadex.org/manga?limit={limit}&title={publicationTitle}&offset={offset}");
|
||||
if (requestResult.statusCode != HttpStatusCode.OK)
|
||||
break;
|
||||
JsonObject? result = JsonSerializer.Deserialize<JsonObject>(requestResult.result);
|
||||
|
||||
offset += limit;
|
||||
if (result is null)
|
||||
break;
|
||||
|
||||
total = result["total"]!.GetValue<int>();
|
||||
JsonArray mangaInResult = result["data"]!.AsArray();
|
||||
total = result["total"]!.GetValue<int>(); //Update the total number of Publications
|
||||
|
||||
JsonArray mangaInResult = result["data"]!.AsArray(); //Manga-data-Array
|
||||
//Loop each Manga and extract information from JSON
|
||||
foreach (JsonNode? mangeNode in mangaInResult)
|
||||
{
|
||||
JsonObject manga = (JsonObject)mangeNode!;
|
||||
@ -113,7 +117,7 @@ public class MangaDex : Connector
|
||||
status,
|
||||
manga["id"]!.GetValue<string>()
|
||||
);
|
||||
publications.Add(pub);
|
||||
publications.Add(pub); //Add Publication (Manga) to result
|
||||
}
|
||||
}
|
||||
|
||||
@ -122,16 +126,17 @@ public class MangaDex : Connector
|
||||
|
||||
public override Chapter[] GetChapters(Publication publication, string language = "")
|
||||
{
|
||||
const int limit = 100;
|
||||
int offset = 0;
|
||||
string id = publication.downloadUrl;
|
||||
int total = int.MaxValue;
|
||||
const int limit = 100; //How many values we want returned at once
|
||||
int offset = 0; //"Page"
|
||||
int total = int.MaxValue; //How many total results are there, is updated on first request
|
||||
List<Chapter> chapters = new();
|
||||
//As long as we haven't requested all "Pages"
|
||||
while (offset < total)
|
||||
{
|
||||
//Request next "Page"
|
||||
DownloadClient.RequestResult requestResult =
|
||||
downloadClient.MakeRequest(
|
||||
$"https://api.mangadex.org/manga/{id}/feed?limit={limit}&offset={offset}&translatedLanguage%5B%5D={language}");
|
||||
$"https://api.mangadex.org/manga/{publication.downloadUrl}/feed?limit={limit}&offset={offset}&translatedLanguage%5B%5D={language}");
|
||||
if (requestResult.statusCode != HttpStatusCode.OK)
|
||||
break;
|
||||
JsonObject? result = JsonSerializer.Deserialize<JsonObject>(requestResult.result);
|
||||
@ -142,6 +147,7 @@ public class MangaDex : Connector
|
||||
|
||||
total = result["total"]!.GetValue<int>();
|
||||
JsonArray chaptersInResult = result["data"]!.AsArray();
|
||||
//Loop through all Chapters in result and extract information from JSON
|
||||
foreach (JsonNode? jsonNode in chaptersInResult)
|
||||
{
|
||||
JsonObject chapter = (JsonObject)jsonNode!;
|
||||
@ -164,6 +170,7 @@ public class MangaDex : Connector
|
||||
}
|
||||
}
|
||||
|
||||
//Return Chapters ordered by Chapter-Number
|
||||
NumberFormatInfo chapterNumberFormatInfo = new()
|
||||
{
|
||||
NumberDecimalSeparator = "."
|
||||
@ -173,6 +180,7 @@ public class MangaDex : Connector
|
||||
|
||||
public override void DownloadChapter(Publication publication, Chapter chapter)
|
||||
{
|
||||
//Request URLs for Chapter-Images
|
||||
DownloadClient.RequestResult requestResult =
|
||||
downloadClient.MakeRequest($"https://api.mangadex.org/at-home/server/{chapter.url}?forcePort443=false'");
|
||||
if (requestResult.statusCode != HttpStatusCode.OK)
|
||||
@ -184,22 +192,26 @@ public class MangaDex : Connector
|
||||
string baseUrl = result["baseUrl"]!.GetValue<string>();
|
||||
string hash = result["chapter"]!["hash"]!.GetValue<string>();
|
||||
JsonArray imageFileNames = result["chapter"]!["data"]!.AsArray();
|
||||
//Loop through all imageNames and construct urls (imageUrl)
|
||||
HashSet<string> imageUrls = new();
|
||||
foreach (JsonNode? image in imageFileNames)
|
||||
imageUrls.Add($"{baseUrl}/data/{hash}/{image!.GetValue<string>()}");
|
||||
|
||||
//Download Chapter-Images
|
||||
DownloadChapterImages(imageUrls.ToArray(), Path.Join(downloadLocation, publication.folderName, chapter.fileName), this.downloadClient);
|
||||
}
|
||||
|
||||
public override void DownloadCover(Publication publication)
|
||||
{
|
||||
string publicationPath = Path.Join(downloadLocation, publication.folderName);
|
||||
Directory.CreateDirectory(publicationPath);
|
||||
DirectoryInfo dirInfo = new (publicationPath);
|
||||
//Check if Publication already has a Folder and cover
|
||||
string publicationFolder = Path.Join(downloadLocation, publication.folderName);
|
||||
Directory.CreateDirectory(publicationFolder);
|
||||
DirectoryInfo dirInfo = new (publicationFolder);
|
||||
foreach(FileInfo fileInfo in dirInfo.EnumerateFiles())
|
||||
if (fileInfo.Name.Contains("cover."))
|
||||
return;
|
||||
|
||||
//Request information where to download Cover
|
||||
DownloadClient.RequestResult requestResult =
|
||||
downloadClient.MakeRequest($"https://api.mangadex.org/cover/{publication.posterUrl}");
|
||||
if (requestResult.statusCode != HttpStatusCode.OK)
|
||||
@ -211,11 +223,15 @@ public class MangaDex : Connector
|
||||
string fileName = result!["data"]!["attributes"]!["fileName"]!.GetValue<string>();
|
||||
|
||||
string coverUrl = $"https://uploads.mangadex.org/covers/{publication.downloadUrl}/{fileName}";
|
||||
|
||||
//Get file-extension (jpg, png)
|
||||
string[] split = coverUrl.Split('.');
|
||||
string extension = split[split.Length - 1];
|
||||
|
||||
string outFolderPath = Path.Join(downloadLocation, publication.folderName);
|
||||
Directory.CreateDirectory(outFolderPath);
|
||||
|
||||
//Download cover-Image
|
||||
DownloadImage(coverUrl, Path.Join(downloadLocation, publication.folderName, $"cover.{extension}"), this.downloadClient);
|
||||
}
|
||||
}
|
@ -17,6 +17,7 @@ public static class TaskExecutor
|
||||
/// <exception cref="ArgumentException">Is thrown when there is no Connector available with the name of the TrangaTask.connectorName</exception>
|
||||
public static void Execute(Connector[] connectors, TrangaTask trangaTask, Dictionary<Publication, List<Chapter>> chapterCollection)
|
||||
{
|
||||
//Get Connector from list of available Connectors and the required Connector of the TrangaTask
|
||||
Connector? connector = connectors.FirstOrDefault(c => c.name == trangaTask.connectorName);
|
||||
if (connector is null)
|
||||
throw new ArgumentException($"Connector {trangaTask.connectorName} is not a known connector.");
|
||||
@ -26,6 +27,7 @@ public static class TaskExecutor
|
||||
trangaTask.isBeingExecuted = true;
|
||||
trangaTask.lastExecuted = DateTime.Now;
|
||||
|
||||
//Call appropriate Method based on TrangaTask.Task
|
||||
switch (trangaTask.task)
|
||||
{
|
||||
case TrangaTask.Task.DownloadNewChapters:
|
||||
@ -42,6 +44,11 @@ public static class TaskExecutor
|
||||
trangaTask.isBeingExecuted = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the available Publications from a Connector (all of them)
|
||||
/// </summary>
|
||||
/// <param name="connector">Connector to receive Publications from</param>
|
||||
/// <param name="chapterCollection"></param>
|
||||
private static void UpdatePublications(Connector connector, Dictionary<Publication, List<Chapter>> chapterCollection)
|
||||
{
|
||||
Publication[] publications = connector.GetPublications();
|
||||
@ -49,6 +56,14 @@ public static class TaskExecutor
|
||||
chapterCollection.TryAdd(publication, new List<Chapter>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks for new Chapters and Downloads new ones.
|
||||
/// If no Chapters had been downloaded previously, download also cover and create series.json
|
||||
/// </summary>
|
||||
/// <param name="connector">Connector to use</param>
|
||||
/// <param name="publication">Publication to check</param>
|
||||
/// <param name="language">Language to receive chapters for</param>
|
||||
/// <param name="chapterCollection"></param>
|
||||
private static void DownloadNewChapters(Connector connector, Publication publication, string language, Dictionary<Publication, List<Chapter>> chapterCollection)
|
||||
{
|
||||
List<Chapter> newChapters = UpdateChapters(connector, publication, language, chapterCollection);
|
||||
@ -58,6 +73,14 @@ public static class TaskExecutor
|
||||
connector.SaveSeriesInfo(publication);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the available Chapters of a Publication
|
||||
/// </summary>
|
||||
/// <param name="connector">Connector to use</param>
|
||||
/// <param name="publication">Publication to check</param>
|
||||
/// <param name="language">Language to receive chapters for</param>
|
||||
/// <param name="chapterCollection"></param>
|
||||
/// <returns>List of Chapters that were previously not in collection</returns>
|
||||
private static List<Chapter> UpdateChapters(Connector connector, Publication publication, string language, Dictionary<Publication, List<Chapter>> chapterCollection)
|
||||
{
|
||||
List<Chapter> newChaptersList = new();
|
||||
|
@ -70,10 +70,12 @@ public class TaskManager
|
||||
public void AddTask(TrangaTask.Task task, string connectorName, Publication? publication, TimeSpan reoccurrence,
|
||||
string language = "")
|
||||
{
|
||||
//Get appropriate Connector from available Connectors for TrangaTask
|
||||
Connector? connector = connectors.FirstOrDefault(c => c.name == connectorName);
|
||||
if (connector is null)
|
||||
throw new ArgumentException($"Connector {connectorName} is not a known connector.");
|
||||
|
||||
//Check if same task already exists
|
||||
if (!_allTasks.Any(trangaTask => trangaTask.task != task && trangaTask.connectorName != connector.name &&
|
||||
trangaTask.publication?.downloadUrl != publication?.downloadUrl))
|
||||
{
|
||||
@ -142,7 +144,7 @@ public class TaskManager
|
||||
//Wait for tasks to finish
|
||||
while(_allTasks.Any(task => task.isBeingExecuted))
|
||||
Thread.Sleep(10);
|
||||
|
||||
Environment.Exit(0);
|
||||
}
|
||||
|
||||
private HashSet<TrangaTask> ImportTasks(string importFolderPath)
|
||||
|
Loading…
Reference in New Issue
Block a user