Handle database

This commit is contained in:
ZXY101
2023-08-09 10:27:58 +02:00
parent 4e645467bb
commit 416b832f63
17 changed files with 14576 additions and 258 deletions

14458
Bocchi-the-Rock!-02.json Normal file

File diff suppressed because it is too large Load Diff

11
package-lock.json generated
View File

@@ -9,6 +9,7 @@
"version": "0.0.1", "version": "0.0.1",
"dependencies": { "dependencies": {
"@zip.js/zip.js": "^2.7.20", "@zip.js/zip.js": "^2.7.20",
"dexie": "^4.0.1-alpha.25",
"panzoom": "^9.4.3" "panzoom": "^9.4.3"
}, },
"devDependencies": { "devDependencies": {
@@ -1279,6 +1280,11 @@
"integrity": "sha512-KqFl6pOgOW+Y6wJgu80rHpo2/3H07vr8ntR9rkkFIRETewbf5GaYYcakYfiKz89K+sLsuPkQIZaXDMjUObZwWg==", "integrity": "sha512-KqFl6pOgOW+Y6wJgu80rHpo2/3H07vr8ntR9rkkFIRETewbf5GaYYcakYfiKz89K+sLsuPkQIZaXDMjUObZwWg==",
"dev": true "dev": true
}, },
"node_modules/dexie": {
"version": "4.0.1-alpha.25",
"resolved": "https://registry.npmjs.org/dexie/-/dexie-4.0.1-alpha.25.tgz",
"integrity": "sha512-udAExYYfJaC6IzVC+ygHdlWYoYxhwYHtiITkthGgSH7dh2FBk3qqDGkJ/Mn2lfxsmATcq9qJDUjyO7yWOo/ZPA=="
},
"node_modules/dir-glob": { "node_modules/dir-glob": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
@@ -4025,6 +4031,11 @@
"integrity": "sha512-KqFl6pOgOW+Y6wJgu80rHpo2/3H07vr8ntR9rkkFIRETewbf5GaYYcakYfiKz89K+sLsuPkQIZaXDMjUObZwWg==", "integrity": "sha512-KqFl6pOgOW+Y6wJgu80rHpo2/3H07vr8ntR9rkkFIRETewbf5GaYYcakYfiKz89K+sLsuPkQIZaXDMjUObZwWg==",
"dev": true "dev": true
}, },
"dexie": {
"version": "4.0.1-alpha.25",
"resolved": "https://registry.npmjs.org/dexie/-/dexie-4.0.1-alpha.25.tgz",
"integrity": "sha512-udAExYYfJaC6IzVC+ygHdlWYoYxhwYHtiITkthGgSH7dh2FBk3qqDGkJ/Mn2lfxsmATcq9qJDUjyO7yWOo/ZPA=="
},
"dir-glob": { "dir-glob": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",

View File

@@ -31,6 +31,7 @@
"type": "module", "type": "module",
"dependencies": { "dependencies": {
"@zip.js/zip.js": "^2.7.20", "@zip.js/zip.js": "^2.7.20",
"dexie": "^4.0.1-alpha.25",
"panzoom": "^9.4.3" "panzoom": "^9.4.3"
} }
} }

20
src/lib/catalog/db.ts Normal file
View File

@@ -0,0 +1,20 @@
import type { Volume } from '$lib/upload';
import Dexie, { type Table } from 'dexie';
export interface Catalog {
id?: number;
manga: Volume[];
}
export class CatalogDexie extends Dexie {
catalog!: Table<Catalog>;
constructor() {
super('mokuro');
this.version(1).stores({
catalog: '++id, manga'
});
}
}
export const db = new CatalogDexie();

View File

