diff --git a/README.md b/README.md new file mode 100644 index 0000000..fed1b32 --- /dev/null +++ b/README.md @@ -0,0 +1,123 @@ + + + + +
+
+ +

Tranga-Website

+ +

+ Automatic Manga and Metadata downloader +

+

+ This is the Website for Tranga (API) +

+
+ + + + +
+ Table of Contents +
    +
  1. + About The Project + +
  2. +
  3. + Getting Started +
  4. +
  5. Roadmap
  6. +
  7. Contributing
  8. +
  9. License
  10. +
  11. Acknowledgments
  12. +
+
+ + + + +## About The Project + +Tranga-Website is the Web-frontend to [Tranga](https://github.com/C9Glax/tranga) (the API). It displays information aquired from Tranga and can create Jobs (Manga-Downloads). + +### What this does do (and nothing else) + +This repo makes HTTP-requests to the [Tranga-API](https://github.com/C9Glax/tranga) to display it's present configuration. + +### Built With + +- nginx +- HTML, CSS, and barebones Javascript +- 💙 Blåhaj 🦈 + +

(back to top)

+ + +## Getting Started + +There is a single release: + + +### Docker + +Download [docker-compose.yaml](https://github.com/C9Glax/tranga-website/blob/cuttingedge/docker-compose.yaml) and configure to your needs. +The `docker-compose` also includes [Tranga](https://github.com/C9Glax/tranga) as backend. For its configuration refer to the repo README. + + +## Roadmap + +- [ ] ❓ + +See the [open issues](https://github.com/C9Glax/tranga-website/issues) for a full list of proposed features (and known issues). + +

(back to top)

+ + + + +## Contributing + +The following is copy & pasted: + +Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**. + +If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement". +Don't forget to give the project a star! Thanks again! + +1. Fork the Project +2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`) +3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`) +4. Push to the Branch (`git push origin feature/AmazingFeature`) +5. Open a Pull Request + +

(back to top)

+ + + + +## License + +Distributed under the GNU GPLv3 License. See `LICENSE.txt` for more information. + +

(back to top)

+ + + + +## Acknowledgments + +* [Choose an Open Source License](https://choosealicense.com) +* [Font Awesome](https://fontawesome.com) +* [Best-README-Template](https://github.com/othneildrew/Best-README-Template/tree/master) + +

(back to top)

