Skip to content

Commit 686cc6c

Browse files
committed
Add Take Comic connector
1 parent 46300e5 commit 686cc6c

File tree

3 files changed

+176
-106
lines changed

3 files changed

+176
-106
lines changed
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import Comici from './templates/Comici.mjs';
2+
import Manga from '../engine/Manga.mjs';
3+
4+
export default class YoungChampion extends Comici {
5+
constructor() {
6+
super();
7+
super.id = 'takecomic';
8+
super.label = 'Take Comic';
9+
this.tags = ['manga', 'japanese'];
10+
this.url = 'https://takecomic.jp';
11+
this.apiUrl = this.url;
12+
this.links = {
13+
login: 'https://takecomic.jp/auth/signin'
14+
};
15+
16+
this.queryMangaTitleURI = 'h1.series-h-title';
17+
this.apiPath = '/api/';
18+
}
19+
20+
async _getMangaFromURI(uri) {
21+
const request = new Request(uri);
22+
const [data] = await this.fetchDOM(request, this.queryMangaTitleURI);
23+
const id = uri.pathname.replace('/series/', '');
24+
const title = data.lastChild.textContent.trim();
25+
return new Manga(this, id, title);
26+
}
27+
28+
async _getChapters(manga) {
29+
const uri = new URL('/api/episodes', this.url);
30+
uri.searchParams.append('seriesHash', manga.id);
31+
uri.searchParams.append('episodeTo', 999);
32+
const request = new Request(uri);
33+
const data = await this.fetchJSON(request);
34+
return data.series.episodes;
35+
}
36+
37+
async _fetchCoordInfo(viewer) {
38+
//first request get page count
39+
let uri = new URL(this.apiPath+'book/contentsInfo', this.url);
40+
uri.searchParams.set('comici-viewer-id', viewer.dataset['comiciViewerId']);
41+
uri.searchParams.set('user-id', viewer.dataset['memberJwt']);
42+
uri.searchParams.set('page-from', '0');
43+
uri.searchParams.set('page-to', '1');
44+
let request = new Request(uri);
45+
request.headers.set('x-origin', this.url);
46+
request.headers.set('x-referer', this.url);
47+
const data = await this.fetchJSON(request);
48+
49+
//second request fetch actual pages data
50+
const numbers = data.totalPages;
51+
uri = new URL(this.apiPath+'book/contentsInfo', this.url);
52+
uri.searchParams.set('comici-viewer-id', viewer.dataset['comiciViewerId']);
53+
uri.searchParams.set('user-id', viewer.dataset['memberJwt']);
54+
uri.searchParams.set('page-from', '0');
55+
uri.searchParams.set('page-to', numbers);
56+
request = new Request(uri);
57+
request.headers.set('x-origin', this.url);
58+
request.headers.set('x-referer', this.url);
59+
return await this.fetchJSON(request);
60+
}
61+
}
Lines changed: 7 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -1,107 +1,47 @@
1-
import Connector from '../engine/Connector.mjs';
1+
import Comici from './templates/Comici.mjs';
22
import Manga from '../engine/Manga.mjs';
33

