17 Commits
1.1 ... 1.1.1

Author SHA1 Message Date
732c2f119c More logging 2023-05-26 15:09:26 +02:00
81638f4b4a Path.join joins paths ya know 2023-05-26 14:51:11 +02:00
c547aa6422 favicon <3 2023-05-26 14:49:17 +02:00
d80980512e #28 is a pain in the buttcheeks 2023-05-26 14:47:13 +02:00
f9f802155d #28 why was there a '!' 2023-05-26 14:43:47 +02:00
eef0955009 #28 wrong filesnames 2023-05-26 14:36:02 +02:00
ec25900ac0 resolves #29 start manual execution 2023-05-26 14:32:08 +02:00
e5fe14a09e #28 2023-05-26 14:31:34 +02:00
5dc91095f8 #28 2023-05-26 14:30:52 +02:00
985ac8fc7a Fix #28 coverimages 2023-05-26 14:07:11 +02:00
c9537a9963 #24 2023-05-26 13:39:42 +02:00
4fd3c03804 Styling 2023-05-26 13:30:20 +02:00
a1e9dd0232 resolves #27 Foldernames ending in '.' 2023-05-25 22:22:57 +02:00
aa1f9b1b56 background fade 2023-05-25 22:17:47 +02:00
6069578b6e style choices 2023-05-25 22:15:06 +02:00
a84b768e24 design choices 2023-05-25 22:05:29 +02:00
d1a21af15d resolves #26 2023-05-25 21:58:45 +02:00
9 changed files with 302 additions and 162 deletions

View File

