mirror of
https://github.com/danbulant/Mangades
synced 2026-07-08 04:20:52 +00:00
243 lines
No EOL
6.7 KiB
Svelte
243 lines
No EOL
6.7 KiB
Svelte
<script>
|
|
import { page, url } from "@roxi/routify/runtime/helpers";
|
|
import { Zip, ZipPassThrough } from "fflate";
|
|
import { prepareEpub } from "../../util/generateEpub";
|
|
// import * as streamSaver from "streamsaver";
|
|
|
|
import request from "../../util/request";
|
|
|
|
export var scoped;
|
|
|
|
var mangaId = scoped.id;
|
|
$: mangaId = scoped.id;
|
|
var manga = scoped.manga;
|
|
$: manga = scoped.manga;
|
|
var relationships = scoped.mangaRelationships;
|
|
$: relationships = scoped.mangaRelationships;
|
|
|
|
async function getMangaChapters(id) {
|
|
const data = await request("manga/" + id + "/feed?translatedLanguage[]=en");
|
|
data.results.sort((a, b) => a.data.attributes.chapter - b.data.attributes.chapter);
|
|
return data;
|
|
}
|
|
|
|
$: chapters = getMangaChapters(mangaId);
|
|
|
|
console.log(manga);
|
|
console.log(chapters);
|
|
console.log(relationships);
|
|
|
|
var progress = 0;
|
|
var state = "idle";
|
|
var text = "Choose a chapter to view online or download EPUB";
|
|
var pagesDone = 0;
|
|
var totalPages = 0;
|
|
|
|
$: progress = pagesDone / (totalPages || 1);
|
|
$: if(totalPages) text = `Saving page ${pagesDone + 1} of ${totalPages}`;
|
|
|
|
var enc = new TextEncoder();
|
|
async function prepare(chapter) {
|
|
state = "active";
|
|
text = "Starting download of chapter " + chapter.data.attributes.chapter;
|
|
|
|
const { baseUrl } = await request("at-home/server/" + chapter.data.id);
|
|
const quality = "data";
|
|
|
|
const URLs = [];
|
|
const hashes = [];
|
|
for(const hash of chapter.data.attributes[quality]) {
|
|
URLs.push(`${baseUrl}/${quality}/${chapter.data.attributes.hash}/${hash}`);
|
|
hashes.push(hash);
|
|
}
|
|
|
|
text = "Found " + URLs.length + " pages";
|
|
totalPages += URLs.length;
|
|
|
|
const file = streamSaver.createWriteStream(`${manga.title.en} ${chapter.data.attributes.chapter}.epub`, {
|
|
writableStrategy: undefined, // (optional)
|
|
readableStrategy: undefined, // (optional)
|
|
});
|
|
|
|
const zip = await prepareEpub({
|
|
title: `${manga.title.en} ${chapter.data.attributes.chapter}`,
|
|
id: `https://manga.danbulant.eu/${mangaId}/${chapter.data.id}`,
|
|
file,
|
|
chapter: chapter.data.attributes.chapter,
|
|
links: hashes,
|
|
updatedAt: chapter.data.attributes.updatedAt
|
|
});
|
|
|
|
for(var i = 0; i < URLs.length; i++) {
|
|
const url = URLs[i];
|
|
const hash = hashes[i];
|
|
const res = await fetch(url);
|
|
const image = new ZipPassThrough("OEBPS/" + hash);
|
|
zip.add(image);
|
|
image.push(new Uint8Array(await res.arrayBuffer()), true);
|
|
const textContent = new ZipPassThrough("OEBPS/" + i + ".xhtml");
|
|
zip.add(textContent);
|
|
textContent.push(enc.encode(`<?xml version="1.0" encoding="UTF-8"?>
|
|
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
|
|
<head>
|
|
<title>Page ${i + 1}</title>
|
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
|
<meta name="EPB-UUID" content=""/>
|
|
</head>
|
|
<body>
|
|
<img style="margin:auto;height:100%;" src="${hash}" />
|
|
</body>
|
|
</html>`), true);
|
|
pagesDone++;
|
|
}
|
|
|
|
zip.end();
|
|
if(pagesDone === totalPages) {
|
|
text = "Done!";
|
|
state = "idle";
|
|
pagesDone = 0;
|
|
totalPages = 0;
|
|
|
|
setTimeout(() => {
|
|
if(totalPages === 0) {
|
|
text = "Choose a chapter to view online or download EPUB";
|
|
}
|
|
}, 3000);
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<svelte:head>
|
|
<title>Chapters of {manga.title.en}</title>
|
|
</svelte:head>
|
|
|
|
<main>
|
|
<h1>{manga.title.en}</h1>
|
|
|
|
<div class="linklist">
|
|
<a href={$url("..")}>Go back to search page</a>
|
|
</div>
|
|
|
|
<br>
|
|
|
|
<div class="state {state}">
|
|
<div class="progress" style="width: {progress * 100}%;"></div>
|
|
|
|
<p>
|
|
{text}
|
|
</p>
|
|
</div>
|
|
|
|
<p>
|
|
<b>
|
|
Do not close the tab when a download is in progress.
|
|
</b>
|
|
</p>
|
|
|
|
{#await chapters}
|
|
Loading chapters...
|
|
{:then chapters}
|
|
{#if chapters.results.filter(c => c.data.attributes.translatedLanguage === "en").length === 0}
|
|
<p>No chapters found.</p>
|
|
{/if}
|
|
<table>
|
|
<tbody>
|
|
{#each chapters.results.filter(c => c.data.attributes.translatedLanguage === "en") as chapter}
|
|
<tr>
|
|
<td class="no-wrap">{chapter.data.attributes.volume ? "Vol " + chapter.data.attributes.volume : ""}</td>
|
|
<td class="no-wrap">Chapter {chapter.data.attributes.chapter}</td>
|
|
<td>{chapter.data.attributes.title}</td>
|
|
<td class="action no-wrap"><span on:click={() => prepare(chapter)}>Download</span></td>
|
|
<td class="action no-wrap"><a href={$url("./" + chapter.data.id)} on:click|stopPropagation>View</a></td>
|
|
</tr>
|
|
{/each}
|
|
</tbody>
|
|
</table>
|
|
{/await}
|
|
</main>
|
|
|
|
<style>
|
|
main {
|
|
font-size: 1.1rem;
|
|
}
|
|
.no-wrap {
|
|
white-space: nowrap;
|
|
}
|
|
tbody {
|
|
list-style-type: disc;
|
|
}
|
|
table {
|
|
border-collapse: collapse;
|
|
width: 100%;
|
|
}
|
|
|
|
tr {
|
|
border: 1px solid black;
|
|
}
|
|
|
|
tr:hover {
|
|
background: rgba(0,0,0,0.1);
|
|
}
|
|
|
|
td {
|
|
padding: 5px 5px;
|
|
}
|
|
|
|
td.action {
|
|
font-weight: bold;
|
|
color: black;
|
|
text-decoration: none;
|
|
cursor: pointer;
|
|
}
|
|
|
|
td.action:hover {
|
|
text-decoration: underline;
|
|
color: rgb(0,100,200);
|
|
}
|
|
|
|
td.action a {
|
|
color: inherit;
|
|
}
|
|
|
|
.state {
|
|
border-radius: 10px;
|
|
border-width: 4px;
|
|
border-style: solid;
|
|
padding: 10px;
|
|
position: relative;
|
|
transition: all .3s;
|
|
}
|
|
|
|
.state.idle {
|
|
background: rgb(140, 209, 255);
|
|
border-color: rgb(77, 184, 255);
|
|
}
|
|
|
|
.state.active {
|
|
background: rgb(255, 255, 81);
|
|
border-color: yellow;
|
|
}
|
|
.state.error {
|
|
background: rgb(255, 103, 103);
|
|
border-color: rgb(255, 59, 59);
|
|
}
|
|
|
|
.state p {
|
|
margin: 0;
|
|
z-index: 1;
|
|
position: relative;
|
|
}
|
|
|
|
.progress {
|
|
z-index: 0;
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
height: 100%;
|
|
border-top-left-radius: 10px;
|
|
border-bottom-left-radius: 10px;
|
|
}
|
|
.state.active .progress {
|
|
background: rgb(140, 209, 255);
|
|
}
|
|
</style> |