@@ -28,10 +28,32 @@ class RanobesPlugin implements Plugin.PluginBase {
|
|||||||
this.name = metadata.sourceName;
|
this.name = metadata.sourceName;
|
||||||
this.icon = 'multisrc/ranobes/ranobes/icon.png';
|
this.icon = 'multisrc/ranobes/ranobes/icon.png';
|
||||||
this.site = metadata.sourceSite;
|
this.site = metadata.sourceSite;
|
||||||
this.version = '2.0.1';
|
this.version = '2.0.2';
|
||||||
this.options = metadata.options as RanobesOptions;
|
this.options = metadata.options as RanobesOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async safeFecth(url: string, init: any = {}): Promise<string> {
|
||||||
|
const r = await fetchApi(url, init);
|
||||||
|
if (!r.ok)
|
||||||
|
throw new Error(
|
||||||
|
'Could not reach site (' + r.status + ') try to open in webview.',
|
||||||
|
);
|
||||||
|
const data = await r.text();
|
||||||
|
const title = data.match(/<title>(.*?)<\/title>/)?.[1]?.trim();
|
||||||
|
|
||||||
|
if (
|
||||||
|
title &&
|
||||||
|
(title == 'Bot Verification' ||
|
||||||
|
title == 'You are being redirected...' ||
|
||||||
|
title == 'Un instant...' ||
|
||||||
|
title == 'Just a moment...' ||
|
||||||
|
title == 'Redirecting...')
|
||||||
|
)
|
||||||
|
throw new Error('Captcha error, please open in webview');
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
parseNovels(html: string) {
|
parseNovels(html: string) {
|
||||||
const novels: Plugin.NovelItem[] = [];
|
const novels: Plugin.NovelItem[] = [];
|
||||||
let tempNovel = {} as Plugin.NovelItem;
|
let tempNovel = {} as Plugin.NovelItem;
|
||||||
@@ -154,7 +176,7 @@ class RanobesPlugin implements Plugin.PluginBase {
|
|||||||
|
|
||||||
async popularNovels(page: number): Promise<Plugin.NovelItem[]> {
|
async popularNovels(page: number): Promise<Plugin.NovelItem[]> {
|
||||||
const link = `${this.site}/${this.options.path}/page/${page}/`;
|
const link = `${this.site}/${this.options.path}/page/${page}/`;
|
||||||
const body = await fetchApi(link).then(r => r.text());
|
const body = await this.safeFecth(link);
|
||||||
return this.parseNovels(body);
|
return this.parseNovels(body);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,8 +184,7 @@ class RanobesPlugin implements Plugin.PluginBase {
|
|||||||
novelPath: string,
|
novelPath: string,
|
||||||
): Promise<Plugin.SourceNovel & { totalPages: number }> {
|
): Promise<Plugin.SourceNovel & { totalPages: number }> {
|
||||||
const baseUrl = this.site;
|
const baseUrl = this.site;
|
||||||
const result = await fetchApi(baseUrl + novelPath);
|
const html = await this.safeFecth(baseUrl + novelPath);
|
||||||
const html = await result.text();
|
|
||||||
const novel: Plugin.SourceNovel & { totalPages: number } = {
|
const novel: Plugin.SourceNovel & { totalPages: number } = {
|
||||||
path: novelPath,
|
path: novelPath,
|
||||||
name: '',
|
name: '',
|
||||||
@@ -330,9 +351,7 @@ class RanobesPlugin implements Plugin.PluginBase {
|
|||||||
: '/' + novelPath.split('-').slice(1).join('-').split('.')[0];
|
: '/' + novelPath.split('-').slice(1).join('-').split('.')[0];
|
||||||
const firstUrl =
|
const firstUrl =
|
||||||
this.site + '/chapters' + pagePath.replace(this.options.path + '/', '');
|
this.site + '/chapters' + pagePath.replace(this.options.path + '/', '');
|
||||||
const pageBody = await fetchApi(firstUrl + '/page/' + page).then(r =>
|
const pageBody = await this.safeFecth(firstUrl + '/page/' + page);
|
||||||
r.text(),
|
|
||||||
);
|
|
||||||
|
|
||||||
const baseUrl = this.site;
|
const baseUrl = this.site;
|
||||||
let isScript = false;
|
let isScript = false;
|
||||||
@@ -408,8 +427,7 @@ class RanobesPlugin implements Plugin.PluginBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async parseChapter(chapterPath: string): Promise<string> {
|
async parseChapter(chapterPath: string): Promise<string> {
|
||||||
const result = await fetchApi(this.site + chapterPath);
|
const html = await this.safeFecth(this.site + chapterPath);
|
||||||
const html = await result.text();
|
|
||||||
|
|
||||||
const indexA = html.indexOf('<div class="text" id="arrticle">');
|
const indexA = html.indexOf('<div class="text" id="arrticle">');
|
||||||
const indexB = html.indexOf('<div class="category grey ellipses">', indexA);
|
const indexB = html.indexOf('<div class="category grey ellipses">', indexA);
|
||||||
@@ -424,7 +442,7 @@ class RanobesPlugin implements Plugin.PluginBase {
|
|||||||
): Promise<Plugin.NovelItem[]> {
|
): Promise<Plugin.NovelItem[]> {
|
||||||
let html;
|
let html;
|
||||||
if (this.id === 'ranobes-ru') {
|
if (this.id === 'ranobes-ru') {
|
||||||
html = await fetchApi(this.site + '/index.php?do=search', {
|
html = await this.safeFecth(this.site + '/index.php?do=search', {
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/x-www-form-urlencoded',
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
Referer: this.site + '/',
|
Referer: this.site + '/',
|
||||||
@@ -436,10 +454,10 @@ class RanobesPlugin implements Plugin.PluginBase {
|
|||||||
search_start: page.toString(),
|
search_start: page.toString(),
|
||||||
story: searchTerm,
|
story: searchTerm,
|
||||||
}).toString(),
|
}).toString(),
|
||||||
}).then(res => res.text());
|
});
|
||||||
} else {
|
} else {
|
||||||
const link = `${this.site}/search/${searchTerm}/page/${page}`;
|
const link = `${this.site}/search/${searchTerm}/page/${page}`;
|
||||||
html = await fetchApi(link).then(r => r.text());
|
html = await this.safeFecth(link);
|
||||||
}
|
}
|
||||||
return this.parseNovels(html);
|
return this.parseNovels(html);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,9 +9,31 @@ class Foxteller implements Plugin.PluginBase {
|
|||||||
id = 'foxteller';
|
id = 'foxteller';
|
||||||
name = 'Foxteller';
|
name = 'Foxteller';
|
||||||
site = 'https://www.foxteller.com';
|
site = 'https://www.foxteller.com';
|
||||||
version = '1.0.1';
|
version = '1.0.2';
|
||||||
icon = 'src/en/foxteller/icon.png';
|
icon = 'src/en/foxteller/icon.png';
|
||||||
|
|
||||||
|
async safeFecth(url: string, init: any = {}): Promise<string> {
|
||||||
|
const r = await fetchApi(url, init);
|
||||||
|
if (!r.ok)
|
||||||
|
throw new Error(
|
||||||
|
'Could not reach site (' + r.status + ') try to open in webview.',
|
||||||
|
);
|
||||||
|
const data = await r.text();
|
||||||
|
const title = data.match(/<title>(.*?)<\/title>/)?.[1]?.trim();
|
||||||
|
|
||||||
|
if (
|
||||||
|
title &&
|
||||||
|
(title == 'Bot Verification' ||
|
||||||
|
title == 'You are being redirected...' ||
|
||||||
|
title == 'Un instant...' ||
|
||||||
|
title == 'Just a moment...' ||
|
||||||
|
title == 'Redirecting...')
|
||||||
|
)
|
||||||
|
throw new Error('Captcha error, please open in webview');
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
async popularNovels(
|
async popularNovels(
|
||||||
pageNo: number,
|
pageNo: number,
|
||||||
{ filters }: Plugin.PopularNovelsOptions<typeof this.filters>,
|
{ filters }: Plugin.PopularNovelsOptions<typeof this.filters>,
|
||||||
@@ -19,7 +41,7 @@ class Foxteller implements Plugin.PluginBase {
|
|||||||
const url =
|
const url =
|
||||||
this.site + '/library?sort=' + filters?.order?.value || 'popularity';
|
this.site + '/library?sort=' + filters?.order?.value || 'popularity';
|
||||||
|
|
||||||
const body = await fetchApi(url).then(res => res.text());
|
const body = await this.safeFecth(url);
|
||||||
const novels: Plugin.NovelItem[] = [];
|
const novels: Plugin.NovelItem[] = [];
|
||||||
|
|
||||||
const div = body.match(/<div class="col-md-6">([\s\S]*?)<\/div>/g) || [];
|
const div = body.match(/<div class="col-md-6">([\s\S]*?)<\/div>/g) || [];
|
||||||
@@ -44,9 +66,7 @@ class Foxteller implements Plugin.PluginBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async parseNovel(novelPath: string): Promise<Plugin.SourceNovel> {
|
async parseNovel(novelPath: string): Promise<Plugin.SourceNovel> {
|
||||||
const body = await fetchApi(this.resolveUrl(novelPath)).then(res =>
|
const body = await this.safeFecth(this.resolveUrl(novelPath));
|
||||||
res.text(),
|
|
||||||
);
|
|
||||||
const novel: Plugin.SourceNovel = {
|
const novel: Plugin.SourceNovel = {
|
||||||
path: novelPath,
|
path: novelPath,
|
||||||
name: '',
|
name: '',
|
||||||
@@ -189,10 +209,9 @@ class Foxteller implements Plugin.PluginBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async parseChapter(chapterPath: string): Promise<string> {
|
async parseChapter(chapterPath: string): Promise<string> {
|
||||||
const res = await fetchApi(this.resolveUrl(chapterPath));
|
const res = await this.safeFecth(this.resolveUrl(chapterPath));
|
||||||
const body = await res.text();
|
|
||||||
const novelID = chapterPath.split('/')[0];
|
const novelID = chapterPath.split('/')[0];
|
||||||
const chapterID = body.match(/'chapter_id': '([\d]+)'/)?.[1];
|
const chapterID = res.match(/'chapter_id': '([\d]+)'/)?.[1];
|
||||||
|
|
||||||
if (!chapterID) throw new Error('No chapter found');
|
if (!chapterID) throw new Error('No chapter found');
|
||||||
|
|
||||||
@@ -203,7 +222,7 @@ class Foxteller implements Plugin.PluginBase {
|
|||||||
'Accept-Language': 'ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3',
|
'Accept-Language': 'ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3',
|
||||||
'Content-Type': 'application/json;charset=utf-8',
|
'Content-Type': 'application/json;charset=utf-8',
|
||||||
},
|
},
|
||||||
Referer: res.url,
|
Referer: this.resolveUrl(chapterPath),
|
||||||
body: JSON.stringify({ 'x1': novelID, 'x2': chapterID }),
|
body: JSON.stringify({ 'x1': novelID, 'x2': chapterID }),
|
||||||
}).then(res => res.json());
|
}).then(res => res.json());
|
||||||
|
|
||||||
@@ -234,7 +253,7 @@ class Foxteller implements Plugin.PluginBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async searchNovels(searchTerm: string): Promise<Plugin.NovelItem[]> {
|
async searchNovels(searchTerm: string): Promise<Plugin.NovelItem[]> {
|
||||||
const body = await fetchApi(this.site + '/search', {
|
const body = await this.safeFecth(this.site + '/search', {
|
||||||
method: 'post',
|
method: 'post',
|
||||||
headers: {
|
headers: {
|
||||||
Accept: 'application/json, text/plain, */*',
|
Accept: 'application/json, text/plain, */*',
|
||||||
@@ -243,7 +262,7 @@ class Foxteller implements Plugin.PluginBase {
|
|||||||
},
|
},
|
||||||
Referer: this.site,
|
Referer: this.site,
|
||||||
body: JSON.stringify({ query: searchTerm }),
|
body: JSON.stringify({ query: searchTerm }),
|
||||||
}).then(res => res.text());
|
});
|
||||||
const novels: Plugin.NovelItem[] = [];
|
const novels: Plugin.NovelItem[] = [];
|
||||||
|
|
||||||
const items = body.match(/<a.*>([\s\S]*?)<\/a>/g) || [];
|
const items = body.match(/<a.*>([\s\S]*?)<\/a>/g) || [];
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ class RLIB implements Plugin.PluginBase {
|
|||||||
name = 'RanobeLib';
|
name = 'RanobeLib';
|
||||||
site = 'https://ranobelib.me';
|
site = 'https://ranobelib.me';
|
||||||
apiSite = 'https://api.mangalib.me/api/manga/';
|
apiSite = 'https://api.mangalib.me/api/manga/';
|
||||||
version = '2.1.0';
|
version = '2.1.1';
|
||||||
icon = 'src/ru/ranobelib/icon.png';
|
icon = 'src/ru/ranobelib/icon.png';
|
||||||
webStorageUtilized = true;
|
webStorageUtilized = true;
|
||||||
|
|
||||||
@@ -126,10 +126,10 @@ class RLIB implements Plugin.PluginBase {
|
|||||||
novel.genres = genres.join(', ');
|
novel.genres = genres.join(', ');
|
||||||
}
|
}
|
||||||
|
|
||||||
const branch_id: Record<number, string> = {};
|
const branch_name: Record<number, string> = {};
|
||||||
if (data.teams.length) {
|
if (data.teams.length) {
|
||||||
data.teams.forEach(
|
data.teams.forEach(
|
||||||
({ name, details }) => (branch_id[details?.branch_id || '0'] = name),
|
({ name, details }) => (branch_name[details?.branch_id || '0'] = name),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,27 +144,39 @@ class RLIB implements Plugin.PluginBase {
|
|||||||
const chapters: Plugin.ChapterItem[] = [];
|
const chapters: Plugin.ChapterItem[] = [];
|
||||||
|
|
||||||
chaptersJSON.data.forEach(chapter =>
|
chaptersJSON.data.forEach(chapter =>
|
||||||
chapters.push({
|
chapter.branches.forEach(({ branch_id, created_at }) =>
|
||||||
name:
|
chapters.push({
|
||||||
'Том ' +
|
name:
|
||||||
chapter.volume +
|
'Том ' +
|
||||||
' Глава ' +
|
chapter.volume +
|
||||||
chapter.number +
|
' Глава ' +
|
||||||
(chapter.name ? ' ' + chapter.name : ''),
|
chapter.number +
|
||||||
path:
|
(chapter.name ? ' ' + chapter.name : ''),
|
||||||
novelPath +
|
path:
|
||||||
'/' +
|
novelPath +
|
||||||
chapter.volume +
|
'/' +
|
||||||
'/' +
|
chapter.volume +
|
||||||
chapter.number +
|
'/' +
|
||||||
'/' +
|
chapter.number +
|
||||||
(chapter.branches[0]?.branch_id || ''),
|
'/' +
|
||||||
releaseTime: dayjs(chapter.branches[0].created_at).format('LLL'),
|
(branch_id || ''),
|
||||||
chapterNumber: chapter.index,
|
releaseTime: dayjs(created_at).format('LLL'),
|
||||||
page: branch_id[chapter.branches[0].branch_id || '0'],
|
chapterNumber: chapter.index,
|
||||||
}),
|
page: branch_name[branch_id || '0'],
|
||||||
|
}),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (chapters.length && data.teams.length > 1) {
|
||||||
|
//For whatever reason, the chapters overlap with another page.
|
||||||
|
chapters.sort((chapterA, chapterB) => {
|
||||||
|
if (chapterA.page !== chapterB.page) {
|
||||||
|
return chapterA.page.localeCompare(chapterB.page);
|
||||||
|
}
|
||||||
|
return chapterA.chapterNumber - chapterB.chapterNumber;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
novel.chapters = chapters;
|
novel.chapters = chapters;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -509,6 +521,42 @@ function jsonToHtml(json: HTML[], images, html = '') {
|
|||||||
(element.content ? jsonToHtml(element.content, images) : '<br>') +
|
(element.content ? jsonToHtml(element.content, images) : '<br>') +
|
||||||
'</p>';
|
'</p>';
|
||||||
break;
|
break;
|
||||||
|
case 'orderedList':
|
||||||
|
html +=
|
||||||
|
'<ol>' +
|
||||||
|
(element.content ? jsonToHtml(element.content, images) : '<br>') +
|
||||||
|
'</ol>';
|
||||||
|
break;
|
||||||
|
case 'listItem':
|
||||||
|
html +=
|
||||||
|
'<li>' +
|
||||||
|
(element.content ? jsonToHtml(element.content, images) : '<br>') +
|
||||||
|
'</li>';
|
||||||
|
break;
|
||||||
|
case 'blockquote':
|
||||||
|
html +=
|
||||||
|
'<blockquote>' +
|
||||||
|
(element.content ? jsonToHtml(element.content, images) : '<br>') +
|
||||||
|
'</blockquote>';
|
||||||
|
break;
|
||||||
|
case 'italic':
|
||||||
|
html +=
|
||||||
|
'<i>' +
|
||||||
|
(element.content ? jsonToHtml(element.content, images) : '<br>') +
|
||||||
|
'</i>';
|
||||||
|
break;
|
||||||
|
case 'bold':
|
||||||
|
html +=
|
||||||
|
'<b>' +
|
||||||
|
(element.content ? jsonToHtml(element.content, images) : '<br>') +
|
||||||
|
'</b>';
|
||||||
|
break;
|
||||||
|
case 'underline':
|
||||||
|
html +=
|
||||||
|
'<u>' +
|
||||||
|
(element.content ? jsonToHtml(element.content, images) : '<br>') +
|
||||||
|
'</u>';
|
||||||
|
break;
|
||||||
case 'heading':
|
case 'heading':
|
||||||
html +=
|
html +=
|
||||||
'<h2>' +
|
'<h2>' +
|
||||||
|
|||||||
Reference in New Issue
Block a user