Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
Chytanka supports opening episodes from the following platforms:

- [x] ~~[Blankary](https://blankary.com)~~ (image support has been discontinued)
- [x] [Comick](https://comick.io)
- [x] ~~[Comick](https://comick.io)~~ (baned)
- [x] [Imgur](https://imgur.com)
- [x] [Mangadex](https://mangadex.org)
- [x] [Nhentai](https://nhentai.net)
Expand Down
23 changes: 20 additions & 3 deletions src/app/app.component.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { Component, HostListener, PLATFORM_ID, WritableSignal, inject, signal } from '@angular/core';
import { Component, HostListener, PLATFORM_ID, WritableSignal, effect, inject, signal } from '@angular/core';
import { LangService } from './shared/data-access/lang.service';
import { ActivatedRoute } from '@angular/router';
import { DOCUMENT, isPlatformBrowser } from '@angular/common';
import { environment } from '../environments/environment';
import { DISPLAY_MODES, LinkParserSettingsService } from './link-parser/data-access/link-parser-settings.service';

const SCALE_GAP = 128;

Expand All @@ -15,14 +16,16 @@ const SCALE_GAP = 128;
export class AppComponent {
private readonly document = inject(DOCUMENT);
platformId = inject(PLATFORM_ID)
setts = inject(LinkParserSettingsService)


constructor(public lang: LangService, private route: ActivatedRoute) {
this.lang.updateManifest()
this.lang.updateTranslate()

this.route.pathFromRoot[0].queryParams.subscribe(async q => {
const l = q['lang']

if (l) {
this.lang.setLang(l)
}
Expand All @@ -37,11 +40,25 @@ export class AppComponent {
console.log(`%c${msg}`, "background-color: #166496; color: #ffd60a; font-size: 4rem; font-family: monospace; padding: 8px 16px");
}


effect(() => {
this.updateDisplayMode();
});
}

ngOnInit() {
this.initScaleDifference();
this.updateDisplayMode();
}

updateDisplayMode() {
if (this.setts.displayMode != undefined) {
for (const mode of DISPLAY_MODES) {
this.document.documentElement.classList.remove(mode);
}
this.document.documentElement.classList.add(this.setts.displayMode() + 'mode');

}

}

@HostListener('window:resize')
Expand Down
68 changes: 19 additions & 49 deletions src/app/file/data-access/zip.worker.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
/// <reference lib="webworker" />

// TODO: change to https://github.com/101arrowz/fflate
// because jszip is toooo slow
// https://chatgpt.com/c/68cadcf9-6c28-8329-add8-cb20abaa2f85

import JSZip from 'jszip';
import { filterImages, getAcbfFile, getComicInfoFile, processFile, processImagesInBatches } from '../utils';

const metadataFiles = [
{ getter: getComicInfoFile, type: 'comicinfo' },
{ getter: getAcbfFile, type: 'acbf' },
];

addEventListener('message', ({ data }) => {
const arrayBuffer = data.arrayBuffer;
Expand All @@ -11,59 +21,19 @@ addEventListener('message', ({ data }) => {
.then(async zip => {
const filesName: string[] = Object.keys(zip.files);

// console.dir(zip.files)

const comicInfoFile = getComicInfoFile(filesName)

if (comicInfoFile) {
const comicinfo = zip.files[comicInfoFile]
await comicinfo.async('text').then(text => { postMessage({ type: 'comicinfo', data: text }); })
}
console.log(filesName);


const acbf = getAcbfFile(filesName)
if (acbf) {
const acbfF = zip.files[acbf]
await acbfF.async('text').then(text => { postMessage({ type: 'acbf', data: text }); })
// metadata
for (const { getter, type } of metadataFiles) {
await processFile(getter(filesName), zip, type);
}

const images = filterImages(filesName).sort()
// images
const images = filterImages(filesName).sort();
postMessage({ type: 'zipopen', data: { count: images.length } });

for (let i = 0; i < images.length; i++) {
const filename = images[i];

await zip.files[filename].async('blob').then(blob => {
const url = URL.createObjectURL(blob);
postMessage({ type: 'file', url: url, index: i });
});
}

await processImagesInBatches(zip, images, 30);

});
});

function filterImages(fileList: Array<string>) {
const imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp'];

return fileList.filter(file => {
const extension = file.substring(file.lastIndexOf('.')).toLowerCase();
return imageExtensions.includes(extension);
});
}

function getComicInfoFile(fileList: Array<string>) {
const resultArray = fileList.filter(f => f.toLowerCase() == 'comicinfo.xml')

return resultArray.length > 0 ? resultArray[0] : false
}

function getAcbfFile(fileList: Array<string>) {
const imageExtensions = ['.acbf'];

const result = fileList.filter(file => {
const extension = file.substring(file.lastIndexOf('.')).toLowerCase();
return imageExtensions.includes(extension);
})

return result.length > 0 ? result[0] : null;
}
});
16 changes: 16 additions & 0 deletions src/app/file/utils/comic-metadata-files.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export function getComicInfoFile(fileList: Array<string>) {
const resultArray = fileList.filter(f => f.toLowerCase() == 'comicinfo.xml')

return resultArray.length > 0 ? resultArray[0] : false
}

export function getAcbfFile(fileList: Array<string>) {
const imageExtensions = ['.acbf'];

const result = fileList.filter(file => {
const extension = file.substring(file.lastIndexOf('.')).toLowerCase();
return imageExtensions.includes(extension);
})

return result.length > 0 ? result[0] : null;
}
8 changes: 8 additions & 0 deletions src/app/file/utils/filter-images.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export function filterImages(fileList: Array<string>) {
const imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp'];

return fileList.filter(file => {
const extension = file.substring(file.lastIndexOf('.')).toLowerCase();
return imageExtensions.includes(extension);
});
}
4 changes: 4 additions & 0 deletions src/app/file/utils/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from './filter-images';
export * from './comic-metadata-files';
export * from './process-file';
export * from './process-images-in-batches';
13 changes: 13 additions & 0 deletions src/app/file/utils/process-file.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export async function processFile(
fileName: string | false | null,
zip: any,
type: string
) {
if (!fileName) return;

const file = zip.files[fileName];
if (!file) return;

const text = await file.async('text');
postMessage({ type, data: text });
}
23 changes: 23 additions & 0 deletions src/app/file/utils/process-images-in-batches.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import JSZip from "jszip";

export async function processImagesInBatches(
zip: JSZip,
images: string[],
batchSize = 20
) {
for (let i = 0; i < images.length; i += batchSize) {
const batch = images.slice(i, i + batchSize);

await Promise.all(
batch.map(async (filename, index) => {
const blob = await zip.files[filename].async('blob');
const url = URL.createObjectURL(blob);
postMessage({
type: 'file',
url,
index: i + index,
});
})
);
}
}
11 changes: 6 additions & 5 deletions src/app/history/ui/history-list/history-list.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
@let sites = historyItems() | async;

@if (sites && sites.length > 0) {
<details>
<details name="history">
<summary>
<span>SITES HISTORY</span>
@if ( sites.length > 0) {
Expand All @@ -21,8 +21,8 @@
}

@if (files && files.length > 0) {
<details open>
<summary>
<details open name="history">
<summary [vibrateHaptic]="10">
<span>FILES HISTORY</span> |
<code>{{fileSize() | filesize}}</code> |
<code>{{fileCount()}}</code>
Expand All @@ -35,7 +35,7 @@

<div class="wrap">
@for (item of files; track item.sha256;) {
@let ab = item.arrayBuffer;
@let ab = item.arrayBuffer;
<div>
<app-title-card [value]="{
site: (ab) ? ['file', item.format] : '',
Expand All @@ -55,5 +55,6 @@
}

@if (files?.length ==0 && sites?.length ==0) {
none
<h2>{{lang.ph().historyEmpty}}</h2>
<p>{{lang.ph().histyryEmptyDesc}}</p>
}
2 changes: 1 addition & 1 deletion src/app/history/ui/history-list/history-list.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { FileHistoryService } from '../../../file/data-access/file-history.servi
@Component({
selector: 'app-history-list',
templateUrl: './history-list.component.html',
styleUrl: './history-list.component.scss',
styleUrls: ['./history-list.component.scss', '../../../shared/ui/@styles/details.scss'],
standalone: false
})
export class HistoryListComponent {
Expand Down
23 changes: 23 additions & 0 deletions src/app/link-parser/data-access/link-parser-settings.service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { isPlatformBrowser } from '@angular/common';
import { Injectable, PLATFORM_ID, WritableSignal, computed, inject, signal } from '@angular/core';

export const DISPLAY_MODES = ['softmode', 'truemode'];
const NAME_DISPLAY_MODE = 'displayMode';
const NAME_AUTO_PASTE_LINK = 'autoPasteLink';
const NAME_SEASONAL_THEME = 'seasonalTheme';

@Injectable({
providedIn: 'root'
})
Expand All @@ -12,6 +17,7 @@ export class LinkParserSettingsService {
constructor() {
this.initAutoPasteLink()
this.initSeasonalTheme()
this.initDisplayMode()
}

initAutoPasteLink() {
Expand All @@ -28,6 +34,23 @@ export class LinkParserSettingsService {
localStorage.setItem('autoPasteLink', n.toString())
}

displayMode!: WritableSignal<string>;

initDisplayMode() {
if (!isPlatformBrowser(this.platformId)) return;

const n = localStorage.getItem(NAME_DISPLAY_MODE) === null ? 'soft' : localStorage.getItem(NAME_DISPLAY_MODE) as string;
this.displayMode = signal(n);
this.setDisplayMode(n);
}

setDisplayMode(n: string) {
if (!isPlatformBrowser(this.platformId)) return;

this.displayMode.update(v => n);
localStorage.setItem(NAME_DISPLAY_MODE, n)
}

/**
*
*/
Expand Down
2 changes: 1 addition & 1 deletion src/app/link-parser/data-access/link-parser.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export class LinkParserService {
"Reddit",
"MangaDex",
"Zenko",
"Comick",
// "Comick",
"NHentai",
"Yandere Pools",
"Imgchest",
Expand Down
4 changes: 2 additions & 2 deletions src/app/link-parser/link-parser/link-parser.component.html
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<main>
<main >
<app-parser-form />
</main>

<aside>
<app-chytanka-logo-with-tags [fileTags]="parser.supportFiles()" [siteTags]="parser.supportSites()" />
<app-chytanka-logo-with-tags [ngClass]="setts.theme()" [fileTags]="parser.supportFiles()" [siteTags]="parser.supportSites()" />
</aside>

@defer (on immediate) {
Expand Down
Loading
Loading