@ -55,12 +55,13 @@ public abstract class Connector
/// <param name="publication">Publication that contains Chapter</param> /// <param name="publication">Publication that contains Chapter</param>
/// <param name="chapter">Chapter with Images to retrieve</param> /// <param name="chapter">Chapter with Images to retrieve</param>
public abstract void DownloadChapter(Publication publication, Chapter chapter); public abstract void DownloadChapter(Publication publication, Chapter chapter);
/// <summary> /// <summary>
/// Retrieves the Cover from the Website /// Retrieves the Cover from the Website
/// </summary> /// </summary>
/// <param name="publication">Publication to retrieve Cover for</param> /// <param name="publication">Publication to retrieve Cover for</param>
public abstract void DownloadCover(Publication publication); /// <param name="settings">TrangaSettings</param>
public abstract void CloneCoverFromCache(Publication publication, TrangaSettings settings);
/// <summary> /// <summary>
/// Saves the series-info to series.json in the Publication Folder /// Saves the series-info to series.json in the Publication Folder
@ -86,7 +87,7 @@ public abstract class Connector
/// <returns>XML-string</returns> /// <returns>XML-string</returns>
protected static string CreateComicInfo(Publication publication, Chapter chapter, Logger? logger) protected static string CreateComicInfo(Publication publication, Chapter chapter, Logger? logger)
{ {
logger?.WriteLine("Connector", $"Creating ComicInfo.Xml for {publication.sortName} Chapter {chapter.volumeNumber} {chapter.chapterNumber}"); logger?.WriteLine("Connector", $"Creating ComicInfo.Xml for {publication.sortName} {publication.internalId} {chapter.volumeNumber}-{chapter.chapterNumber}");
XElement comicInfo = new XElement("ComicInfo", XElement comicInfo = new XElement("ComicInfo",
new XElement("Tags", string.Join(',',publication.tags)), new XElement("Tags", string.Join(',',publication.tags)),
new XElement("LanguageISO", publication.originalLanguage), new XElement("LanguageISO", publication.originalLanguage),
@ -137,11 +138,12 @@ public abstract class Connector
/// <param name="imageUrls">List of URLs to download Images from</param> /// <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="saveArchiveFilePath">Full path to save archive to (without file ending .cbz)</param>
/// <param name="downloadClient">DownloadClient of the connector</param> /// <param name="downloadClient">DownloadClient of the connector</param>
/// <param name="logger"></param>
/// <param name="comicInfoPath">Path of the generate Chapter ComicInfo.xml, if it was generated</param> /// <param name="comicInfoPath">Path of the generate Chapter ComicInfo.xml, if it was generated</param>
/// <param name="requestType">RequestType for RateLimits</param> /// <param name="requestType">RequestType for RateLimits</param>
protected static void DownloadChapterImages(string[] imageUrls, string saveArchiveFilePath, DownloadClient downloadClient, byte requestType, Logger? logger, string? comicInfoPath = null) protected static void DownloadChapterImages(string[] imageUrls, string saveArchiveFilePath, DownloadClient downloadClient, byte requestType, Logger? logger, string? comicInfoPath = null)
{ {
logger?.WriteLine("Connector", "Downloading Images"); logger?.WriteLine("Connector", $"Downloading Images for {saveArchiveFilePath}");
//Check if Publication Directory already exists //Check if Publication Directory already exists
string directoryPath = Path.GetDirectoryName(saveArchiveFilePath)!; string directoryPath = Path.GetDirectoryName(saveArchiveFilePath)!;
if (!Directory.Exists(directoryPath)) if (!Directory.Exists(directoryPath))
@ -159,13 +161,14 @@ public abstract class Connector
{ {
string[] split = imageUrl.Split('.'); string[] split = imageUrl.Split('.');
string extension = split[^1]; string extension = split[^1];
logger?.WriteLine("Connector", $"Downloading Image {chapter + 1}/{imageUrls.Length}");
DownloadImage(imageUrl, Path.Join(tempFolder, $"{chapter++}.{extension}"), downloadClient, requestType); DownloadImage(imageUrl, Path.Join(tempFolder, $"{chapter++}.{extension}"), downloadClient, requestType);
} }
if(comicInfoPath is not null) if(comicInfoPath is not null)
File.Copy(comicInfoPath, Path.Join(tempFolder, "ComicInfo.xml")); File.Copy(comicInfoPath, Path.Join(tempFolder, "ComicInfo.xml"));
logger?.WriteLine("Connector", "Creating archive"); logger?.WriteLine("Connector", $"Creating archive {saveArchiveFilePath}");
//ZIP-it and ship-it //ZIP-it and ship-it
ZipFile.CreateFromDirectory(tempFolder, saveArchiveFilePath); ZipFile.CreateFromDirectory(tempFolder, saveArchiveFilePath);
Directory.Delete(tempFolder, true); //Cleanup Directory.Delete(tempFolder, true); //Cleanup
@ -227,6 +230,7 @@ public abstract class Connector
catch (HttpRequestException e) catch (HttpRequestException e)
{ {
logger?.WriteLine(this.GetType().ToString(), e.Message); logger?.WriteLine(this.GetType().ToString(), e.Message);
logger?.WriteLine(this.GetType().ToString(), $"Waiting {_rateLimit[requestType] * 2}");
Thread.Sleep(_rateLimit[requestType] * 2); Thread.Sleep(_rateLimit[requestType] * 2);
} }
} }

View File

@ -55,6 +55,7 @@ public class MangaDex : Connector
total = result["total"]!.GetValue<int>(); //Update the total number of Publications total = result["total"]!.GetValue<int>(); //Update the total number of Publications
JsonArray mangaInResult = result["data"]!.AsArray(); //Manga-data-Array JsonArray mangaInResult = result["data"]!.AsArray(); //Manga-data-Array
logger?.WriteLine(this.GetType().ToString(), $"Getting publication data.");
//Loop each Manga and extract information from JSON //Loop each Manga and extract information from JSON
foreach (JsonNode? mangeNode in mangaInResult) foreach (JsonNode? mangeNode in mangaInResult)
{ {
@ -142,12 +143,13 @@ public class MangaDex : Connector
} }
} }
logger?.WriteLine(this.GetType().ToString(), $"Done getting publications (title={publicationTitle})");
return publications.ToArray(); return publications.ToArray();
} }
public override Chapter[] GetChapters(Publication publication, string language = "") public override Chapter[] GetChapters(Publication publication, string language = "")
{ {
logger?.WriteLine(this.GetType().ToString(), $"Getting Chapters {publication.sortName} (language={language})"); logger?.WriteLine(this.GetType().ToString(), $"Getting Chapters for {publication.sortName} {publication.internalId} (language={language})");
const int limit = 100; //How many values we want returned at once const int limit = 100; //How many values we want returned at once
int offset = 0; //"Page" int offset = 0; //"Page"
int total = int.MaxValue; //How many total results are there, is updated on first request int total = int.MaxValue; //How many total results are there, is updated on first request
@ -197,12 +199,13 @@ public class MangaDex : Connector
{ {
NumberDecimalSeparator = "." NumberDecimalSeparator = "."
}; };
logger?.WriteLine(this.GetType().ToString(), $"Done getting Chapters for {publication.internalId}");
return chapters.OrderBy(chapter => Convert.ToSingle(chapter.chapterNumber, chapterNumberFormatInfo)).ToArray(); return chapters.OrderBy(chapter => Convert.ToSingle(chapter.chapterNumber, chapterNumberFormatInfo)).ToArray();
} }
public override void DownloadChapter(Publication publication, Chapter chapter) public override void DownloadChapter(Publication publication, Chapter chapter)
{ {
logger?.WriteLine(this.GetType().ToString(), $"Download Chapter {publication.sortName} {chapter.volumeNumber}-{chapter.chapterNumber}"); logger?.WriteLine(this.GetType().ToString(), $"Downloading Chapter-Info {publication.sortName} {publication.internalId} {chapter.volumeNumber}-{chapter.chapterNumber}");
//Request URLs for Chapter-Images //Request URLs for Chapter-Images
DownloadClient.RequestResult requestResult = DownloadClient.RequestResult requestResult =
downloadClient.MakeRequest($"https://api.mangadex.org/at-home/server/{chapter.url}?forcePort443=false'", (byte)RequestType.AtHomeServer); downloadClient.MakeRequest($"https://api.mangadex.org/at-home/server/{chapter.url}?forcePort443=false'", (byte)RequestType.AtHomeServer);
@ -229,9 +232,10 @@ public class MangaDex : Connector
private string? GetCoverUrl(string publicationId, string? posterId) private string? GetCoverUrl(string publicationId, string? posterId)
{ {
logger?.WriteLine(this.GetType().ToString(), $"Getting CoverUrl for {publicationId}");
if (posterId is null) if (posterId is null)
{ {
logger?.WriteLine(this.GetType().ToString(), $"No posterId"); logger?.WriteLine(this.GetType().ToString(), $"No posterId, aborting");
return null; return null;
} }
@ -247,6 +251,7 @@ public class MangaDex : Connector
string fileName = result["data"]!["attributes"]!["fileName"]!.GetValue<string>(); string fileName = result["data"]!["attributes"]!["fileName"]!.GetValue<string>();
string coverUrl = $"https://uploads.mangadex.org/covers/{publicationId}/{fileName}"; string coverUrl = $"https://uploads.mangadex.org/covers/{publicationId}/{fileName}";
logger?.WriteLine(this.GetType().ToString(), $"Got Cover-Url for {publicationId} -> {coverUrl}");
return coverUrl; return coverUrl;
} }
@ -264,14 +269,16 @@ public class MangaDex : Connector
return null; return null;
string author = result["data"]!["attributes"]!["name"]!.GetValue<string>(); string author = result["data"]!["attributes"]!["name"]!.GetValue<string>();
logger?.WriteLine(this.GetType().ToString(), $"Got author {authorId} -> {author}");
return author; return author;
} }
public override void DownloadCover(Publication publication) public override void CloneCoverFromCache(Publication publication, TrangaSettings settings)
{ {
logger?.WriteLine(this.GetType().ToString(), $"Download cover {publication.sortName}"); logger?.WriteLine(this.GetType().ToString(), $"Cloning cover {publication.sortName}");
//Check if Publication already has a Folder and cover //Check if Publication already has a Folder and cover
string publicationFolder = Path.Join(downloadLocation, publication.folderName); string publicationFolder = Path.Join(downloadLocation, publication.folderName);
if(!Directory.Exists(publicationFolder)) if(!Directory.Exists(publicationFolder))
Directory.CreateDirectory(publicationFolder); Directory.CreateDirectory(publicationFolder);
DirectoryInfo dirInfo = new (publicationFolder); DirectoryInfo dirInfo = new (publicationFolder);
@ -281,21 +288,10 @@ public class MangaDex : Connector
return; return;
} }
if (publication.posterUrl is null || publication.posterUrl!.Contains("http")) string fileInCache = Path.Join(settings.coverImageCache, publication.coverFileNameInCache);
{ string newFilePath = Path.Join(publicationFolder, $"cover.{Path.GetFileName(fileInCache).Split('.')[^1]}" );
logger?.WriteLine(this.GetType().ToString(), $"No Poster-URL in publication"); logger?.WriteLine(this.GetType().ToString(), $"Cloning cover {fileInCache} -> {newFilePath}");
return; File.Copy(fileInCache, newFilePath, true);
}
//Get file-extension (jpg, png)
string[] split = publication.posterUrl.Split('.');
string extension = split[^1];
string outFolderPath = Path.Join(downloadLocation, publication.folderName);
Directory.CreateDirectory(outFolderPath);
//Download cover-Image
DownloadImage(publication.posterUrl, Path.Join(downloadLocation, publication.folderName, $"cover.{extension}"), this.downloadClient, (byte)RequestType.AtHomeServer);
} }
private string SaveImage(string url) private string SaveImage(string url)
@ -311,6 +307,7 @@ public class MangaDex : Connector
using MemoryStream ms = new(); using MemoryStream ms = new();
coverResult.result.CopyTo(ms); coverResult.result.CopyTo(ms);
File.WriteAllBytes(saveImagePath, ms.ToArray()); File.WriteAllBytes(saveImagePath, ms.ToArray());
logger?.WriteLine(this.GetType().ToString(), $"Saving image to {saveImagePath}");
return filename; return filename;
} }
} }

