comic-book-dl
Version:
139 lines (128 loc) • 4.69 kB
JavaScript
import protobuf from 'protobufjs';
import NodeRSA from 'node-rsa';
const key = 'MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAK8nNR1lTnIfIes6oRWJNj3mB6OssDGx0uGMpgpbVCpf6+VwnuI2stmhZNoQcM417Iz7WqlPzbUmu9R4dEKmLGEEqOhOdVaeh9Xk2IPPjqIu5TbkLZRxkY3dJM1htbz57d/roesJLkZXqssfG5EJauNc+RcABTfLb4IiFjSMlTsnAgMBAAECgYEAiz/pi2hKOJKlvcTL4jpHJGjn8+lL3wZX+LeAHkXDoTjHa47g0knYYQteCbv+YwMeAGupBWiLy5RyyhXFoGNKbbnvftMYK56hH+iqxjtDLnjSDKWnhcB7089sNKaEM9Ilil6uxWMrMMBH9v2PLdYsqMBHqPutKu/SigeGPeiB7VECQQDizVlNv67go99QAIv2n/ga4e0wLizVuaNBXE88AdOnaZ0LOTeniVEqvPtgUk63zbjl0P/pzQzyjitwe6HoCAIpAkEAxbOtnCm1uKEp5HsNaXEJTwE7WQf7PrLD4+BpGtNKkgja6f6F4ld4QZ2TQ6qvsCizSGJrjOpNdjVGJ7bgYMcczwJBALvJWPLmDi7ToFfGTB0EsNHZVKE66kZ/8Stx+ezueke4S556XplqOflQBjbnj2PigwBN/0afT+QZUOBOjWzoDJkCQClzo+oDQMvGVs9GEajS/32mJ3hiWQZrWvEzgzYRqSf3XVcEe7PaXSd8z3y3lACeeACsShqQoc8wGlaHXIJOHTcCQQCZw5127ZGs8ZDTSrogrH73Kw/HvX55wGAeirKYcv28eauveCG7iyFR0PFB/P/EDZnyb+ifvyEFlucPUI0+Y87F';
const ChapterImageProtoDefinition = `
// https://github.com/tachiyomiorg/tachiyomi-extensions/blob/master/src/zh/dmzj/API.md
syntax = "proto3";
package dmzj.chapter_images;
message ResponseDto {
int32 Errno = 1;
string Errmsg = 2;
ChapterImagesDto Data= 3;
}
message ChapterImagesDto {
int32 Id = 1;
int32 MangaId = 2;
string Name= 3;
int32 Order= 4;
int32 Direction= 5;
repeated string LowResImages= 6;
int32 PageCount= 7;
repeated string Images= 8;
int32 CommentCount= 9;
}
`;
const ChapterListProtoDefinition = `
syntax = "proto3";
package dmzj.comic;
message ComicDetailResponse {
int32 Errno = 1;
string Errmsg = 2;
ComicDetailInfoResponse Data= 3;
}
message ComicDetailInfoResponse {
int32 Id = 1;
string Title = 2;
int32 Direction=3;
int32 Islong=4;
int32 IsDmzj=5;
string Cover=6;
string Description=7;
int64 LastUpdatetime=8;
string LastUpdateChapterName=9;
int32 Copyright=10;
string FirstLetter=11;
string ComicPy=12;
int32 Hidden=13;
int32 HotNum=14;
int32 HitNum=15;
int32 Uid=16;
int32 IsLock=17;
int32 LastUpdateChapterId=18;
repeated ComicDetailTypeItemResponse Types=19;
repeated ComicDetailTypeItemResponse Status=20;
repeated ComicDetailTypeItemResponse Authors=21;
int32 SubscribeNum=22;
repeated ComicDetailChapterResponse Chapters=23;
int32 IsNeedLogin=24;
//object UrlLinks=25;
int32 IsHideChapter=26;
//object DhUrlLinks=27;
}
message ComicDetailTypeItemResponse {
int32 TagId = 1;
string TagName = 2;
}
message ComicDetailChapterResponse {
string Title = 1;
repeated ComicDetailChapterInfoResponse Data=2;
}
message ComicDetailChapterInfoResponse {
int32 ChapterId = 1;
string ChapterTitle = 2;
int64 Updatetime=3;
int32 Filesize=4;
int32 ChapterOrder=5;
}
`;
export function ApiV4Decrypt(data) {
const keyByte = Buffer.from(key, 'base64');
const tempData = Buffer.from(data, 'base64');
const privateKey = new NodeRSA(keyByte, 'pkcs8-der', {});
privateKey.setOptions({ encryptionScheme: 'pkcs1', environment: 'browser' });
const MAX_DECRYPT_BLOCK = 128;
const inputLen = tempData.length;
let result = new Uint8Array(inputLen);
let chunk = 0;
for (let offset = 0; offset < inputLen; offset += MAX_DECRYPT_BLOCK) {
const blockLen = Math.min(MAX_DECRYPT_BLOCK, inputLen - offset);
const encryptedData = tempData.subarray(offset, offset + blockLen);
const decryptData = privateKey.decrypt(encryptedData);
result.set(decryptData, chunk);
chunk = chunk + decryptData.length;
}
result = result.subarray(0, chunk);
return Buffer.from(result);
}
export function ApiV4ChapterImageParse(data) {
const root = protobuf.parse(ChapterImageProtoDefinition, {
keepCase: true
}).root;
const ResponseDto = root.lookupType('ResponseDto');
const decodedRes = ResponseDto.decode(data);
const decodedObject = ResponseDto.toObject(decodedRes, {
longs: String,
enums: String,
bytes: String
});
if (!decodedObject) {
return null;
}
return decodedObject?.Data;
}
export function ApiV4ChapterListParse(data) {
const root = protobuf.parse(ChapterListProtoDefinition, {
keepCase: true
}).root;
const ResponseDto = root.lookupType('ComicDetailResponse');
const decodedRes = ResponseDto.decode(data);
const decodedObject = ResponseDto.toObject(decodedRes, {
longs: String,
enums: String,
bytes: String
});
if (!decodedObject) {
return null;
}
return decodedObject?.Data;
}