@@ -1,5 +1,5 @@
import type { Manga, Volume } from "$lib/types/catalog"; import type { Volume } from "$lib/upload";
import { writable } from "svelte/store"; import { writable } from "svelte/store";
export const currentManga = writable<Manga | undefined>(undefined); export const currentManga = writable<Volume[] | undefined>(undefined);
export const currentVolume = writable<Volume | undefined>(undefined); export const currentVolume = writable<Volume | undefined>(undefined);

9
src/lib/catalog/test.ts Normal file
View File

@@ -0,0 +1,9 @@
import { writable } from "svelte/store";
import { db } from "$lib/catalog/db";
import type { Volume } from "$lib/upload";
import { liveQuery } from "dexie";
export const catalog = liveQuery(() => db.catalog.toArray());

View File

@@ -1,110 +1,14 @@
<script lang="ts"> <script lang="ts">
import image1 from '$lib/assets/1.jpg'; import { catalog } from '$lib/catalog/test';
import image2 from '$lib/assets/2.jpg';
import image3 from '$lib/assets/3.jpg';
import image4 from '$lib/assets/4.jpg';
import image5 from '$lib/assets/5.jpg';
import type { Manga } from '$lib/types/catalog';
import CatalogItem from './CatalogItem.svelte'; import CatalogItem from './CatalogItem.svelte';
const manga: Manga[] = [
{
title: 'Manga name',
cover: image1,
volumes: [
{
cover: image1,
title: 'Volume 1',
currentPage: 0,
totalPages: 100
},
{
cover: image1,
title: 'Volume 2',
currentPage: 0,
totalPages: 100
}
]
},
{
title: 'Another',
cover: image2,
volumes: [
{
cover: image2,
title: 'Volume 1',
currentPage: 0,
totalPages: 100
},
{
cover: image2,
title: 'Volume 2',
currentPage: 0,
totalPages: 100
}
]
},
{
title: 'Awooo',
cover: image3,
volumes: [
{
cover: image3,
title: 'Volume 1',
currentPage: 0,
totalPages: 100
},
{
cover: image3,
title: 'Volume 2',
currentPage: 0,
totalPages: 100
}
]
},
{
title: 'Title',
cover: image4,
volumes: [
{
cover: image4,
title: 'Volume 1',
currentPage: 0,
totalPages: 100
},
{
cover: image4,
title: 'Volume 2',
currentPage: 0,
totalPages: 100
}
]
},
{
title: 'sdhfjksdh',
cover: image5,
volumes: [
{
cover: image5,
title: 'Volume 1',
currentPage: 0,
totalPages: 100
},
{
cover: image5,
title: 'Volume 2',
currentPage: 0,
totalPages: 100
}
]
}
];
</script> </script>
<div> <div>
{#each manga as item} {#if $catalog}
<CatalogItem manga={item} /> {#each $catalog as { manga }}
{/each} <CatalogItem {manga} />
{/each}
{/if}
</div> </div>
<style> <style>

View File

@@ -1,22 +1,20 @@
<script lang="ts"> <script lang="ts">
import { currentManga } from '$lib/catalog'; import { currentManga } from '$lib/catalog';
import type { Manga } from '$lib/types/catalog'; import type { Volume } from '$lib/upload';
import { navbarTitle } from './NavBar.svelte'; export let manga: Volume[];
export let manga: Manga; const { volumeName, files, mokuroData } = manga[0];
const { cover, title } = manga;
function onClick() { function onClick() {
console.log('bruh');
navbarTitle.set(title);
currentManga.set(manga); currentManga.set(manga);
} }
</script> </script>
<a href={title} on:click={onClick}> <a href={volumeName} on:click={onClick}>
<div class="content"> <div class="content">
{title} {mokuroData.title}
<img src={cover} alt={title} /> {#if files}
<img src={URL.createObjectURL(Object.values(files)[0])} alt="img" />
{/if}
</div> </div>
</a> </a>

View File

@@ -18,12 +18,12 @@
switch ($page?.route.id) { switch ($page?.route.id) {
case '/[manga]': case '/[manga]':
title = $currentManga?.title; title = $currentManga?.[0].mokuroData.title;
back = '/'; back = '/';
break; break;
case '/[manga]/[volume]': case '/[manga]/[volume]':
window.document.body.classList.add('reader'); window.document.body.classList.add('reader');
title = $currentVolume?.title; title = $currentVolume?.volumeName;
back = '/manga'; back = '/manga';
break; break;
case '/upload': case '/upload':

View File

@@ -1,6 +1,4 @@
<script lang="ts"> <script lang="ts">
import img from '$lib/assets/001.jpg';
type Block = { type Block = {
box: number[]; box: number[];
vertical: boolean; vertical: boolean;
@@ -10,102 +8,35 @@
type Page = { type Page = {
version?: string; version?: string;
imgWidth: number; img_width: number;
imgHeight: number; img_height: number;
blocks: Block[]; blocks: Block[];
imgPath: string; imgPath: string;
}; };
let page: Page = { export let page: Page;
version: '0.1.6',
imgWidth: 1078,
imgHeight: 1530,
blocks: [
{
box: [924, 167, 948, 380],
vertical: true,
fontSize: 21.0,
lines: ['私も頑張らなくちゃ!']
},
{
box: [584, 242, 610, 397],
vertical: true,
fontSize: 26.0,
lines: ['リョウ先輩!']
},
{
box: [895, 474, 943, 687],
vertical: true,
fontSize: 20.0,
lines: ['私のギターの練習', 'みてもらえませんか!']
},
{
box: [898, 787, 947, 979],
vertical: true,
fontSize: 21.0,
lines: ['ぼっちに教えて', 'もらってるじゃん?']
},
{
box: [708, 796, 783, 936],
vertical: true,
fontSize: 20.0,
lines: ['〜〜っ', 'もっと練習', 'したいんです!!']
},
{
box: [663, 832, 701, 971],
vertical: true,
fontSize: 20.0,
lines: ['後藤さんだって', '?の練習あるし...']
},
{
box: [568, 800, 616, 1011],
vertical: true,
fontSize: 20.0,
lines: ['ちょっと喜多ちゃん', '急にどうしちゃったの']
},
{
box: [874, 1122, 951, 1380],
vertical: true,
fontSize: 22.0,
lines: ['まさか結束パンドの', 'ギター同士で血で血を洗う', 'パート争いを...']
},
{
box: [636, 1135, 682, 1256],
vertical: true,
fontSize: 20.0,
lines: ['ちょっと', '結束してよ〜']
},
{
box: [560, 1240, 590, 1381],
vertical: true,
fontSize: 27.0,
lines: ['違います!!']
}
],
imgPath: '049.jpg'
};
let bold = false; let bold = false;
$: fontWeight = bold ? 'bold' : '400'; $: fontWeight = bold ? 'bold' : '400';
console.log(bold);
function clamp(x: number, min: number, max: number) { function clamp(x: number, min: number, max: number) {
return Math.min(Math.max(x, min), max); return Math.min(Math.max(x, min), max);
} }
const { blocks, imgHeight, imgPath, imgWidth } = page; console.log(page);
const area = imgWidth * imgHeight;
const textBoxes = blocks.map((block) => { $: textBoxes = page.blocks.map((block) => {
const { img_height, img_width } = page;
const { box, fontSize, lines, vertical } = block; const { box, fontSize, lines, vertical } = block;
let [_xmin, _ymin, _xmax, _ymax] = box; let [_xmin, _ymin, _xmax, _ymax] = box;
const xmin = clamp(_xmin, 0, imgWidth); const xmin = clamp(_xmin, 0, img_width);
const ymin = clamp(_ymin, 0, imgHeight); const ymin = clamp(_ymin, 0, img_height);
const xmax = clamp(_xmax, 0, imgWidth); const xmax = clamp(_xmax, 0, img_width);
const ymax = clamp(_ymax, 0, imgHeight); const ymax = clamp(_ymax, 0, img_height);
console.log('ymax', img_width);
const width = xmax - xmin; const width = xmax - xmin;
const height = ymax - ymin; const height = ymax - ymin;
@@ -120,11 +51,11 @@
lines lines
}; };
console.log(textBox);
return textBox; return textBox;
}); });
let src = `/src/lib/assets/${imgPath}`; console.log(textBoxes);
export let src;
</script> </script>
<div style:--bold={fontWeight}> <div style:--bold={fontWeight}>
@@ -132,7 +63,7 @@
<label>Bold</label> <label>Bold</label>
<input bind:checked={bold} type="checkbox" placeholder="????" /> <input bind:checked={bold} type="checkbox" placeholder="????" />
</div> </div>
<img draggable="false" {src} alt="Page 1" /> <img draggable="false" src={URL.createObjectURL(src)} alt="img" />
{#each textBoxes as { left, top, width, height, lines, fontSize, writingMode }} {#each textBoxes as { left, top, width, height, lines, fontSize, writingMode }}
<div <div
class="text-box" class="text-box"

View File

@@ -5,14 +5,28 @@
import MangaPage from './MangaPage.svelte'; import MangaPage from './MangaPage.svelte';
const volume = $currentVolume; const volume = $currentVolume;
let page = 1;
let pages = volume?.mokuroData.pages;
function right() {
page++;
}
function left() {
if (page > 1) {
page--;
}
}
</script> </script>
{#if volume} {#if volume}
<div> <div>
<Button on:click={zoomOriginal}>Reset Zoom</Button> <Button on:click={zoomOriginal}>Reset Zoom</Button>
<Button on:click={left}>{'<'}</Button>
<Button on:click={right}>{'>'}</Button>
</div> </div>
<Panzoom> <Panzoom>
<MangaPage /> <MangaPage page={pages[page - 1]} src={Object.values(volume?.files)[page - 1]} />
</Panzoom> </Panzoom>
{/if} {/if}

View File

@@ -1,22 +1,22 @@
<script lang="ts"> <script lang="ts">
import { goto } from '$app/navigation';
import { page } from '$app/stores'; import { page } from '$app/stores';
import { currentManga, currentVolume } from '$lib/catalog'; import { currentVolume } from '$lib/catalog';
import type { Volume } from '$lib/types/catalog'; import type { Volume } from '$lib/upload';
export let volume: Volume; export let volume: Volume;
const { cover, title, currentPage, totalPages } = volume; const { volumeName, files } = volume;
function onClick() { function onClick() {
currentVolume.set(volume); currentVolume.set(volume);
} }
</script> </script>
<a href={`${$page.params.manga}/${title}`} on:click={onClick}> <a href={`${$page.params.manga}/${volumeName}`} on:click={onClick}>
<div class="content"> <div class="content">
{title} {volumeName}
<img src={cover} alt={title} /> {#if files}
{currentPage} / {totalPages} <img src={URL.createObjectURL(Object.values(files)[0])} alt="img" />
{/if}
</div> </div>
</a> </a>

View File

@@ -1,12 +0,0 @@
export type Volume = {
title: string;
cover: string;
currentPage: number;
totalPages: number;
}
export type Manga = {
title: string;
cover: string;
volumes: Volume[];
};

View File

@@ -1,3 +1,4 @@
import { db } from "$lib/catalog/db";
import { requestPersistentStorage } from "$lib/util/upload"; import { requestPersistentStorage } from "$lib/util/upload";
import { BlobReader, ZipReader } from "@zip.js/zip.js"; import { BlobReader, ZipReader } from "@zip.js/zip.js";
@@ -83,6 +84,6 @@ export async function processFiles(fileList: FileList) {
if (vols.length > 0) { if (vols.length > 0) {
await requestPersistentStorage(); await requestPersistentStorage();
await processVolumes(vols) await processVolumes(vols)
return vols; await db.catalog.put({ manga: vols })
} }
} }

View File

@@ -1,18 +1,25 @@
<script lang="ts"> <script lang="ts">
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import { db } from '$lib/catalog/db';
import { catalog } from '$lib/catalog/test';
import Button from '$lib/components/Button.svelte'; import Button from '$lib/components/Button.svelte';
import Catalog from '$lib/components/Catalog.svelte'; import Catalog from '$lib/components/Catalog.svelte';
import Modal from '$lib/components/Modal.svelte'; import Modal from '$lib/components/Modal.svelte';
import { showSnackbar } from '$lib/util/snackbar';
function onClick() { function onClick() {
goto('/upload'); goto('/upload');
} }
function clear() {
db.catalog.clear();
}
let modal: HTMLDialogElement; let modal: HTMLDialogElement;
</script> </script>
<Button variant="secondary" on:click={onClick}>Upload</Button> <Button variant="secondary" on:click={onClick}>Upload</Button>
<Button variant="danger" on:click={clear}>Clear</Button>
<Modal bind:modal> <Modal bind:modal>
<div slot="title">Title</div> <div slot="title">Title</div>
<div slot="content"> <div slot="content">

View File

@@ -2,8 +2,6 @@
import { currentManga } from '$lib/catalog'; import { currentManga } from '$lib/catalog';
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import Button from '$lib/components/Button.svelte';
import { panAlign, Panzoom, zoomOriginal } from '$lib/panzoom';
import VolumeItem from '$lib/components/VolumeItem.svelte'; import VolumeItem from '$lib/components/VolumeItem.svelte';
const manga = $currentManga; const manga = $currentManga;
@@ -17,7 +15,7 @@
<div> <div>
{#if manga} {#if manga}
{#each manga.volumes as volume} {#each manga as volume}
<VolumeItem {volume} /> <VolumeItem {volume} />
{/each} {/each}
{/if} {/if}

View File

@@ -1,44 +1,22 @@
<script lang="ts"> <script lang="ts">
import { db } from '$lib/catalog/db';
import Button from '$lib/components/Button.svelte'; import Button from '$lib/components/Button.svelte';
import Catalog from '$lib/components/Catalog.svelte';
import FileUpload from '$lib/components/FileUpload.svelte'; import FileUpload from '$lib/components/FileUpload.svelte';
import { processFiles, unzipManga, type Volume } from '$lib/upload'; import { processFiles } from '$lib/upload';
let promise: Promise<Volume[] | undefined> | undefined; let promise: Promise<void>;
async function onUpload(files: FileList) { async function onUpload(files: FileList) {
promise = processFiles(files); promise = processFiles(files);
} }
function up() {
page++;
}
function down() {
if (page > 0) {
page--;
}
}
let page = 0;
</script> </script>
<!-- Note: webkitdirectory is not fully supported and does not work on mobile --> <!-- Note: webkitdirectory is not fully supported and does not work on mobile -->
<Button on:click={down}>{'<'}</Button>
<Button on:click={up}>{'>'}</Button>
{page}
<FileUpload {onUpload} webkitdirectory>Upload directory</FileUpload> <FileUpload {onUpload} webkitdirectory>Upload directory</FileUpload>
<FileUpload {onUpload} accept=".mokuro,.zip,.cbz,.rar" multiple>Upload files</FileUpload> <FileUpload {onUpload} accept=".mokuro,.zip,.cbz,.rar" multiple>Upload files</FileUpload>
{#if promise}
{#await promise} {#await promise}
<p>Loading...</p> <h2>Loading...</h2>
{:then volumes} {/await}
{#if volumes} <Catalog />
{#each volumes as { mokuroData, volumeName, archiveFile, files }}
<p>{volumeName}</p>
{#if files}
<img src={URL.createObjectURL(Object.values(files)[page])} alt="img" />
{/if}
{/each}
{/if}
{/await}
{/if}