diff --git a/Screenshots/Screenshot 2023-09-08 at 20-03-13 Tranga.png b/Screenshots/Screenshot 2023-09-08 at 20-03-13 Tranga.png new file mode 100644 index 0000000..c30b3a8 Binary files /dev/null and b/Screenshots/Screenshot 2023-09-08 at 20-03-13 Tranga.png differ diff --git a/Screenshots/Screenshot 2023-09-08 at 20-03-37 Tranga.png b/Screenshots/Screenshot 2023-09-08 at 20-03-37 Tranga.png new file mode 100644 index 0000000..670743c Binary files /dev/null and b/Screenshots/Screenshot 2023-09-08 at 20-03-37 Tranga.png differ diff --git a/Screenshots/Screenshot 2023-09-08 at 20-03-45 Tranga.png b/Screenshots/Screenshot 2023-09-08 at 20-03-45 Tranga.png new file mode 100644 index 0000000..2f38769 Binary files /dev/null and b/Screenshots/Screenshot 2023-09-08 at 20-03-45 Tranga.png differ diff --git a/Screenshots/Screenshot 2023-09-08 at 20-03-58 Tranga.png b/Screenshots/Screenshot 2023-09-08 at 20-03-58 Tranga.png new file mode 100644 index 0000000..a287f41 Binary files /dev/null and b/Screenshots/Screenshot 2023-09-08 at 20-03-58 Tranga.png differ diff --git a/Screenshots/Screenshot 2023-09-08 at 20-04-41 Tranga.png b/Screenshots/Screenshot 2023-09-08 at 20-04-41 Tranga.png new file mode 100644 index 0000000..68c191f Binary files /dev/null and b/Screenshots/Screenshot 2023-09-08 at 20-04-41 Tranga.png differ diff --git a/Website/apiConnector.js b/Website/apiConnector.js index 560d3a5..f3aec41 100644 --- a/Website/apiConnector.js +++ b/Website/apiConnector.js @@ -3,6 +3,15 @@ if(getCookie("apiUri") != ""){ apiUri = getCookie("apiUri"); } +setCookie("apiUri", apiUri); + +function setCookie(cname, cvalue) { + const d = new Date(); + d.setTime(d.getTime() + (365*24*60*60*1000)); + let expires = "expires="+ d.toUTCString(); + document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/;samesite=strict"; +} + function getCookie(cname) { let name = cname + "="; let decodedCookie = decodeURIComponent(document.cookie); @@ -42,127 +51,144 @@ function DeleteData(uri){ }); } +async function Ping(){ + let ret = await GetData(`${apiUri}/Ping`); + return ret; +} + async function GetAvailableControllers(){ var uri = apiUri + "/Connectors"; let json = await GetData(uri); return json; } -async function GetPublicationFromConnector(connectorName, title){ - var uri = apiUri + `/Publications/FromConnector?connectorName=${connectorName}&title=${title}`; +async function GetPublicationFromConnector(connector, title){ + var uri; + if(title.includes("http")){ + uri = `${apiUri}/Manga/FromConnector?connector=${connector}&url=${title}`; + }else{ + uri = `${apiUri}/Manga/FromConnector?connector=${connector}&title=${title}`; + } + let json = await GetData(uri); + return json; +} + +async function GetChapters(connector, internalId, language){ + var uri = `${apiUri}/Manga/Chapters?connector=${connector}&internalId=${internalId}&translatedLanguage=${language}`; let json = await GetData(uri); return json; } -async function GetKnownPublications(){ - var uri = apiUri + "/Publications/Known"; +function GetCoverUrl(internalId){ + return `${apiUri}/Manga/Cover?internalId=${internalId}`; +} + +async function GetAllJobs(){ + var uri = `${apiUri}/Jobs`; + let json = await GetData(uri); + return json; +} + +async function GetRunningJobs(){ + var uri = `${apiUri}/Jobs/Running`; let json = await GetData(uri); return json; } -async function GetPublication(internalId){ - var uri = apiUri + `/Publications/Known?internalId=${internalId}`; +async function GetWaitingJobs(){ + var uri = `${apiUri}/Jobs/Waiting`; let json = await GetData(uri); return json; } -async function GetChapters(internalId, connectorName, onlyNew, language){ - var uri = apiUri + `/Publications/Chapters?internalId=${internalId}&connectorName=${connectorName}&onlyNew=${onlyNew}&language=${language}`; - let json = await GetData(uri); - return json; +async function GetMonitorJobs(){ + var uri = `${apiUri}/Jobs/MonitorJobs`; + let json = await GetData(uri); + return json; } -async function GetTaskTypes(){ - var uri = apiUri + "/Tasks/Types"; - let json = await GetData(uri); - return json; -} -async function GetRunningTasks(){ - var uri = apiUri + "/Tasks/RunningTasks"; - let json = await GetData(uri); - return json; -} - -async function GetDownloadTasks(){ - var uri = apiUri + "/Tasks?taskType=MonitorPublication"; +async function GetProgress(jobId){ + var uri = `${apiUri}/Jobs/Progress?jobId=${jobId}`; let json = await GetData(uri); return json; } async function GetSettings(){ - var uri = apiUri + "/Settings"; + var uri = `${apiUri}/Settings`; let json = await GetData(uri); return json; } -async function GetKomgaTask(){ - var uri = apiUri + "/Tasks?taskType=UpdateLibraries"; - let json = await GetData(uri); - return json; +async function GetAvailableNotificationConnectors(){ + var uri = `${apiUri}/NotificationConnectors/Types`; + let json = await GetData(uri); + return json; } -function CreateMonitorTask(connectorName, internalId, reoccurrence, language){ - var uri = apiUri + `/Tasks/CreateMonitorTask?connectorName=${connectorName}&internalId=${internalId}&reoccurrenceTime=${reoccurrence}&language=${language}`; +async function GetNotificationConnectors(){ + var uri = `${apiUri}/NotificationConnectors`; + let json = await GetData(uri); + return json; +} + +async function GetAvailableLibraryConnectors(){ + var uri = `${apiUri}/LibraryConnectors/Types`; + let json = await GetData(uri); + return json; +} + +async function GetLibraryConnectors(){ + var uri = `${apiUri}/LibraryConnectors`; + let json = await GetData(uri); + return json; +} + +function CreateMonitorJob(connector, internalId, language){ + var uri = `${apiUri}/Jobs/MonitorManga?connector=${connector}&internalId=${internalId}&interval=03:00:00&translatedLanguage=${language}`; PostData(uri); } -function CreateDownloadChaptersTask(connectorName, internalId, chapters, language){ - var uri = apiUri + `/Tasks/CreateDownloadChaptersTask?connectorName=${connectorName}&internalId=${internalId}&chapters=${chapters}&language=${language}`; +function CreateDownloadNewChaptersJob(connector, internalId, language){ + var uri = `${apiUri}/Jobs/DownloadNewChapters?connector=${connector}&internalId=${internalId}&translatedLanguage=${language}`; PostData(uri); } -function StartTask(taskType, connectorName, internalId){ - var uri = apiUri + `/Tasks/Start?taskType=${taskType}&connectorName=${connectorName}&internalId=${internalId}`; - PostData(uri); -} - -function EnqueueTask(taskType, connectorName, publicationId){ - var uri = apiUri + `/Queue/Enqueue?taskType=${taskType}&connectorName=${connectorName}&publicationId=${publicationId}`; +function StartJob(jobId){ + var uri = `${apiUri}/Jobs/StartNow?jobId=${jobId}`; PostData(uri); } function UpdateDownloadLocation(downloadLocation){ - var uri = apiUri + "/Settings/Update?" - uri += "&downloadLocation="+downloadLocation; - PostData(uri); + var uri = `${apiUri}/Settings/UpdateDownloadLocation?downloadLocation=${downloadLocation}`; + PostData(uri); } function UpdateKomga(komgaUrl, komgaAuth){ - var uri = apiUri + "/Settings/Update?" - uri += `&komgaUrl=${komgaUrl}&komgaAuth=${komgaAuth}`; + var uri = `${apiUri}/LibraryConnectors/Update?libraryConnector=Komga&komgaUrl=${komgaUrl}&komgaAuth=${komgaAuth}`; PostData(uri); } -function UpdateKavita(kavitaUrl, kavitaUser, kavitaPass){ - var uri = apiUri + "/Settings/Update?" - uri += `&kavitaUrl=${kavitaUrl}&kavitaUsername=${kavitaUser}&kavitaPassword=${kavitaPass}`; +function UpdateKavita(kavitaUrl, kavitaUsername, kavitaPassword){ + var uri = `${apiUri}/LibraryConnectors/Update?libraryConnector=Kavita&kavitaUrl=${kavitaUrl}&kavitaUsername=${kavitaUsername}&kavitaPassword={kavitaPassword}`; PostData(uri); } function UpdateGotify(gotifyUrl, gotifyAppToken){ - var uri = apiUri + "/Settings/Update?" - uri += `&gotifyUrl=${gotifyUrl}&gotifyAppToken=${gotifyAppToken}`; + var uri = `${apiUri}/NotificationConnectors/Update?notificationConnector=Gotify&gotifyUrl=${gotifyUrl}&gotifyAppToken=${gotifyAppToken}`; PostData(uri); } function UpdateLunaSea(lunaseaWebhook){ - var uri = apiUri + "/Settings/Update?" - uri += `&lunaseaWebhook=${lunaseaWebhook}`; + var uri = `${apiUri}/NotificationConnectors/Update?notificationConnector=LunaSea&lunaseaWebhook=${lunaseaWebhook}`; PostData(uri); } -function DeleteTask(taskType, connectorName, publicationId){ - var uri = apiUri + `/Tasks?taskType=${taskType}&connectorName=${connectorName}&publicationId=${publicationId}`; +function RemoveJob(jobId){ + var uri = `${apiUri}/Jobs?jobId=${jobId}`; DeleteData(uri); } -function DequeueTask(taskType, connectorName, publicationId){ - var uri = apiUri + `/Queue/Dequeue?taskType=${taskType}&connectorName=${connectorName}&publicationId=${publicationId}`; - DeleteData(uri); -} - -async function GetQueue(){ - var uri = apiUri + "/Queue/List"; - let json = await GetData(uri); - return json; +function CancelJob(jobId){ + var uri = `${apiUri}/Jobs/Cancel?jobId=${jobId}`; + PostData(uri); } \ No newline at end of file diff --git a/Website/index.html b/Website/index.html index d6e9667..6ae6379 100644 --- a/Website/index.html +++ b/Website/index.html @@ -20,94 +20,41 @@ settingscog +
+ +
+

Check your Settings > API-URI

+

+

- + cover - MangaDex - Tensei Pandemic + Sample + Best Manga there is
- - - - Select Publication - -
- - -
-
- -
- -
-
-
-
- - - - - Create Task: Monitor Publication - -
- Run every - hours - minutes - -
-
-
-
- - - - - Create Task: Download Chapter(s) - -
- -
-
- -
-
-
-
- - - - - cover - - Tensei Pandemic - - Imamura Hinata - 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! - - - Start Task ▶️ - Delete Task ❌ - Monitor ➕ - Download Chapter ➕ - - - + + +
+ + +
+
- + Settings @@ -145,29 +92,53 @@
- - +
+ + + + + cover + + Best Manga there is + A Manga + Glax + + An interesting description. The description is very intriguing, yet wholesome. + + + Start Job ▶️ + Cancel Job ❌ + Delete Job 🗑️ + Monitor ➕ + Download Chapter 📥 + + + + - - + + - Task Progress - - - +
+
+
+
+
+
+ diff --git a/Website/interaction.js b/Website/interaction.js index d834a8a..ccd8351 100644 --- a/Website/interaction.js +++ b/Website/interaction.js @@ -1,31 +1,26 @@ -let publications = []; -let tasks = []; -let toEditId; +let runningJobs = []; +let waitingJobs = []; +let notificationConnectorTypes = []; +let libraryConnectorTypes = []; +let selectedManga; +let selectedJob; const searchBox = document.querySelector("#searchbox"); -const searchPublicationQuery = document.querySelector("#searchPublicationQuery"); -const selectPublication = document.querySelector("#taskSelectOutput"); -const connectorSelect = document.querySelector("#connectors"); const settingsPopup = document.querySelector("#settingsPopup"); const settingsCog = document.querySelector("#settingscog"); -const selectRecurrence = document.querySelector("#selectReccurrence"); const tasksContent = document.querySelector("content"); -const selectPublicationPopup = document.querySelector("#selectPublicationPopup"); -const createMonitorTaskButton = document.querySelector("#createMonitorTaskButton"); -const createDownloadChapterTaskButton = document.querySelector("#createDownloadChapterTaskButton"); -const createMonitorTaskPopup = document.querySelector("#createMonitorTaskPopup"); -const createDownloadChaptersTask = document.querySelector("#createDownloadChaptersTask"); -const chapterOutput = document.querySelector("#chapterOutput"); -const selectedChapters = document.querySelector("#selectedChapters"); -const publicationViewerPopup = document.querySelector("#publicationViewerPopup"); -const publicationViewerWindow = document.querySelector("publication-viewer"); -const publicationViewerDescription = document.querySelector("#publicationViewerDescription"); -const publicationViewerName = document.querySelector("#publicationViewerName"); -const publicationViewerTags = document.querySelector("#publicationViewerTags"); -const publicationViewerAuthor = document.querySelector("#publicationViewerAuthor"); -const pubviewcover = document.querySelector("#pubviewcover"); -const publicationDelete = document.querySelector("publication-delete"); -const publicationTaskStart = document.querySelector("publication-starttask"); +const createMonitorTaskButton = document.querySelector("#createMonitoJobButton"); +const createDownloadChapterTaskButton = document.querySelector("#createDownloadChapterJobButton"); +const startJobButton = document.querySelector("#startJobButton"); +const cancelJobButton = document.querySelector("#cancelJobButton"); +const deleteJobButton = document.querySelector("#deleteJobButton"); +const mangaViewerPopup = document.querySelector("#publicationViewerPopup"); +const mangaViewerWindow = document.querySelector("publication-viewer"); +const mangaViewerDescription = document.querySelector("#publicationViewerDescription"); +const mangaViewerName = document.querySelector("#publicationViewerName"); +const mangaViewerTags = document.querySelector("#publicationViewerTags"); +const mangaViewerAuthor = document.querySelector("#publicationViewerAuthor"); +const mangaViewCover = document.querySelector("#pubviewcover"); const settingDownloadLocation = document.querySelector("#downloadLocation"); const settingKomgaUrl = document.querySelector("#komgaUrl"); const settingKomgaUser = document.querySelector("#komgaUsername"); @@ -36,166 +31,55 @@ const settingKavitaPass = document.querySelector("#kavitaPassword"); const settingGotifyUrl = document.querySelector("#gotifyUrl"); const settingGotifyAppToken = document.querySelector("#gotifyAppToken"); const settingLunaseaWebhook = document.querySelector("#lunaseaWebhook"); -const libraryUpdateTime = document.querySelector("#libraryUpdateTime"); const settingKomgaConfigured = document.querySelector("#komgaConfigured"); const settingKavitaConfigured = document.querySelector("#kavitaConfigured"); const settingGotifyConfigured = document.querySelector("#gotifyConfigured"); const settingLunaseaConfigured = document.querySelector("#lunaseaConfigured"); const settingApiUri = document.querySelector("#settingApiUri"); -const tagTasksRunning = document.querySelector("#tasksRunningTag"); -const tagTasksQueued = document.querySelector("#tasksQueuedTag"); -const downloadTasksPopup = document.querySelector("#downloadTasksPopup"); -const downloadTasksOutput = downloadTasksPopup.querySelector("popup-content"); +const newMangaPopup = document.querySelector("#newMangaPopup"); +const newMangaConnector = document.querySelector("#newMangaConnector"); +const newMangaTitle = document.querySelector("#newMangaTitle"); +const newMangaResult = document.querySelector("#newMangaResult"); +const newMangaTranslatedLanguage = document.querySelector("#newMangaTranslatedLanguage"); +const jobsRunningTag = document.querySelector("#jobsRunningTag"); +const jobsQueuedTag = document.querySelector("#jobsQueuedTag"); +const loaderdiv = document.querySelector('#loaderdiv'); +const jobStatusView = document.querySelector("#jobStatusView"); +const jobStatusRunning = document.querySelector("#jobStatusRunning"); +const jobStatusWaiting = document.querySelector("#jobStatusWaiting"); -searchBox.addEventListener("keyup", () => FilterResults()); -settingsCog.addEventListener("click", () => OpenSettings()); -document.querySelector("#blurBackgroundSettingsPopup").addEventListener("click", () => settingsPopup.style.display = "none"); -document.querySelector("#blurBackgroundTaskPopup").addEventListener("click", () => selectPublicationPopup.style.display = "none"); -document.querySelector("#blurBackgroundPublicationPopup").addEventListener("click", () => HidePublicationPopup()); -document.querySelector("#blurBackgroundCreateMonitorTaskPopup").addEventListener("click", () => createMonitorTaskPopup.style.display = "none"); -document.querySelector("#blurBackgroundCreateDownloadChaptersTask").addEventListener("click", () => createDownloadChaptersTask.style.display = "none"); -document.querySelector("#blurBackgroundTasksQueuePopup").addEventListener("click", () => downloadTasksPopup.style.display = "none"); -selectedChapters.addEventListener("keypress", (event) => { - if(event.key === "Enter"){ - DownloadChapterTaskClick(); - } -}) -publicationDelete.addEventListener("click", () => DeleteTaskClick()); -createMonitorTaskButton.addEventListener("click", () => { - HidePublicationPopup(); - createMonitorTaskPopup.style.display = "block"; -}); -createDownloadChapterTaskButton.addEventListener("click", () => { - HidePublicationPopup(); - OpenDownloadChapterTaskPopup(); -}); -publicationTaskStart.addEventListener("click", () => StartTaskClick()); -searchPublicationQuery.addEventListener("keypress", (event) => { - if(event.key === "Enter"){ - NewSearch(); - } -}); - - -GetAvailableControllers() - .then(json => availableConnectors = json) - .then(json => - json.forEach(connector => { - var option = document.createElement('option'); - option.value = connector; - option.innerText = connector; - connectorSelect.appendChild(option); - }) - ); - - -function NewSearch(){ - //Disable inputs - connectorSelect.disabled = true; - searchPublicationQuery.disabled = true; - //Waitcursor - document.body.style.cursor = "wait"; - - //Empty previous results - selectPublication.replaceChildren(); - GetPublicationFromConnector(connectorSelect.value, searchPublicationQuery.value) - .then(json => - json.forEach(publication => { - var option = CreatePublication(publication, connectorSelect.value); - option.addEventListener("click", (mouseEvent) => { - ShowPublicationViewerWindow(publication.internalId, mouseEvent, true); - }); - selectPublication.appendChild(option); - } - )) - .then(() => { - //Re-enable inputs - connectorSelect.disabled = false; - searchPublicationQuery.disabled = false; - //Cursor - document.body.style.cursor = "initial"; - }); -} - -//Returns a new "Publication" Item to display in the tasks section -function CreatePublication(publication, connector){ - var publicationElement = document.createElement('publication'); - publicationElement.setAttribute("id", publication.internalId); - var img = document.createElement('img'); - img.src = `imageCache/${publication.coverFileNameInCache}`; - publicationElement.appendChild(img); - var info = document.createElement('publication-information'); - var connectorName = document.createElement('connector-name'); - connectorName.innerText = connector; - connectorName.className = "pill"; - info.appendChild(connectorName); - var publicationName = document.createElement('publication-name'); - publicationName.innerText = publication.sortName; - info.appendChild(publicationName); - publicationElement.appendChild(info); - if(publications.filter(pub => pub.internalId === publication.internalId) < 1) - publications.push(publication); - return publicationElement; -} - -function AddMonitorTask(){ - var hours = document.querySelector("#hours").value; - var minutes = document.querySelector("#minutes").value; - CreateMonitorTask(connectorSelect.value, toEditId, `${hours}:${minutes}:00`, "en"); - HidePublicationPopup(); - createMonitorTaskPopup.style.display = "none"; - selectPublicationPopup.style.display = "none"; -} - -function OpenDownloadChapterTaskPopup(){ - selectedChapters.value = ""; - chapterOutput.replaceChildren(); - createDownloadChaptersTask.style.display = "block"; - GetChapters(toEditId, connectorSelect.value, true, "en").then((json) => { - var i = 0; - json.forEach(chapter => { - var chapterDom = document.createElement("div"); - var indexDom = document.createElement("span"); - indexDom.className = "index"; - indexDom.innerText = i++; - chapterDom.appendChild(indexDom); - - var volDom = document.createElement("span"); - volDom.className = "vol"; - volDom.innerText = chapter.volumeNumber; - chapterDom.appendChild(volDom); - - var chDom = document.createElement("span"); - chDom.className = "ch"; - chDom.innerText = chapter.chapterNumber; - chapterDom.appendChild(chDom); - - var titleDom = document.createElement("span"); - titleDom.innerText = chapter.name; - chapterDom.appendChild(titleDom); - chapterOutput.appendChild(chapterDom); - }); +function Setup(){ + Ping().then((ret) => { + loaderdiv.style.display = 'none'; + + GetAvailableNotificationConnectors().then((json) => { + json.forEach(connector => { + notificationConnectorTypes[connector.Key] = connector.Value; + }); }); -} -function DownloadChapterTaskClick(){ - CreateDownloadChaptersTask(connectorSelect.value, toEditId, selectedChapters.value, "en"); - HidePublicationPopup(); - createDownloadChaptersTask.style.display = "none"; - selectPublicationPopup.style.display = "none"; -} + GetAvailableLibraryConnectors().then((json) => { + json.forEach(connector => { + libraryConnectorTypes[connector.Key] = connector.Value; + }); + }); -function DeleteTaskClick(){ - taskToDelete = tasks.filter(tTask => tTask.publication.internalId === toEditId)[0]; - DeleteTask("MonitorPublication", taskToDelete.connectorName, toEditId); - HidePublicationPopup(); -} - -function StartTaskClick(){ - var toEditTask = tasks.filter(task => task.publication.internalId == toEditId)[0]; - StartTask("MonitorPublication", toEditTask.connectorName, toEditId); - HidePublicationPopup(); + GetAvailableControllers().then((json) => { + json.forEach(connector => { + var option = document.createElement('option'); + option.value = connector; + option.innerText = connector; + newMangaConnector.appendChild(option); + }); + }); + + UpdateJobs(); + setInterval(() => { + UpdateJobs(); + }, 1000); + }); } +Setup(); function ResetContent(){ //Delete everything @@ -207,45 +91,120 @@ function ResetContent(){ var plus = document.createElement("p"); plus.innerText = "+"; add.appendChild(plus); - add.addEventListener("click", () => ShowNewTaskWindow()); + add.addEventListener("click", () => ShowNewMangaSearch()); tasksContent.appendChild(add); } -function ShowPublicationViewerWindow(publicationId, event, add){ + +function ShowNewMangaSearch(){ + newMangaTitle.value = ""; + newMangaPopup.style.display = "block"; + newMangaResult.replaceChildren(); +} + +newMangaTitle.addEventListener("keypress", (event) => { if(event.key === "Enter") GetNewMangaItems();}) +function GetNewMangaItems(){ + if(newMangaTitle.value.length < 4) + return; + + newMangaResult.replaceChildren(); + newMangaConnector.disabled = true; + newMangaTitle.disabled = true; + newMangaTranslatedLanguage.disabled = true; + GetPublicationFromConnector(newMangaConnector.value, newMangaTitle.value).then((json) => { + //console.log(json); + if(json.length > 0) + newMangaResult.style.display = "flex"; + json.forEach(result => { + var mangaElement = CreateManga(result, newMangaConnector.value) + newMangaResult.appendChild(mangaElement); + mangaElement.addEventListener("click", (event) => { + ShowMangaWindow(null, result, event, true); + }); + }); + + newMangaConnector.disabled = false; + newMangaTitle.disabled = false; + newMangaTranslatedLanguage.disabled = false; + }); +} + +//Returns a new "Publication" Item to display in the jobs section +function CreateManga(manga, connector){ + var mangaElement = document.createElement('publication'); + mangaElement.id = GetValidSelector(manga.internalId); + var mangaImage = document.createElement('img'); + mangaImage.src = GetCoverUrl(manga.internalId); + mangaElement.appendChild(mangaImage); + var info = document.createElement('publication-information'); + var connectorName = document.createElement('connector-name'); + connectorName.innerText = connector; + connectorName.className = "pill"; + info.appendChild(connectorName); + var mangaName = document.createElement('publication-name'); + mangaName.innerText = manga.sortName; + info.appendChild(mangaName); + mangaElement.appendChild(info); + return mangaElement; +} + +createMonitorJobButton.addEventListener("click", () => { + CreateMonitorJob(newMangaConnector.value, selectedManga.internalId, newMangaTranslatedLanguage.value); + UpdateJobs(); + mangaViewerPopup.style.display = "none"; +}); +startJobButton.addEventListener("click", () => { + StartJob(selectedJob.id); + mangaViewerPopup.style.display = "none"; +}); +cancelJobButton.addEventListener("click", () => { + CancelJob(selectedJob.id); + mangaViewerPopup.style.display = "none"; +}); +deleteJobButton.addEventListener("click", () => { + RemoveJob(selectedJob.id); + UpdateJobs(); + mangaViewerPopup.style.display = "none"; +}); + +function ShowMangaWindow(job, manga, event, add){ + selectedManga = manga; + selectedJob = job; //Show popup - publicationViewerPopup.style.display = "block"; + mangaViewerPopup.style.display = "block"; //Set position to mouse-position - if(event.clientY < window.innerHeight - publicationViewerWindow.offsetHeight) - publicationViewerWindow.style.top = `${event.clientY}px`; + if(event.clientY < window.innerHeight - mangaViewerWindow.offsetHeight) + mangaViewerWindow.style.top = `${event.clientY}px`; else - publicationViewerWindow.style.top = `${event.clientY - publicationViewerWindow.offsetHeight}px`; + mangaViewerWindow.style.top = `${event.clientY - mangaViewerWindow.offsetHeight}px`; - if(event.clientX < window.innerWidth - publicationViewerWindow.offsetWidth) - publicationViewerWindow.style.left = `${event.clientX}px`; + if(event.clientX < window.innerWidth - mangaViewerWindow.offsetWidth) + mangaViewerWindow.style.left = `${event.clientX}px`; else - publicationViewerWindow.style.left = `${event.clientX - publicationViewerWindow.offsetWidth}px`; + mangaViewerWindow.style.left = `${event.clientX - mangaViewerWindow.offsetWidth}px`; //Edit information inside the window - var publication = publications.filter(pub => pub.internalId === publicationId)[0]; - publicationViewerName.innerText = publication.sortName; - publicationViewerTags.innerText = publication.tags.join(", "); - publicationViewerDescription.innerText = publication.description; - publicationViewerAuthor.innerText = publication.authors.join(','); - pubviewcover.src = `imageCache/${publication.coverFileNameInCache}`; - toEditId = publicationId; + mangaViewerName.innerText = manga.sortName; + mangaViewerTags.innerText = manga.tags.join(", "); + mangaViewerDescription.innerText = manga.description; + mangaViewerAuthor.innerText = manga.authors.join(','); + mangaViewCover.src = GetCoverUrl(manga.internalId); + toEditId = manga.internalId; //Check what action should be listed if(add){ - createMonitorTaskButton.style.display = "initial"; - createDownloadChapterTaskButton.style.display = "initial"; - publicationDelete.style.display = "none"; - publicationTaskStart.style.display = "none"; + createMonitorJobButton.style.display = "initial"; + createDownloadChapterJobButton.style.display = "none"; + cancelJobButton.style.display = "none"; + startJobButton.style.display = "none"; + deleteJobButton.style.display = "none"; } else{ - createMonitorTaskButton.style.display = "none"; - createDownloadChapterTaskButton.style.display = "none"; - publicationDelete.style.display = "initial"; - publicationTaskStart.style.display = "initial"; + createMonitorJobButton.style.display = "none"; + createDownloadChapterJobButton.style.display = "none"; + cancelJobButton.style.display = "initial"; + startJobButton.style.display = "initial"; + deleteJobButton.style.display = "initial"; } } @@ -253,108 +212,8 @@ function HidePublicationPopup(){ publicationViewerPopup.style.display = "none"; } -function ShowNewTaskWindow(){ - selectPublication.replaceChildren(); - searchPublicationQuery.value = ""; - selectPublicationPopup.style.display = "flex"; -} - - -const fadeIn = [ - { opacity: "0" }, - { opacity: "1" } -]; - -const fadeInTiming = { - duration: 50, - iterations: 1, - fill: "forwards" -} - -function OpenSettings(){ - GetSettingsClick(); - settingsPopup.style.display = "flex"; -} - -function GetSettingsClick(){ - settingApiUri.value = ""; - settingKomgaUrl.value = ""; - settingKomgaUser.value = ""; - settingKomgaPass.value = ""; - settingKomgaConfigured.innerText = "❌"; - settingKavitaUrl.value = ""; - settingKavitaUser.value = ""; - settingKavitaPass.value = ""; - settingKavitaConfigured.innerText = "❌"; - settingGotifyUrl.value = ""; - settingGotifyAppToken.value = ""; - settingGotifyConfigured.innerText = "❌"; - settingLunaseaWebhook.value = ""; - settingLunaseaConfigured.innerText = "❌"; - - settingApiUri.placeholder = apiUri; - - GetSettings().then(json => { - settingDownloadLocation.innerText = json.downloadLocation; - json.libraryManagers.forEach(lm => { - if(lm.libraryType == 0){ - settingKomgaUrl.placeholder = lm.baseUrl; - settingKomgaUser.placeholder = "User"; - settingKomgaPass.placeholder = "***"; - settingKomgaConfigured.innerText = "✅"; - } else if(lm.libraryType == 1){ - settingKavitaUrl.placeholder = lm.baseUrl; - settingKavitaUser.placeholder = "User"; - settingKavitaPass.placeholder = "***"; - settingKavitaConfigured.innerText = "✅"; - } - }); - json.notificationManagers.forEach(nm => { - if(nm.notificationManagerType == 0){ - settingGotifyConfigured.innerText = "✅"; - } else if(nm.notificationManagerType == 1){ - settingLunaseaConfigured.innerText = "✅"; - } - }); - }); - - GetKomgaTask().then(json => { - if(json.length > 0) - libraryUpdateTime.value = json[0].reoccurrence; - }); -} - -function UpdateLibrarySettings(){ - if(settingKomgaUrl.value != "" && settingKomgaUser.value != "" && settingKomgaPass.value != ""){ - var auth = utf8_to_b64(`${settingKomgaUser.value}:${settingKomgaPass.value}`); - console.log(auth); - UpdateKomga(settingKomgaUrl.value, auth); - } - - if(settingKavitaUrl.value != "" && settingKavitaUser.value != "" && settingKavitaPass.value != ""){ - UpdateKavita(settingKavitaUrl.value, settingKavitaUser.value, settingKavitaPass.value); - } - - if(settingGotifyUrl.value != "" && settingGotifyAppToken.value != ""){ - UpdateGotify(settingGotifyUrl.value, settingGotifyAppToken.value); - } - - if(settingLunaseaWebhook.value != ""){ - UpdateLunaSea(settingLunaseaWebhook.value); - } - - if(settingApiUri.value != ""){ - apiUri = settingApiUri.value; - document.cookie = `apiUri=${apiUri};`; - } - - setTimeout(() => GetSettingsClick(), 200); -} - -function utf8_to_b64( str ) { - return window.btoa(unescape(encodeURIComponent( str ))); -} - +searchBox.addEventListener("keyup", () => FilterResults()); +//Filter shown jobs function FilterResults(){ if(searchBox.value.length > 0){ tasksContent.childNodes.forEach(publication => { @@ -377,147 +236,249 @@ function FilterResults(){ } } -function ShowTasksQueue(){ +settingsCog.addEventListener("click", () => { + OpenSettings(); + settingsPopup.style.display = "flex"; +}); - downloadTasksOutput.replaceChildren(); - GetRunningTasks() - .then(json => { - tagTasksRunning.innerText = json.length; - json.forEach(task => { - if(task.task == 2 || task.task == 4) { - downloadTasksOutput.appendChild(CreateProgressChild(task)); - document.querySelector(`#progress${GetValidSelector(task.taskId)}`).value = task.progress; - var finishedHours = task.executionApproximatelyRemaining.split(':')[0]; - var finishedMinutes = task.executionApproximatelyRemaining.split(':')[1]; - var finishedSeconds = task.executionApproximatelyRemaining.split(':')[2].split('.')[0]; - document.querySelector(`#progressStr${GetValidSelector(task.taskId)}`).innerText = `${finishedHours}:${finishedMinutes}:${finishedSeconds}`; - } - }); - }); +settingKomgaUrl.addEventListener("keypress", (event) => { { if(event.key === "Enter") UpdateSettings(); } }); +settingKomgaUser.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings(); }); +settingKomgaPass.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings(); }); +settingKavitaUrl.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings(); }); +settingKavitaUser.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings(); }); +settingKavitaPass.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings(); }); +settingGotifyUrl.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings(); }); +settingGotifyAppToken.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings(); }); +settingLunaseaWebhook.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings(); }); +settingApiUri.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings(); }); - GetQueue() - .then(json => { - tagTasksQueued.innerText = json.length; - json.forEach(task => { - downloadTasksOutput.appendChild(CreateProgressChild(task)); - }); - }); - downloadTasksPopup.style.display = "flex"; +function OpenSettings(){ + settingGotifyConfigured.innerText = "❌"; + settingLunaseaConfigured.innerText = "❌"; + settingKavitaConfigured.innerText = "❌"; + settingKomgaConfigured.innerText = "❌"; + settingKomgaUrl.value = ""; + settingKomgaUser.value = ""; + settingKomgaPass.value = ""; + settingKavitaUrl.value = ""; + settingKavitaUser.value = ""; + settingKavitaPass.value = ""; + settingGotifyUrl.value = ""; + settingGotifyAppToken.value = ""; + settingLunaseaWebhook.value = ""; + settingApiUri.value = ""; + + GetSettings().then((json) => { + //console.log(json); + settingDownloadLocation.innerText = json.downloadLocation; + settingApiUri.placeholder = apiUri; + }); + GetLibraryConnectors().then((json) => { + //console.log(json); + json.forEach(connector => { + switch(libraryConnectorTypes[connector.libraryType]){ + case "Kavita": + settingKavitaConfigured.innerText = "✅"; + settingKavitaUrl.placeholder = connector.baseUrl; + settingKavitaUser.placeholder = "***"; + settingKavitaPass.placeholder = "***"; + break; + case "Komga": + settingKomgaConfigured.innerText = "✅"; + settingKomgaUrl.placeholder = connector.baseUrl; + settingKomgaUser.placeholder = "***"; + settingKomgaPass.placeholder = "***"; + break; + default: + console.log("Unknown type"); + console.log(connector); + break; + } + }); + }); + GetNotificationConnectors().then((json) => { + //console.log(json); + json.forEach(connector => { + switch(notificationConnectorTypes[connector.notificationConnectorType]){ + case "Gotify": + settingGotifyUrl.placeholder = connector.endpoint; + settingGotifyAppToken.placeholder = "***"; + settingGotifyConfigured.innerText = "✅"; + break; + case "LunaSea": + settingLunaseaConfigured.innerText = "✅"; + settingLunaseaWebhook.placeholder = connector.id; + break; + default: + console.log("Unknown type"); + console.log(connector); + break; + } + }); + }); } -function CreateProgressChild(task){ - var child = document.createElement("div"); - var img = document.createElement('img'); - img.src = `imageCache/${task.publication.coverFileNameInCache}`; - child.appendChild(img); - - var name = document.createElement("span"); - name.innerText = task.publication.sortName; - name.className = "pubTitle"; - child.appendChild(name); +function UpdateSettings(){ + if(settingApiUri.value != ""){ + apiUri = settingApiUri.value; + setCookie("apiUri", apiUri); + Setup(); + } + + if(settingKomgaUrl.value != "" && + settingKomgaUser.value != "" && + settingKomgaPass.value != ""){ + UpdateKomga(settingKomgaUrl.value, utf8_to_b64(`${settingKomgaUser.value}:${settingKomgaPass.value}`)); + } + + if(settingKavitaUrl.value != "" && + settingKavitaUser.value != "" && + settingKavitaPass.value != ""){ + UpdateKavita(settingKavitaUrl.value, settingKavitaUser.value, settingKavitaPass.value); + } + + if(settingGotifyUrl.value != "" && + settingGotifyAppToken.value != ""){ + UpdateGotify(settingGotifyUrl.value, settingGotifyAppToken.value); + } + + if(settingLunaseaWebhook.value != ""){ + UpdateLunaSea(settingLunaseaWebhook.value); + } + + OpenSettings(); + Setup(); +} +function utf8_to_b64(str) { + return window.btoa(unescape(encodeURIComponent( str ))); +} - var progress = document.createElement("progress"); - progress.id = `progress${GetValidSelector(task.taskId)}`; - child.appendChild(progress); +function UpdateJobs(){ + GetMonitorJobs().then((json) => { + ResetContent(); + //console.log(json); + json.forEach(job => { + var mangaView = CreateManga(job.manga, job.mangaConnector.name); + mangaView.addEventListener("click", (event) => { + ShowMangaWindow(job, job.manga, event, false); + }); + tasksContent.appendChild(mangaView); + }); + }); - var progressStr = document.createElement("span"); - progressStr.innerText = " \t∞"; - progressStr.className = "progressStr"; - progressStr.id = `progressStr${GetValidSelector(task.taskId)}`; - child.appendChild(progressStr); + GetWaitingJobs().then((json) => { + jobsQueuedTag.innerText = json.length; - if(task.chapter != undefined){ - var chapterNumber = document.createElement("span"); - chapterNumber.className = "chapterNumber"; - chapterNumber.innerText = `Vol.${task.chapter.volumeNumber} Ch.${task.chapter.chapterNumber}`; - child.appendChild(chapterNumber); - - var chapterName = document.createElement("span"); - chapterName.className = "chapterName"; - chapterName.innerText = task.chapter.name; - child.appendChild(chapterName); + var nowWaitingJobs = []; + + json.forEach(job => { + if(!waitingJobs.includes(GetValidSelector(job.id))){ + var jobDom = createJob(job); + jobStatusWaiting.appendChild(jobDom); + } + nowWaitingJobs.push(GetValidSelector(job.id)); + }); + waitingJobs = nowWaitingJobs; + }); + + jobStatusWaiting.childNodes.forEach(child => { + if(!waitingJobs.includes(child.id)) + jobStatusWaiting.removeChild(child); + }); + + GetRunningJobs().then((json) => { + jobsRunningTag.innerText = json.length; + + var nowRunningJobs = []; + + json.forEach(job => { + if(!runningJobs.includes(GetValidSelector(job.id))){ + var jobDom = createJob(job); + jobStatusRunning.appendChild(jobDom); + } + nowRunningJobs.push(GetValidSelector(job.id)); + UpdateJobProgress(job.id); + }); + + runningJobs = nowRunningJobs; + }); + + jobStatusRunning.childNodes.forEach(child => { + if(!runningJobs.includes(child.id)) + jobStatusRunning.removeChild(child); + }); +} + +function createJob(jobjson){ + var manga; + if(jobjson.chapter != null) + manga = jobjson.chapter.parentManga; + else if(jobjson.manga != null) + manga = jobjson.manga; + else return null; + + + var wrapper = document.createElement("div"); + wrapper.className = "jobWrapper"; + wrapper.id = GetValidSelector(jobjson.id); + + var image = document.createElement("img"); + image.className = "jobImage"; + image.src = GetCoverUrl(manga.internalId); + wrapper.appendChild(image); + + var title = document.createElement("span"); + title.className = "jobTitle"; + if(jobjson.chapter != null) + title.innerText = `${manga.sortName} - ${jobjson.chapter.fileName}`; + else if(jobjson.manga != null) + title.innerText = manga.sortName; + wrapper.appendChild(title); + + var progressBar = document.createElement("progress"); + progressBar.className = "jobProgressBar"; + progressBar.id = `jobProgressBar${GetValidSelector(jobjson.id)}`; + wrapper.appendChild(progressBar); + + var progressSpan = document.createElement("span"); + progressSpan.className = "jobProgressSpan"; + progressSpan.id = `jobProgressSpan${GetValidSelector(jobjson.id)}`; + progressSpan.innerText = "0% 00:00:00"; + wrapper.appendChild(progressSpan); + + var cancelSpan = document.createElement("span"); + cancelSpan.className = "jobCancel"; + cancelSpan.innerText = "Cancel"; + cancelSpan.addEventListener("click", () => CancelJob(jobjson.id)); + wrapper.appendChild(cancelSpan); + + return wrapper; +} + +function ShowJobQueue(){ + jobStatusView.style.display = "initial"; +} + +function UpdateJobProgress(jobId){ + GetProgress(jobId).then((json) => { + var progressBar = document.querySelector(`#jobProgressBar${GetValidSelector(jobId)}`); + var progressSpan = document.querySelector(`#jobProgressSpan${GetValidSelector(jobId)}`); + if(progressBar != null && json.progress != 0){ + progressBar.value = json.progress; } - - - return child; + if(progressSpan != null){ + var percentageStr = "0%"; + var timeleftStr = "00:00:00"; + if(json.progress != 0){ + percentageStr = Intl.NumberFormat("en-US", { style: "percent"}).format(json.progress); + timeleftStr = json.timeRemaining.split('.')[0]; + } + progressSpan.innerText = `${percentageStr} ${timeleftStr}`; + } + }); } -//Resets the tasks shown -ResetContent(); -downloadTasksOutput.replaceChildren(); -//Get Tasks and show them -GetDownloadTasks() - .then(json => json.forEach(task => { - var publication = CreatePublication(task.publication, task.connectorName); - publication.addEventListener("click", (event) => ShowPublicationViewerWindow(task.publication.internalId, event, false)); - tasksContent.appendChild(publication); - tasks.push(task); - })); - -GetRunningTasks() - .then(json => { - tagTasksRunning.innerText = json.length; - json.forEach(task => { - downloadTasksOutput.appendChild(CreateProgressChild(task)); - }); - }); - -GetQueue() - .then(json => { - tagTasksQueued.innerText = json.length; - json.forEach(task => { - downloadTasksOutput.appendChild(CreateProgressChild(task)); - }); - }) - -setInterval(() => { - //Tasks from API - var cTasks = []; - GetDownloadTasks() - .then(json => json.forEach(task => cTasks.push(task))) - .then(() => { - //Only update view if tasks-amount has changed - if(tasks.length != cTasks.length) { - //Resets the tasks shown - ResetContent(); - //Add all currenttasks to view - cTasks.forEach(task => { - var publication = CreatePublication(task.publication, task.connectorName); - publication.addEventListener("click", (event) => ShowPublicationViewerWindow(task.publication.internalId, event, false)); - tasksContent.appendChild(publication); - }) - - tasks = cTasks; - } - } - ); - - GetRunningTasks() - .then(json => { - tagTasksRunning.innerText = json.length; - }); - - GetQueue() - .then(json => { - tagTasksQueued.innerText = json.length; - }); -}, 1000); - -setInterval(() => { - GetRunningTasks().then((json) => { - json.forEach(task => { - if(task.task == 2 || task.task == 4){ - document.querySelector(`#progress${GetValidSelector(task.taskId)}`).value = task.progress; - var finishedHours = task.executionApproximatelyRemaining.split(':')[0]; - var finishedMinutes = task.executionApproximatelyRemaining.split(':')[1]; - var finishedSeconds = task.executionApproximatelyRemaining.split(':')[2].split('.')[0]; - document.querySelector(`#progressStr${GetValidSelector(task.taskId)}`).innerText = `${finishedHours}:${finishedMinutes}:${finishedSeconds}`; - } - }); - }); -},500); - function GetValidSelector(str){ var clean = [...str.matchAll(/[a-zA-Z0-9]*-*_*/g)]; return clean.join(''); diff --git a/Website/style.css b/Website/style.css index f7114ed..486a5dd 100644 --- a/Website/style.css +++ b/Website/style.css @@ -148,7 +148,7 @@ content { } #settingsPopup{ - z-index: 10; + z-index: 300; } #settingsPopup popup-content{ @@ -205,6 +205,7 @@ publication{ margin: 10px 10px; padding: 15px 20px; position: relative; + flex-shrink: 0; } publication::after{ @@ -305,138 +306,57 @@ popup popup-window popup-content input, select { border-radius: 3px; } -#selectPublicationPopup publication { - width: 150px; - height: 250px; +#newMangaPopup > div { + z-index: 3; + position: relative; } -#createTaskPopup { - z-index: 7; +#newMangaPopup > #newMangaPopupSelector { + width: 600px; + height: 40px; + margin: 80px auto 0; } -#createTaskPopup input { - height: 30px; - width: 200px; +#newMangaPopup > div > #newMangaConnector, #newMangaTitle, #newMangaTranslatedLanguage { + margin: 0; + display: inline-block; + height: 40px; } -#createMonitorTaskPopup, #createDownloadChaptersTask { - z-index: 9; +#newMangaPopup #newMangaConnector { + width: 100px; + padding: 0 0 0 5px; + border-radius: 5px 0 0 5px; + border: 0; + border-right: 1px solid darkgray; } -#createMonitorTaskPopup input[type="number"] { - width: 40px; +#newMangaPopup #newMangaTitle{ + width: 445px; + padding: 0 5px 0 5px; + border: 0; } -#createDownloadChaptersTask popup-content { - flex-direction: column; - align-items: start; +#newMangaPopup #newMangaTranslatedLanguage { + width: 45px; + border-radius: 0 5px 5px 0; + border: 0; + border-left: 1px solid darkgray; + margin-left: -5px; } -#createDownloadChaptersTask popup-content > * { - margin: 3px 0; -} - -#createDownloadChaptersTask #chapterOutput { - max-height: 50vh; - overflow-y: scroll; -} - -#createDownloadChaptersTask #chapterOutput .index{ - display: inline-block; - width: 25px; -} - -#createDownloadChaptersTask #chapterOutput .index::after{ - content: ':'; -} - -#createDownloadChaptersTask #chapterOutput .vol::before{ - content: 'Vol.'; -} - -#createDownloadChaptersTask #chapterOutput .vol{ - display: inline-block; - width: 45px; -} - -#createDownloadChaptersTask #chapterOutput .ch::before{ - content: 'Ch.'; -} - -#createDownloadChaptersTask #chapterOutput .ch { - display: inline-block; - width: 60px; -} - -#downloadTasksPopup popup-window { - left: 0; - top: 80px; - margin: 0 0 0 10px; - height: calc(100vh - 140px); - width: 400px; - max-width: 95vw; - overflow-y: scroll; -} - -#downloadTasksPopup popup-content { - flex-direction: column; - align-items: start; - margin: 5px; -} - -#downloadTasksPopup popup-content > div { - display: block; - height: 80px; - position: relative; - margin: 5px 0; -} - -#downloadTasksPopup popup-content > div > img { - display: block; - position: absolute; - height: 100%; - width: 60px; - left: 0; - top: 0; - object-fit: cover; - border-radius: 4px; -} - -#downloadTasksPopup popup-content > div > span { - display: block; - position: absolute; - width: max-content; -} - -#downloadTasksPopup popup-content > div > .pubTitle { - left: 70px; - top: 0; -} - -#downloadTasksPopup popup-content > div > .chapterName { - left: 70px; - top: 28pt; -} - -#downloadTasksPopup popup-content > div > .chapterNumber { - left: 70px; - top: 14pt; -} - -#downloadTasksPopup popup-content > div > progress { - display: block; - position: absolute; - left: 150px; - bottom: 0; - width: 200px; -} - -#downloadTasksPopup popup-content > div > .progressStr { - display: block; - position: absolute; - left: 70px; - bottom: 0; - width: 70px; +#newMangaResult { + display: none; + flex-direction: row; + justify-content: flex-start; + margin: 5px auto 0; + border-radius: 5px; + padding: 5px; + width: min-content; + max-width: 98%; + max-height: 400px; + overflow-x: scroll; + overflow-y: hidden; } blur-background { @@ -444,18 +364,10 @@ blur-background { height: 100%; position: absolute; left: 0; - background-color: black; - opacity: 0.5; -} - -#taskSelectOutput{ - display: flex; - flex-direction: row; - flex-wrap: wrap; - justify-content: start; - align-content: start; - max-height: 70vh; - overflow-y: scroll; + background: rgba(245, 169, 184, 0.58); + box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1); + backdrop-filter: blur(4.5px); + -webkit-backdrop-filter: blur(4.5px); } #publicationViewerPopup{ @@ -464,7 +376,7 @@ blur-background { publication-viewer{ display: block; - width: 450px; + width: 460px; position: absolute; top: 200px; left: 400px; @@ -559,6 +471,10 @@ publication-viewer publication-information publication-interactions publication- color: red; } +publication-view publication-information publication-interactions publication-canceltask { + color: yellow; +} + publication-viewer publication-information publication-interactions publication-add { color: limegreen; } @@ -600,4 +516,123 @@ footer-tag-popup::before{ left: 0; bottom: -17px; border-radius: 0 0 0 5px; +} + +#loaderdiv { + position: absolute; + top: 0px; + left: 0px; + width: 100%; + height: 100%; + z-index: 200; +} + +#loader { + border: 16px solid transparent; + border-top: 16px solid var(--secondary-color); + border-bottom: 16px solid var(--primary-color); + border-radius: 50%; + width: 120px; + height: 120px; + animation: spin 2s linear infinite; + position: absolute; + left: calc(50% - 60px); + top: calc(50% - 120px); + z-index: 201; +} + +#loaderText { + position: relative; + margin: 0 auto; + top: calc(50% + 80px); + z-index: 201; + text-align: center; + color: var(--second-background-color); + font-size: 20pt; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +#jobStatusView { + z-index: 50; +} + +#jobStatusView > popup-window { + top: 80px; + width: 50%; + max-height: calc(100% - 140px); + display: flex; + flex-direction: row; + flex-wrap: nowrap; + background-color: transparent; +} + +#jobStatusView > popup-window > div { + overflow-y: scroll; + overflow-x: hidden; + width: 50%; + margin: 0; + max-height: 100%; +} + +#jobStatusView > popup-window > div > div { + overflow-x: hidden; + display: flex; + flex-direction: column; + flex-wrap: nowrap; + width: 100%; + margin: 0; +} + +.jobWrapper { + width: 90%; + margin: 2px 5%; + height: 100px; + position: relative; + flex-shrink: 0; + background-color: rgba(187,187,187,0.4); + border-radius: 3px; +} + +.jobWrapper > .jobImage { + height: 90%; + width: 20%; + left: 5px; + object-fit: contain; + position: absolute; + top: 5%; +} + +.jobWrapper > .jobTitle { + position: absolute; + left: calc(20% + 10px); + top: 5px; +} + +.jobWrapper > .jobProgressBar { + position: absolute; + left: calc(20% + 10px); + bottom: calc(12pt + 10px); + width: calc(80% - 20px); + height: 10px; +} + +.jobWrapper > .jobProgressSpan { + position: absolute; + right: 10px; + bottom: calc(12pt + 20px); + width: 60%; + text-align: right; +} + +.jobWrapper > .jobCancel { + position: absolute; + right: 10px; + bottom: 5px; + font-size: 12pt; + color: var(--secondary-color); + cursor: pointer; } \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml index 9dcf31f..6a06903 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,7 +1,7 @@ version: '3' services: tranga-api: - image: glax/tranga-api:latest + image: glax/tranga-api:cuttingedge container_name: tranga-api volumes: - ./tranga:/usr/share/Tranga-API #1 when replacing ./tranga replace #2 with same value @@ -10,7 +10,7 @@ services: - "6531:6531" restart: unless-stopped tranga-website: - image: glax/tranga-website:latest + image: glax/tranga-website:cuttingedge container_name: tranga-website volumes: - ./tranga/imageCache:/usr/share/nginx/html/imageCache:ro #2 when replacing Point to same value as #1/imageCache