Skip to content
This repository was archived by the owner on Mar 23, 2025. It is now read-only.

Commit 6b39c96

Browse files
authored
Merge pull request #1 from hkalexling/master
Update
2 parents cd892a5 + ca36b40 commit 6b39c96

File tree

6 files changed

+203
-72
lines changed

6 files changed

+203
-72
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ To install a plugin, simply download the folder containing the desired plugin (e
88

99
## Contributing
1010

11-
Pull requests are always welcome! Please check out the [development guidelne](https://github.com/hkalexling/mango-plugins/wiki/Development-Guideline).
11+
Pull requests are always welcome! Please check out the [development guidelne](https://github.com/hkalexling/mango-plugins/wiki/Development-Guideline-v2).

plugins/mangadex/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
storage.json
2+
subscriptions.json

plugins/mangadex/README.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,19 @@
11
# MangaDex Plugin
22

3-
This is the Mango plugin for [MangaDex](https://mangadex.org/). It's developed to demonstrate the usage of the plugin system, and you should use the built-in MangaDex downloader in Mango instead.
3+
This is the official Mango plugin for [MangaDex](https://mangadex.org/).
4+
5+
If you are looking for the v1 version of this plugin (deprecated), check out commit f045ecd5752823f100baf4f298d89f2f99237cd4.
6+
7+
### Configuration
8+
9+
Under the `settings` field in `info.json`, you can customize the following values:
10+
11+
#### `language`
12+
13+
You can list one or more language codes in this field (separated by commas) for the plugin to search MangaDex in. Only chapters in one of the specified languages will be shown and used. Check [here](https://api.mangadex.org/docs.html#section/Language-Codes-and-Localization) for the list of available language codes. When it's not set, the plugin will show you chapters in all languages, and you probably don't want that.
14+
15+
#### `listChapterLimit`
16+
17+
This field controls the maximum number of chapters the plugin lists when browsing a manga or when checking for updates. It can be anything between `1` and `500`. Unset it to use the default value `100` from MangaDex.
418

519
Maintained by [@hkalexling](https://github.com/hkalexling).

plugins/mangadex/index.js

Lines changed: 165 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,81 +1,185 @@
1-
var chapter;
2-
var currentPage;
3-
4-
function listChapters(query) {
5-
var json = {};
6-
var URL = 'https://mangadex.org/api/manga/' + query;
7-
try {
8-
json = JSON.parse(mango.get(URL).body);
9-
} catch (e) {
10-
mango.raise('Failed to get JSON from ' + URL);
1+
function getCoverURL(mangaId, coverId) {
2+
const res = mango.get('https://api.mangadex.org/cover/' + coverId);
3+
if (res.status_code !== 200)
4+
mango.raise('Failed to cover ID. Status ' + res.status_code);
5+
const filename = JSON.parse(res.body).data.attributes.fileName;
6+
return 'https://uploads.mangadex.org/covers/' + mangaId + '/' + filename;
7+
}
8+
9+
function getManga(mangaId) {
10+
const res = mango.get('https://api.mangadex.org/manga/' + mangaId);
11+
if (res.status_code !== 200)
12+
mango.raise('Failed to get manga. Status ' + res.status_code);
13+
return JSON.parse(res.body).data;
14+
}
15+
16+
function formatChapter(json, mangaTitle) {
17+
var title = json.attributes.title || "";
18+
if (json.attributes.volume)
19+
title += ' Vol. ' + json.attributes.volume;
20+
if (json.attributes.chapter)
21+
title += ' Ch. ' + json.attributes.chapter;
22+
var groupId;
23+
for (i = 0; i < json.relationships.length; i++) {
24+
const obj = json.relationships[i];
25+
if (obj.type === 'scanlation_group') {
26+
groupId = obj.id;
27+
break;
28+
}
1129
}
30+
const obj = {
31+
id: json.id,
32+
title: title,
33+
manga_title: mangaTitle,
34+
pages: json.attributes.pages,
35+
volume: json.attributes.volume,
36+
chapter: json.attributes.chapter,
37+
language: json.attributes.translatedLanguage,
38+
group_id: groupId || null,
39+
published_at: Date.parse(json.attributes.publishAt),
40+
};
41+
return obj;
42+
}
1243

13-
if (json.status !== 'OK')
14-
mango.raise('JSON status: ' + json.status);
44+
function formatTimestamp(timestamp) {
45+
return new Date(timestamp).toISOString().replace('.000Z', '');
46+
}
1547

16-
var chapters = [];
17-
Object.keys(json.chapter).forEach(function(id) {
18-
var obj = json.chapter[id];
48+
function searchManga(query) {
49+
const res = mango.get('https://api.mangadex.org/manga?title=' + encodeURIComponent(query));
50+
if (res.status_code !== 200)
51+
mango.raise('Failed to search for manga. Status ' + res.status_code);
52+
const manga = JSON.parse(res.body).data;
53+
if (!manga)
54+
mango.raise('Failed to search for manga.');
1955

20-
var groups = [];
21-
['group_name', 'group_name_2', 'group_name_3'].forEach(function(key) {
22-
if (obj[key]) {
23-
groups.push(obj[key]);
56+
return JSON.stringify(manga.map(function(m) {
57+
const titleAttr = m.attributes.title;
58+
const ch = {
59+
id: m.id,
60+
title: titleAttr.en || titleAttr['ja-ro'] || titleAttr[Object.keys(titleAttr)[0]]
61+
};
62+
for (i = 0; i < m.relationships.length; i++) {
63+
const obj = m.relationships[i];
64+
if (obj.type === 'cover_art') {
65+
ch.cover_url = getCoverURL(m.id, obj.id);
66+
break;
2467
}
68+
}
69+
return ch;
70+
}));
71+
}
72+
73+
function listChapters(id) {
74+
const manga = getManga(id);
75+
const titleAttr = manga.attributes.title;
76+
const title = titleAttr.en || titleAttr['ja-ro'] || titleAttr[Object.keys(titleAttr)[0]];
77+
78+
var url = 'https://api.mangadex.org/manga/' + id + '/feed?';
79+
80+
const langStr = mango.settings('language');
81+
if (langStr) {
82+
const langAry = langStr.split(',').forEach(function(lang) {
83+
url += 'translatedLanguage[]=' + lang.trim() + '&';
2584
});
26-
groups = groups.join(', ');
27-
var time = new Date(obj.timestamp * 1000);
28-
29-
var slimObj = {};
30-
slimObj['id'] = id;
31-
slimObj['volume'] = obj['volume'];
32-
slimObj['chapter'] = obj['chapter'];
33-
slimObj['title'] = obj['title'];
34-
slimObj['lang'] = obj['lang_code'];
35-
slimObj['groups'] = groups;
36-
slimObj['time'] = time;
37-
38-
chapters.push(slimObj);
39-
});
85+
}
4086

41-
return JSON.stringify({
42-
title: json.manga.title,
43-
chapters: chapters
44-
});
87+
const limit = mango.settings('listChapterLimit');
88+
if (limit) {
89+
url += 'limit=' + limit.trim() + '&';
90+
}
91+
92+
const res = mango.get(url);
93+
if (res.status_code !== 200)
94+
mango.raise('Failed to list chapters. Status ' + res.status_code);
95+
96+
const chapters = JSON.parse(res.body).data;
97+
98+
return JSON.stringify(chapters.map(function(ch) {
99+
return formatChapter(ch, title);
100+
}));
101+
}
102+
103+
function newChapters(mangaId, after) {
104+
var url = 'https://api.mangadex.org/manga/' + mangaId + '/feed?publishAtSince=' + formatTimestamp(after) + '&';
105+
106+
const langStr = mango.settings('language');
107+
if (langStr) {
108+
const langAry = langStr.split(',').forEach(function(lang) {
109+
url += 'translatedLanguage[]=' + lang.trim() + '&';
110+
});
111+
}
112+
113+
const limit = mango.settings('listChapterLimit');
114+
if (limit) {
115+
url += 'limit=' + limit.trim() + '&';
116+
}
117+
118+
const res = mango.get(url);
119+
if (res.status_code !== 200)
120+
mango.raise('Failed to list new chapters. Status ' + res.status_code);
121+
122+
const chapters = JSON.parse(res.body).data;
123+
124+
const manga = getManga(mangaId);
125+
const titleAttr = manga.attributes.title;
126+
const title = titleAttr.en || titleAttr['ja-ro'] || titleAttr[Object.keys(titleAttr)[0]];
127+
128+
return JSON.stringify(chapters.map(function(ch) {
129+
return formatChapter(ch, title);
130+
}));
45131
}
46132

47133
function selectChapter(id) {
48-
var json = {};
49-
var URL = 'https://mangadex.org/api/chapter/' + id;
50-
try {
51-
json = JSON.parse(mango.get(URL).body);
52-
} catch (e) {
53-
mango.raise('Failed to get JSON from ' + URL);
134+
const res = mango.get('https://api.mangadex.org/chapter/' + id);
135+
if (res.status_code !== 200)
136+
mango.raise('Failed to get chapter. Status ' + res.status_code);
137+
138+
const chapter = JSON.parse(res.body).data;
139+
var mangaId;
140+
for (i = 0; i < chapter.relationships.length; i++) {
141+
const obj = chapter.relationships[i];
142+
if (obj.type === 'manga') {
143+
mangaId = obj.id;
144+
break;
145+
}
54146
}
55147

56-
if (json.status !== 'OK')
57-
mango.raise('JSON status: ' + json.status);
148+
if (!mangaId)
149+
mango.raise('Failed to get Manga ID from chapter');
58150

59-
chapter = json;
60-
currentPage = 0;
151+
const manga = getManga(mangaId);
152+
const titleAttr = manga.attributes.title;
153+
const title = titleAttr.en || titleAttr['ja-ro'] || titleAttr[Object.keys(titleAttr)[0]];
61154

62-
var info = {
63-
title: json.title.trim() || ('Ch.' + json.chapter),
64-
pages: json.page_array.length
65-
};
66-
return JSON.stringify(info);
155+
const atHome = mango.get('https://api.mangadex.org/at-home/server/' + id);
156+
if (atHome.status_code !== 200)
157+
mango.raise('Failed to get at-home server. Status ' + atHome.status_code);
158+
159+
const atHomeData = JSON.parse(atHome.body);
160+
161+
mango.storage('atHomeData', JSON.stringify(atHomeData));
162+
mango.storage('page', '0');
163+
164+
return JSON.stringify(formatChapter(chapter, title));
67165
}
68166

69167
function nextPage() {
70-
if (currentPage >= chapter.page_array.length)
71-
return JSON.stringify({});
168+
const page = parseInt(mango.storage('page'));
169+
const atHome = JSON.parse(mango.storage('atHomeData'));
170+
const filename = atHome.chapter.data[page];
171+
if (!filename) return JSON.stringify({});
72172

73-
var fn = chapter.page_array[currentPage];
74-
var info = {
75-
filename: fn,
76-
url: chapter.server + chapter.hash + '/' + fn
77-
};
173+
//Get the number of digits of pages
174+
const len = atHome.chapter.data.length.toString().length;
175+
//Pad the page number with zeroes depending of the number of pages
176+
const pageNum = Array(Math.max(len - String(page + 1).length + 1, 0)).join(0) + (page + 1);
177+
178+
const finalFilename = pageNum + '.' + filename.split('.')[filename.split('.').length -1];
179+
mango.storage('page', (page + 1).toString());
78180

79-
currentPage += 1;
80-
return JSON.stringify(info);
181+
return JSON.stringify({
182+
url: atHome.baseUrl + '/data/' + atHome.chapter.hash + '/' + filename,
183+
filename: finalFilename,
184+
});
81185
}

plugins/mangadex/info.json

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@
22
"id": "mangadex",
33
"title": "MangaDex",
44
"author": "Alex Ling - [email protected]",
5-
"version": "v1.0",
6-
"placeholder": "MangaDex manga ID (e.g., 7139)",
7-
"wait_seconds": 5
5+
"version": "v2.0",
6+
"placeholder": "Search MangaDex",
7+
"wait_seconds": 5,
8+
"api_version": 2,
9+
"settings": {
10+
"language": "en",
11+
"listChapterLimit": "200"
12+
}
813
}

plugins/nhentai/index.js

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ function selectChapter(id) {
6363

6464
uglyPageURL = mango.attribute(firstPageATag, 'data-src');
6565
var pageURLMatch = /\/galleries\/([0-9]+)\/.*?\.(\w*)/.exec(uglyPageURL);
66-
66+
6767
pageURL = NHENTAI_IMAGE_URL + pageURLMatch[1] + "/";
6868
imageType = pageURLMatch[2];
6969

@@ -82,22 +82,28 @@ function nextPage() {
8282
if (ended) {
8383
return JSON.stringify({});
8484
}
85-
85+
8686
pageCount += 1;
8787

8888
if (pageCount === totalPages) {
8989
ended = true;
9090
}
9191

92-
var url = pageURL + pageCount + "." + imageType;
92+
var url, extension;
93+
var extensions = [imageType, 'png', 'jpg', 'gif', 'jpeg'];
9394

9495
// Not all galleries have uniform file types
9596
// Post should return 405 if path exists, 404 otherwise.
96-
if (mango.post(url, "").status_code == 404) {
97-
url = pageURL + pageCount + "." + (imageType == "png" ? "jpg" : "png");
97+
for (var i = 0; i < extensions.length; i++) {
98+
extension = extensions[i];
99+
url = pageURL + pageCount + "." + extension;
100+
if (mango.post(url, "").status_code == 405) {
101+
// We got the right extension
102+
break;
103+
}
98104
}
99105

100-
var filename = pad(pageCount, digits) + '.' + imageType;
106+
var filename = pad(pageCount, digits) + '.' + extension;
101107

102108
return JSON.stringify({
103109
url: url,

0 commit comments

Comments
 (0)