4-
//very Similar to Yanmaga - its called ComiciViewer
5-
export default class YoungChampion extends Connector {
4+
export default class YoungChampion extends Comici {
65
constructor() {
76
super();
87
super.id = 'youngchampion';
98
super.label = 'YoungChampion';
109
this.tags = ['manga', 'japanese'];
1110
this.url = 'https://youngchampion.jp';
1211
this.apiUrl = this.url;
13-
this.defaultOrder = [];
1412
this.links = {
1513
login: 'https://youngchampion.jp/signin'
1614
};
17-
for (let i = 0; i < 4; i++) {
18-
for (let j = 0; j < 4; j++) {
19-
this.defaultOrder.push([i, j]);
20-
}
21-
}
2215

23-
this.mangaListPath = '/series/list?page={page}';
2416
this.queryMangaTitleURI = 'h1.series-h-title span:not([class])';
25-
this.queryManga = 'div.series-box-vertical > a';
26-
this.queryMangaTitle = 'h2.title-text';
2717
this.queryChapter = 'div.series-ep-list a[data-href]';
2818
this.queryChapterTitle = 'span.series-ep-list-item-h-text';
2919
}
3020

3121
async _getMangaFromURI(uri) {
3222
const request = new Request(uri);
3323
const [data] = await this.fetchDOM(request, this.queryMangaTitleURI);
34-
const id = uri.pathname;
24+
const id = uri.pathname.replace('/series/', '');
3525
const title = (data.textContent || data.text).trim();
3626
return new Manga(this, id, title);
3727
}
3828

39-
async _getMangas() {
40-
let mangaList = [];
41-
for (let page = 1, run = true; run; page++) {
42-
const mangas = await this._getMangasFromPage(page);
43-
mangas.length > 0 ? mangaList.push(...mangas) : run = false;
44-
}
45-
return mangaList;
46-
}
47-
48-
async _getMangasFromPage(page) {
49-
const uri = new URL( this.mangaListPath.replace('{page}', page), this.url);
50-
const request = new Request(uri, this.requestOptions);
51-
const data = await this.fetchDOM(request, this.queryManga);
52-
return data.map(element => {
53-
const titleElement = element.querySelector(this.queryMangaTitle);
54-
return {
55-
id: this.getRootRelativeOrAbsoluteLink(element, this.url),
56-
title: titleElement ? (titleElement.textContent || titleElement.text).trim() : element.text.trim()
57-
};
58-
});
59-
}
60-
6129
async _getChapters(manga) {
62-
const uri = new URL(manga.id+'/list', this.url);
30+
const uri = new URL('/series/'+manga.id+'/list', this.url);
6331
const request = new Request(uri);
6432
const data = await this.fetchDOM(request, this.queryChapter );
6533
return data.map(element => {
6634
const titleElement = element.querySelector(this.queryChapterTitle);
6735
return {
68-
id : new URL(element.dataset['href']).pathname,
36+
id : new URL(element.dataset['href']).pathname.replace('/series/', ''),
6937
title: titleElement ? (titleElement.textContent ||titleElement.text).trim() : element.text.trim()
7038
};
7139
});
7240
}
7341

74-
async _getPages(chapter) {
75-
const uri = new URL(chapter.id, this.url);
76-
const request = new Request(uri);
77-
const [viewer] = await this.fetchDOM(request, '#comici-viewer');
78-
if (!viewer) {
79-
throw new Error(`The chapter '${chapter.title}' is neither public, nor purchased!`);
80-
}
81-
const coord = await this._fetchCoordInfo(viewer);
82-
return coord.result.map(image => {
83-
return this.createConnectorURI({
84-
url: image.imageUrl,
85-
scramble: image.scramble,
86-
});
87-
});
88-
}
89-
90-
async _handleConnectorURI(payload) {
91-
const request = new Request(new URL(payload.url));
92-
request.headers.set('x-origin', this.url);
93-
request.headers.set('x-referer', this.url);
94-
const res = await fetch(request);
95-
const blob = await res.blob();
96-
const image = await createImageBitmap(blob);
97-
const canvas = this._descramble(image, payload.scramble);
98-
const blobFinally = await this._canvasToBlob(canvas);
99-
return this._blobToBuffer(blobFinally);
100-
}
101-
10242
async _fetchCoordInfo(viewer) {
10343
//first request get page count
104-
let uri = new URL('/book/contentsInfo', this.url);
44+
let uri = new URL(this.apiPath+'book/contentsInfo', this.url);
10545
uri.searchParams.set('comici-viewer-id', viewer.getAttribute('comici-viewer-id'));
10646
uri.searchParams.set('user-id', viewer.dataset['memberJwt']);
10747
uri.searchParams.set('page-from', '0');
@@ -113,7 +53,7 @@ export default class YoungChampion extends Connector {
11353

11454
//second request fetch actual pages data
11555
const numbers = data.totalPages;
116-
uri = new URL('/book/contentsInfo', this.url);
56+
uri = new URL(this.apiPath+'book/contentsInfo', this.url);
11757
uri.searchParams.set('comici-viewer-id', viewer.getAttribute('comici-viewer-id'));
11858
uri.searchParams.set('user-id', viewer.dataset['memberJwt']);
11959
uri.searchParams.set('page-from', '0');
@@ -123,43 +63,4 @@ export default class YoungChampion extends Connector {
12363
request.headers.set('x-referer', this.url);
12464
return await this.fetchJSON(request);
12565
}
126-
127-
_decodeScrambleArray(scramble) {
128-
const decoded = [];
129-
const encoded = scramble.replace(/\s+/g, '').slice(1).slice(0, -1).split(',');
130-
for (let i = 0; i < this.defaultOrder.length; i++) {
131-
decoded.push(this.defaultOrder[encoded[i]]);
132-
}
133-
return decoded;
134-
}
135-
136-
_descramble(imageDom, scrambleString) {
137-
const width = imageDom.width;
138-
const height = imageDom.height;
139-
140-
const canvas = document.createElement('canvas');
141-
canvas.width = width;
142-
canvas.height = height;
143-
144-
const context = canvas.getContext('2d');
145-
const decodedArray = this._decodeScrambleArray(scrambleString);
146-
const tileWidth = Math.floor(width / 4);
147-
const tileHeight = Math.floor(height / 4);
148-
for (let k = 0, i = 0; i < 4; i++) {
149-
for (let j = 0; j < 4; j++) {
150-
const x = decodedArray[k][0], y = decodedArray[k][1];
151-
context.drawImage(imageDom, tileWidth * x, tileHeight * y, tileWidth, tileHeight, tileWidth * i, tileHeight * j, tileWidth, tileHeight);
152-
k++;
153-
}
154-
}
155-
return canvas;
156-
}
157-
158-
_canvasToBlob(canvas) {
159-
return new Promise(resolve => {
160-
canvas.toBlob(data => {
161-
resolve(data);
162-
}, Engine.Settings.recompressionFormat.value, parseFloat(Engine.Settings.recompressionQuality.value) / 100);
163-
});
164-
}
16566
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import Connector from '../../engine/Connector.mjs';
2+
3+
//very Similar to Yanmaga - its called ComiciViewer
4+
export default class Comici extends Connector {
5+
constructor() {
6+
super();
7+
this.defaultOrder = [];
8+
for (let i = 0; i < 4; i++) {
9+
for (let j = 0; j < 4; j++) {
10+
this.defaultOrder.push([i, j]);
11+
}
12+
}
13+
14+
this.mangaListPath = '/series/list?page={page}';
15+
this.queryManga = 'div.series-box-vertical > a';
16+
this.queryMangaTitle = 'h2.title-text';
17+
this.apiPath = '/';
18+
}
19+
20+
async _getMangas() {
21+
let mangaList = [];
22+
for (let page = 1, run = true; run; page++) {
23+
const mangas = await this._getMangasFromPage(page);
24+
mangas.length > 0 ? mangaList.push(...mangas) : run = false;
25+
}
26+
return mangaList;
27+
}
28+
29+
async _getMangasFromPage(page) {
30+
const uri = new URL( this.mangaListPath.replace('{page}', page), this.url);
31+
const request = new Request(uri, this.requestOptions);
32+
const data = await this.fetchDOM(request, this.queryManga);
33+
return data.map(element => {
34+
const titleElement = element.querySelector(this.queryMangaTitle);
35+
return {
36+
id: this.getRootRelativeOrAbsoluteLink(element, this.url),
37+
title: titleElement ? (titleElement.textContent || titleElement.text).trim() : element.text.trim()
38+
};
39+
});
40+
}
41+
42+
async _getPages(chapter) {
43+
const uri = new URL('/episodes/' + chapter.id, this.url);
44+
const request = new Request(uri);
45+
const [viewer] = await this.fetchDOM(request, '#comici-viewer');
46+
if (!viewer) {
47+
throw new Error(`The chapter '${chapter.title}' is neither public, nor purchased!`);
48+
}
49+
const coord = await this._fetchCoordInfo(viewer);
50+
return coord.result.map(image => {
51+
return this.createConnectorURI({
52+
url: image.imageUrl,
53+
scramble: image.scramble,
54+
});
55+
});
56+
}
57+
58+
async _handleConnectorURI(payload) {
59+
const request = new Request(new URL(payload.url));
60+
request.headers.set('x-origin', this.url);
61+
request.headers.set('x-referer', this.url);
62+
const res = await fetch(request);
63+
const blob = await res.blob();
64+
const image = await createImageBitmap(blob);
65+
const canvas = this._descramble(image, payload.scramble);
66+
const blobFinally = await this._canvasToBlob(canvas);
67+
return this._blobToBuffer(blobFinally);
68+
}
69+
70+
_decodeScrambleArray(scramble) {
71+
const decoded = [];
72+
const encoded = scramble.replace(/\s+/g, '').slice(1).slice(0, -1).split(',');
73+
for (let i = 0; i < this.defaultOrder.length; i++) {
74+
decoded.push(this.defaultOrder[encoded[i]]);
75+
}
76+
return decoded;
77+
}
78+
79+
_descramble(imageDom, scrambleString) {
80+
const width = imageDom.width;
81+
const height = imageDom.height;
82+
83+
const canvas = document.createElement('canvas');
84+
canvas.width = width;
85+
canvas.height = height;
86+
87+
const context = canvas.getContext('2d');
88+
const decodedArray = this._decodeScrambleArray(scrambleString);
89+
const tileWidth = Math.floor(width / 4);
90+
const tileHeight = Math.floor(height / 4);
91+
for (let k = 0, i = 0; i < 4; i++) {
92+
for (let j = 0; j < 4; j++) {
93+
const x = decodedArray[k][0], y = decodedArray[k][1];
94+
context.drawImage(imageDom, tileWidth * x, tileHeight * y, tileWidth, tileHeight, tileWidth * i, tileHeight * j, tileWidth, tileHeight);
95+
k++;
96+
}
97+
}
98+
return canvas;
99+
}
100+
101+
_canvasToBlob(canvas) {
102+
return new Promise(resolve => {
103+
canvas.toBlob(data => {
104+
resolve(data);
105+
}, Engine.Settings.recompressionFormat.value, parseFloat(Engine.Settings.recompressionQuality.value) / 100);
106+
});
107+
}
108+
}

0 commit comments

Comments
 (0)