View File

@ -42,6 +42,8 @@ public readonly struct Publication
this.status = status; this.status = status;
this.publicationId = publicationId; this.publicationId = publicationId;
this.folderName = string.Concat(LegalCharacters.Matches(sortName)); this.folderName = string.Concat(LegalCharacters.Matches(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)); string onlyLowerLetters = string.Concat(this.sortName.ToLower().Where(Char.IsLetter));
this.internalId = Convert.ToBase64String(Encoding.ASCII.GetBytes($"{onlyLowerLetters}{this.year}")); this.internalId = Convert.ToBase64String(Encoding.ASCII.GetBytes($"{onlyLowerLetters}{this.year}"));
} }

View File

@ -37,7 +37,7 @@ public static class TaskExecutor
switch (trangaTask.task) switch (trangaTask.task)
{ {
case TrangaTask.Task.DownloadNewChapters: case TrangaTask.Task.DownloadNewChapters:
DownloadNewChapters(connector!, (Publication)trangaTask.publication!, trangaTask.language, ref taskManager._chapterCollection); DownloadNewChapters(connector!, (Publication)trangaTask.publication!, trangaTask.language, ref taskManager._chapterCollection, taskManager.settings);
break; break;
case TrangaTask.Task.UpdateChapters: case TrangaTask.Task.UpdateChapters:
UpdateChapters(connector!, (Publication)trangaTask.publication!, trangaTask.language, ref taskManager._chapterCollection); UpdateChapters(connector!, (Publication)trangaTask.publication!, trangaTask.language, ref taskManager._chapterCollection);
@ -90,7 +90,7 @@ public static class TaskExecutor
/// <param name="publication">Publication to check</param> /// <param name="publication">Publication to check</param>
/// <param name="language">Language to receive chapters for</param> /// <param name="language">Language to receive chapters for</param>
/// <param name="chapterCollection"></param> /// <param name="chapterCollection"></param>
private static void DownloadNewChapters(Connector connector, Publication publication, string language, ref Dictionary<Publication, List<Chapter>> chapterCollection) private static void DownloadNewChapters(Connector connector, Publication publication, string language, ref Dictionary<Publication, List<Chapter>> chapterCollection, TrangaSettings settings)
{ {
//Check if Publication already has a Folder //Check if Publication already has a Folder
string publicationFolder = Path.Join(connector.downloadLocation, publication.folderName); string publicationFolder = Path.Join(connector.downloadLocation, publication.folderName);
@ -98,7 +98,7 @@ public static class TaskExecutor
Directory.CreateDirectory(publicationFolder); Directory.CreateDirectory(publicationFolder);
List<Chapter> newChapters = UpdateChapters(connector, publication, language, ref chapterCollection); List<Chapter> newChapters = UpdateChapters(connector, publication, language, ref chapterCollection);
connector.DownloadCover(publication); connector.CloneCoverFromCache(publication, settings);
string seriesInfoPath = Path.Join(publicationFolder, "series.json"); string seriesInfoPath = Path.Join(publicationFolder, "series.json");
if(!File.Exists(seriesInfoPath)) if(!File.Exists(seriesInfoPath))

View File

@ -141,14 +141,11 @@ public class TaskManager
TrangaTask newTask; TrangaTask newTask;
if (task == TrangaTask.Task.UpdateKomgaLibrary) if (task == TrangaTask.Task.UpdateKomgaLibrary)
{ {
newTask = new TrangaTask(task, null, null, reoccurrence, language); newTask = new TrangaTask(task, null, null, reoccurrence);
logger?.WriteLine(this.GetType().ToString(), $"Removing old {task}-Task.");
//Check if same task already exists //Only one UpdateKomgaLibrary Task
// ReSharper disable once SimplifyLinqExpressionUseAll readabilty _allTasks.RemoveWhere(trangaTask => trangaTask.task is TrangaTask.Task.UpdateKomgaLibrary);
if (!_allTasks.Any(trangaTask => trangaTask.task == task)) _allTasks.Add(newTask);
{
_allTasks.Add(newTask);
}
} }
else else
{ {

BIN
Website/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

View File

@ -6,106 +6,117 @@
<link rel="stylesheet" href="style.css"> <link rel="stylesheet" href="style.css">
</head> </head>
<body> <body>
<topbar> <wrapper>
<titlebox> <topbar>
<img src="media/blahaj.png"> <titlebox>
<span>Tranga</span> <img src="media/blahaj.png">
</titlebox> <span>Tranga</span>
<spacer></spacer> </titlebox>
<searchdiv> <spacer></spacer>
<input id="searchbox" placeholder="Filter" type="text"> <searchdiv>
</searchdiv> <input id="searchbox" placeholder="Filter" type="text">
<img id="settingscog" src="media/settings-cogwheel.svg" height="100%" alt="settingscog"> </searchdiv>
</topbar> <img id="settingscog" src="media/settings-cogwheel.svg" height="100%" alt="settingscog">
<viewport> </topbar>
<content> <viewport>
<div id="addPublication"> <content>
<p>+</p> <div id="addPublication">
<p>+</p>
</div>
<publication>
<img src="media/cover.jpg">
<publication-information>
<connector-name class="pill">MangaDex</connector-name>
<publication-name>Tensei Pandemic</publication-name>
</publication-information>
</publication>
</content>
<popup id="addTaskPopup">
<blur-background id="blurBackgroundTaskPopup"></blur-background>
<addtask-window>
<window-titlebar>
<p>Add Task</p>
<img id="closePopupImg" src="media/close-x.svg" alt="Close">
</window-titlebar>
<window-content>
<addtask-settings>
<addtask-setting><label for="selectReccurrence">Recurrence</label><input id="selectReccurrence" type="time" value="01:00:00" step="3600"></addtask-setting>
<addtask-setting><label for="connectors">Connector</label>
<select id="connectors">
<option value=""></option>
</select>
</addtask-setting>
<addtask-setting><label for="searchPublicationQuery">Search Title</label><input id="searchPublicationQuery" type="text"></addtask-setting>
<input type="submit" value="Search" onclick="NewSearch();">
</addtask-settings>
<div id="taskSelectOutput"></div>
</window-content>
</addtask-window>
</popup>
<popup id="publicationViewerPopup">
<blur-background id="blurBackgroundPublicationPopup"></blur-background>
<publication-viewer>
<img id="pubviewcover" src="media/cover.jpg" alt="cover">
<publication-information>
<publication-name id="publicationViewerName">Tensei Pandemic</publication-name>
<publication-author id="publicationViewerAuthor">Imamura Hinata</publication-author>
<publication-description id="publicationViewerDescription">Imamura Hinata is a high school boy with a cute appearance.
Since his trauma with the first love, he wanted to be more manly than anybody else. But one day he woke up to something different…
The total opposite of his ideal male body!
Pandemic love comedy!
</publication-description>
<publication-interactions>
<publication-starttask>Start Task ▶️</publication-starttask>
<publication-delete>Delete Task ❌</publication-delete>
<publication-add>Add Task </publication-add>
</publication-interactions>
</publication-information>
</publication-viewer>
</popup>
<popup id="settingsPopup">
<blur-background id="blurBackgroundSettingsPopup"></blur-background>
<settings>
<span style="font-weight: bold; text-align: center; font-size: 16pt;">Settings</span>
<div>
<p class="title">Download Location:</p>
<span id="downloadLocation"></span>
</div>
<div>
<p class="title">API-URI</p>
<label for="settingApiUri"></label><input placeholder="https://" type="text" id="settingApiUri">
</div>
<komga-settings>
<span class="title">Komga</span>
<div>Configured: <span id="komgaConfigured">✅❌</span></div>
<label for="komgaUrl"></label><input placeholder="URL" id="komgaUrl" type="text">
<label for="komgaUsername"></label><input placeholder="Username" id="komgaUsername" type="text">
<label for="komgaPassword"></label><input placeholder="Password" id="komgaPassword" type="password">
<label for="komgaUpdateTime" style="margin-right: 5px;">Update Time</label><input id="komgaUpdateTime" type="time" value="00:01:00" step="10">
<input type="submit" value="Update" onclick="UpdateKomgaSettings()">
</komga-settings>
</settings>
</popup>
</viewport>
<footer>
<div>
<img src="media/running.svg" alt="running"><div id="tasksRunningTag">0</div>
</div> </div>
<publication> <div>
<img src="media/cover.jpg"> <img src="media/queue.svg" alt="queue"><div id="tasksQueuedTag">0</div>
<publication-information> </div>
<connector-name class="pill">MangaDex</connector-name> <div>
<publication-name>Tensei Pandemic</publication-name> <img src="media/tasks.svg" alt="queue"><div id="totalTasksTag">0</div>
</publication-information> </div>
</publication> <p id="madeWith">Made with Blåhaj 🦈</p>
</content> </footer>
</wrapper>
<popup id="addTaskPopup"> <footer-tag-popup>
<blur-background id="blurBackgroundTaskPopup"></blur-background> <footer-tag-content>
<addtask-window> <footer-tag-task-name>Test</footer-tag-task-name>
<window-titlebar> </footer-tag-content>
<p>Add Task</p> </footer-tag-popup>
<img id="closePopupImg" src="media/close-x.svg" alt="Close">
</window-titlebar>
<window-content>
<addtask-settings>
<addtask-setting><label for="selectReccurrence">Recurrence</label><input id="selectReccurrence" type="time" value="01:00:00" step="3600"></addtask-setting>
<addtask-setting><label for="connectors">Connector</label>
<select id="connectors">
<option value=""></option>
</select>
</addtask-setting>
<addtask-setting><label for="searchPublicationQuery">Search Title</label><input id="searchPublicationQuery" type="text"></addtask-setting>
<input type="submit" value="Search" onclick="NewSearch();">
</addtask-settings>
<div id="taskSelectOutput"></div>
</window-content>
</addtask-window>
</popup>
<popup id="publicationViewerPopup">
<blur-background id="blurBackgroundPublicationPopup"></blur-background>
<publication-viewer>
<img id="pubviewcover" src="media/cover.jpg" alt="cover">
<publication-information>
<publication-name id="publicationViewerName">Tensei Pandemic</publication-name>
<publication-author id="publicationViewerAuthor">Imamura Hinata</publication-author>
<publication-description id="publicationViewerDescription">Imamura Hinata is a high school boy with a cute appearance.
Since his trauma with the first love, he wanted to be more manly than anybody else. But one day he woke up to something different…
The total opposite of his ideal male body!
Pandemic love comedy!
</publication-description>
<publication-delete>Delete Task ❌</publication-delete>
<publication-add>Add Task </publication-add>
</publication-information>
</publication-viewer>
</popup>
<popup id="settingsPopup">
<blur-background id="blurBackgroundSettingsPopup"></blur-background>
<settings>
<span style="font-weight: bold; text-align: center; font-size: 16pt;">Settings</span>
<div>
<p class="title">Download Location:</p>
<span id="downloadLocation"></span>
</div>
<div>
<p class="title">API-URI</p>
<label for="settingApiUri"></label><input placeholder="https://" type="text" id="settingApiUri">
</div>
<komga-settings>
<span class="title">Komga</span>
<div>Configured: <span id="komgaConfigured">✅❌</span></div>
<label for="komgaUrl"></label><input placeholder="URL" id="komgaUrl" type="text">
<label for="komgaUsername"></label><input placeholder="Username" id="komgaUsername" type="text">
<label for="komgaPassword"></label><input placeholder="Password" id="komgaPassword" type="password">
<label for="komgaUpdateTime" style="margin-right: 5px;">Update Time</label><input id="komgaUpdateTime" type="time" value="00:01:00" step="10">
<input type="submit" value="Update" onclick="UpdateKomgaSettings()">
</komga-settings>
</settings>
</popup>
</viewport>
<footer>
<div>
<img src="media/running.svg" alt="running"><div id="tasksRunningTag">0</div>
</div>
<div>
<img src="media/queue.svg" alt="queue"><div id="tasksQueuedTag">0</div>
</div>
<div>
<img src="media/tasks.svg" alt="queue"><div id="totalTasksTag">0</div>
</div>
<p id="madeWith">Made with Blåhaj 🦈</p>
</footer>
<script src="apiConnector.js"></script> <script src="apiConnector.js"></script>
<script src="interaction.js"></script> <script src="interaction.js"></script>
</body> </body>

View File

@ -18,6 +18,7 @@ const publicationViewerAuthor = document.querySelector("#publicationViewerAuthor
const pubviewcover = document.querySelector("#pubviewcover"); const pubviewcover = document.querySelector("#pubviewcover");
const publicationDelete = document.querySelector("publication-delete"); const publicationDelete = document.querySelector("publication-delete");
const publicationAdd = document.querySelector("publication-add"); const publicationAdd = document.querySelector("publication-add");
const publicationTaskStart = document.querySelector("publication-starttask");
const closetaskpopup = document.querySelector("#closePopupImg"); const closetaskpopup = document.querySelector("#closePopupImg");
const settingDownloadLocation = document.querySelector("#downloadLocation"); const settingDownloadLocation = document.querySelector("#downloadLocation");
const settingKomgaUrl = document.querySelector("#komgaUrl"); const settingKomgaUrl = document.querySelector("#komgaUrl");
@ -29,6 +30,8 @@ const settingApiUri = document.querySelector("#settingApiUri");
const tagTasksRunning = document.querySelector("#tasksRunningTag"); const tagTasksRunning = document.querySelector("#tasksRunningTag");
const tagTasksQueued = document.querySelector("#tasksQueuedTag"); const tagTasksQueued = document.querySelector("#tasksQueuedTag");
const tagTasksTotal = document.querySelector("#totalTasksTag"); const tagTasksTotal = document.querySelector("#totalTasksTag");
const tagTaskPopup = document.querySelector("footer-tag-popup");
const tagTasksPopupContent = document.querySelector("footer-tag-content");
settingsCog.addEventListener("click", () => OpenSettings()); settingsCog.addEventListener("click", () => OpenSettings());
document.querySelector("#blurBackgroundSettingsPopup").addEventListener("click", () => HideSettings()); document.querySelector("#blurBackgroundSettingsPopup").addEventListener("click", () => HideSettings());
@ -37,6 +40,7 @@ document.querySelector("#blurBackgroundTaskPopup").addEventListener("click", ()
document.querySelector("#blurBackgroundPublicationPopup").addEventListener("click", () => HidePublicationPopup()); document.querySelector("#blurBackgroundPublicationPopup").addEventListener("click", () => HidePublicationPopup());
publicationDelete.addEventListener("click", () => DeleteTaskClick()); publicationDelete.addEventListener("click", () => DeleteTaskClick());
publicationAdd.addEventListener("click", () => AddTaskClick()); publicationAdd.addEventListener("click", () => AddTaskClick());
publicationTaskStart.addEventListener("click", () => StartTaskClick());
settingApiUri.addEventListener("keypress", (event) => { settingApiUri.addEventListener("keypress", (event) => {
if(event.key === "Enter"){ if(event.key === "Enter"){
apiUri = settingApiUri.value; apiUri = settingApiUri.value;
@ -49,6 +53,12 @@ searchPublicationQuery.addEventListener("keypress", (event) => {
NewSearch(); NewSearch();
} }
}); });
tagTasksRunning.addEventListener("mouseover", (event) => ShowRunningTasks(event));
tagTasksRunning.addEventListener("mouseout", () => CloseTasksPopup());
tagTasksQueued.addEventListener("mouseover", (event) => ShowQueuedTasks(event));
tagTasksQueued.addEventListener("mouseout", () => CloseTasksPopup());
tagTasksTotal.addEventListener("mouseover", (event) => ShowAllTasks(event));
tagTasksTotal.addEventListener("mouseout", () => CloseTasksPopup());
let availableConnectors; let availableConnectors;
GetAvailableControllers() GetAvailableControllers()
@ -122,6 +132,12 @@ function AddTaskClick(){
HidePublicationPopup(); HidePublicationPopup();
} }
function StartTaskClick(){
var toEditTask = tasks.filter(task => task.publication.internalId == toEditId)[0];
StartTask("DownloadNewChapters", toEditTask.connectorName, toEditId);
HidePublicationPopup();
}
function ResetContent(){ function ResetContent(){
//Delete everything //Delete everything
tasksContent.replaceChildren(); tasksContent.replaceChildren();
@ -136,8 +152,6 @@ function ResetContent(){
tasksContent.appendChild(add); tasksContent.appendChild(add);
} }
function ShowPublicationViewerWindow(publicationId, event, add){ function ShowPublicationViewerWindow(publicationId, event, add){
//Show popup //Show popup
publicationViewerPopup.style.display = "block"; publicationViewerPopup.style.display = "block";
@ -162,12 +176,14 @@ function ShowPublicationViewerWindow(publicationId, event, add){
//Check what action should be listed //Check what action should be listed
if(add){ if(add){
publicationAdd.style.display = "block"; publicationAdd.style.display = "initial";
publicationDelete.style.display = "none"; publicationDelete.style.display = "none";
publicationTaskStart.style.display = "none";
} }
else{ else{
publicationAdd.style.display = "none"; publicationAdd.style.display = "none";
publicationDelete.style.display = "block"; publicationDelete.style.display = "initial";
publicationTaskStart.style.display = "initial";
} }
} }
@ -219,6 +235,7 @@ function GetSettingsClick(){
}); });
GetKomgaTask().then(json => { GetKomgaTask().then(json => {
settingKomgaTime.value = json[0].reoccurrence;
if(json.length > 0) if(json.length > 0)
settingKomgaConfigured.innerText = "✅"; settingKomgaConfigured.innerText = "✅";
else else
@ -227,17 +244,79 @@ function GetSettingsClick(){
} }
function UpdateKomgaSettings(){ function UpdateKomgaSettings(){
var auth = utf8_to_b64(`${settingKomgaUser.value}:${settingKomgaPass.value}`); if(settingKomgaUser.value != "" && settingKomgaPass != ""){
console.log(auth); var auth = utf8_to_b64(`${settingKomgaUser.value}:${settingKomgaPass.value}`);
UpdateSettings("", settingKomgaUrl.value, auth); console.log(auth);
if(settingKomgaUrl.value != "")
UpdateSettings("", settingKomgaUrl.value, auth);
else
UpdateSettings("", settingKomgaUrl.placeholder, auth);
}
CreateTask("UpdateKomgaLibrary", settingKomgaTime.value, "","",""); CreateTask("UpdateKomgaLibrary", settingKomgaTime.value, "","","");
setTimeout(() => GetSettingsClick(), 500); setTimeout(() => GetSettingsClick(), 100);
} }
function utf8_to_b64( str ) { function utf8_to_b64( str ) {
return window.btoa(unescape(encodeURIComponent( str ))); return window.btoa(unescape(encodeURIComponent( str )));
} }
function ShowRunningTasks(event){
GetRunningTasks()
.then(json => {
tagTasksPopupContent.replaceChildren();
json.forEach(task => {
console.log(task);
if(task.publication != null){
var taskname = document.createElement("footer-tag-task-name");
taskname.innerText = task.publication.sortName;
tagTasksPopupContent.appendChild(taskname);
}
});
if(tagTasksPopupContent.children.length > 0){
tagTaskPopup.style.display = "block";
tagTaskPopup.style.left = `${tagTasksRunning.offsetLeft - 20}px`;
}
});
}
function ShowQueuedTasks(event){
GetQueue()
.then(json => {
tagTasksPopupContent.replaceChildren();
json.forEach(task => {
var taskname = document.createElement("footer-tag-task-name");
taskname.innerText = task.publication.sortName;
tagTasksPopupContent.appendChild(taskname);
});
if(json.length > 0){
tagTaskPopup.style.display = "block";
tagTaskPopup.style.left = `${tagTasksQueued.offsetLeft- 20}px`;
}
});
}
function ShowAllTasks(event){
GetDownloadTasks()
.then(json => {
tagTasksPopupContent.replaceChildren();
json.forEach(task => {
var taskname = document.createElement("footer-tag-task-name");
taskname.innerText = task.publication.sortName;
tagTasksPopupContent.appendChild(taskname);
});
if(json.length > 0){
tagTaskPopup.style.display = "block";
tagTaskPopup.style.left = `${tagTasksTotal.offsetLeft - 20}px`;
}
});
}
function CloseTasksPopup(){
tagTaskPopup.style.display = "none";
}
//Resets the tasks shown //Resets the tasks shown
ResetContent(); ResetContent();
//Get Tasks and show them //Get Tasks and show them

View File

@ -1,5 +1,5 @@
:root{ :root{
--background-color: #eee; --background-color: #030304;
--second-background-color: #fff; --second-background-color: #fff;
--primary-color: #f5a9b8; --primary-color: #f5a9b8;
--secondary-color: #5bcefa; --secondary-color: #5bcefa;
@ -11,15 +11,19 @@
body{ body{
padding: 0; padding: 0;
margin: 0; margin: 0;
display: flex;
flex-flow: column;
flex-wrap: nowrap;
height: 100vh; height: 100vh;
background-color: var(--background-color); background-color: var(--background-color);
font-family: "Inter", sans-serif; font-family: "Inter", sans-serif;
overflow-x: hidden; overflow-x: hidden;
} }
wrapper {
display: flex;
flex-flow: column;
flex-wrap: nowrap;
height: 100vh;
}
background-placeholder{ background-placeholder{
background-color: var(--second-background-color); background-color: var(--second-background-color);
opacity: 1; opacity: 1;
@ -229,7 +233,7 @@ publication::after{
left: 0; top: 0; left: 0; top: 0;
border-radius: 5px; border-radius: 5px;
width: 100%; height: 100%; width: 100%; height: 100%;
background: linear-gradient(rgba(0,0,0,0.7), rgba(0, 0, 0, 0.6),rgba(0, 0, 0, 0.2)); background: linear-gradient(rgba(0,0,0,0.8), rgba(0, 0, 0, 0.7),rgba(0, 0, 0, 0.2));
} }
publication-information { publication-information {
@ -379,9 +383,6 @@ publication-viewer{
background-color: var(--accent-color); background-color: var(--accent-color);
border-radius: 5px; border-radius: 5px;
overflow: hidden; overflow: hidden;
}
publication-viewer{
padding: 30px; padding: 30px;
} }
@ -431,20 +432,69 @@ publication-viewer publication-information publication-description {
overflow-x: scroll; overflow-x: scroll;
} }
publication-viewer publication-information publication-delete { publication-viewer publication-information publication-interactions {
display: flex;
flex-direction: row;
position: absolute; position: absolute;
bottom: 0px; justify-content: end;
right: 0px; align-items: start;
color: red; bottom: 0;
margin: 20px; left: 0;
width: 100%;
}
publication-viewer publication-information publication-interactions > * {
margin: 0 10px 10px 10px;
font-size: 16pt; font-size: 16pt;
} }
publication-viewer publication-information publication-add { publication-viewer publication-information publication-interactions publication-starttask {
position: absolute; color: var(--secondary-color);
bottom: 0px; }
right: 0px;
publication-viewer publication-information publication-interactions publication-delete {
color: red;
}
publication-viewer publication-information publication-interactions publication-add {
color: limegreen; color: limegreen;
margin: 20px; }
font-size: 16pt;
footer-tag-popup {
display: none;
padding: 2px 4px;
position: fixed;
bottom: 58px;
left: 20px;
background-color: var(--second-background-color);
z-index: 8;
border-radius: 5px;
max-height: 400px;
}
footer-tag-content{
position: relative;
max-height: 400px;
display: flex;
flex-direction: column;
flex-wrap: nowrap;
overflow-y: scroll;
}
footer-tag-content > * {
margin: 2px 0;
}
footer-tag-popup::before{
content: "";
width: 0;
height: 0;
position: absolute;
border-right: 10px solid var(--second-background-color);
border-left: 10px solid transparent;
border-top: 10px solid var(--second-background-color);
border-bottom: 10px solid transparent;
left: 0px;
bottom: -17px;
border-radius: 0 0 0 5px;
} }