feat: Add New Sources (#583)
* fix * Update ranobelib.js * fix * test * fix * fix path * test * added sources * added ALL sources on novel.tl * add tags * Update NovelTlScraper.js * fix
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import CookieManager from '@react-native-cookies/cookies';
|
||||
import { useMMKVObject } from 'react-native-mmkv';
|
||||
import { MMKVStorage } from '@utils/mmkv/mmkv';
|
||||
import { showToast } from './showToast';
|
||||
@@ -38,7 +39,8 @@ const useSourceStorage = ({ sourceId = -1 }: { sourceId?: number }) => {
|
||||
// }),
|
||||
// );
|
||||
// }
|
||||
|
||||
CookieManager.clearAll();
|
||||
CookieManager.clearAll(true); //clears cookies in webkit
|
||||
setValues({});
|
||||
showToast('Cookies cleared');
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,230 @@
|
||||
import dayjs from 'dayjs';
|
||||
import * as cheerio from 'cheerio';
|
||||
import { htmlToText } from '../../helpers/htmlToText';
|
||||
import { Status, defaultCoverUri } from '../../helpers/constants';
|
||||
|
||||
class NovelTlScraper {
|
||||
constructor(sourceId, baseUrl, sourceName, filter) {
|
||||
this.sourceId = sourceId;
|
||||
this.baseUrl = baseUrl;
|
||||
this.domain = baseUrl.split('//')[1];
|
||||
this.sourceName = sourceName;
|
||||
|
||||
this.filters = filter || null;
|
||||
}
|
||||
|
||||
async popularNovels(page, { filters }) {
|
||||
const result = await fetch('https://api.novel.tl/api/site/v2/graphql', {
|
||||
method: 'post',
|
||||
headers: {
|
||||
'User-Agent':
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/111.0',
|
||||
'Accept': 'application/json, text/plain, */*',
|
||||
'Accept-Language': 'ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3',
|
||||
'Content-Type': 'application/json',
|
||||
'Alt-Used': 'api.novel.tl',
|
||||
'Sec-Fetch-Dest': 'empty',
|
||||
'Sec-Fetch-Mode': 'cors',
|
||||
'Sec-Fetch-Site': 'cross-site',
|
||||
},
|
||||
referrer: this.baseUrl,
|
||||
body: JSON.stringify({
|
||||
operationName: 'Projects',
|
||||
query:
|
||||
'query Projects($hostname:String! $filter:SearchFilter $page:Int $limit:Int){projects(section:{fullUrl:$hostname}filter:$filter page:{pageSize:$limit,pageNumber:$page}){content{title url covers{url}}}}',
|
||||
variables: {
|
||||
filter: filters?.tags?.length > 0 ? { tags: filters.tags } : {},
|
||||
hostname: this.domain,
|
||||
limit: 40,
|
||||
page,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
const json = await result.json();
|
||||
let novels = json?.data?.projects?.content?.map(novel => ({
|
||||
novelName: novel.title,
|
||||
novelUrl: novel.url,
|
||||
sourceId: this.sourceId,
|
||||
novelCover: novel.covers[0]?.url
|
||||
? this.baseUrl + novel.covers[0].url
|
||||
: defaultCoverUri,
|
||||
}));
|
||||
|
||||
return { novels };
|
||||
}
|
||||
|
||||
async parseNovelAndChapters(novelUrl) {
|
||||
const result = await fetch('https://api.novel.tl/api/site/v2/graphql', {
|
||||
method: 'post',
|
||||
headers: {
|
||||
'User-Agent':
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/111.0',
|
||||
'Accept': 'application/json, text/plain, */*',
|
||||
'Accept-Language': 'ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3',
|
||||
'Content-Type': 'application/json',
|
||||
'Alt-Used': 'api.novel.tl',
|
||||
'Sec-Fetch-Dest': 'empty',
|
||||
'Sec-Fetch-Mode': 'cors',
|
||||
'Sec-Fetch-Site': 'cross-site',
|
||||
},
|
||||
referrer: this.baseUrl,
|
||||
body: JSON.stringify({
|
||||
operationName: 'Book',
|
||||
query:
|
||||
'query Book($url:String){project(project:{fullUrl:$url}){title translationStatus url covers{url}persons(langs:["ru","en","*"]roles:["author","illustrator","original_story","original_design"]){role name{firstName lastName}}genres{nameRu nameEng}tags{nameRu nameEng}annotation{text}subprojects{content{title volumes{content{url shortName chapters{title publishDate url published}}}}}}}',
|
||||
variables: {
|
||||
hostname: this.domain,
|
||||
project: novelUrl,
|
||||
url: this.domain + '/r/' + novelUrl,
|
||||
},
|
||||
}),
|
||||
});
|
||||
const json = await result.json();
|
||||
|
||||
let novel = {
|
||||
sourceId: this.sourceId,
|
||||
sourceName: this.sourceName,
|
||||
url: this.baseUrl + '/r/' + novelUrl,
|
||||
novelUrl,
|
||||
novelName: json.data.project.title,
|
||||
novelCover: json.data.project.covers[0]?.url
|
||||
? this.baseUrl + json.data.project.covers[0].url
|
||||
: defaultCoverUri,
|
||||
summary: htmlToText(json.data.project.annotation?.text),
|
||||
author:
|
||||
json.data.project.persons[0].name.firstName +
|
||||
' ' +
|
||||
json.data.project.persons[0].name.lastName,
|
||||
status:
|
||||
json.data.project.translationStatus === 'active'
|
||||
? Status.ONGOING
|
||||
: Status.COMPLETED,
|
||||
};
|
||||
|
||||
let genres = []
|
||||
.concat(json.data.project.tags, json.data.project.genres)
|
||||
.map(item => item?.nameRu || item?.nameEng)
|
||||
.filter(item => item);
|
||||
|
||||
if (genres.length > 0) {
|
||||
novel.genre = genres.join(',');
|
||||
}
|
||||
|
||||
let novelChapters = [];
|
||||
json.data.project.subprojects.content.forEach(work =>
|
||||
work.volumes.content.forEach(volume =>
|
||||
volume.chapters.forEach(
|
||||
chapter =>
|
||||
chapter?.published &&
|
||||
novelChapters.push({
|
||||
chapterName: volume.shortName + ' ' + chapter.title,
|
||||
chapterUrl: volume.url + '/' + chapter.url,
|
||||
releaseDate: dayjs(chapter.publishDate).format('LLL'),
|
||||
}),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
novel.chapters = novelChapters;
|
||||
return novel;
|
||||
}
|
||||
|
||||
async parseChapter(novelUrl, chapterUrl) {
|
||||
const baseUrl = this.baseUrl;
|
||||
const sourceId = this.sourceId;
|
||||
const result = await fetch('https://api.novel.tl/api/site/v2/graphql', {
|
||||
method: 'post',
|
||||
headers: {
|
||||
'User-Agent':
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/111.0',
|
||||
'Accept': 'application/json, text/plain, */*',
|
||||
'Accept-Language': 'ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3',
|
||||
'Content-Type': 'application/json',
|
||||
'Alt-Used': 'api.novel.tl',
|
||||
'Sec-Fetch-Dest': 'empty',
|
||||
'Sec-Fetch-Mode': 'cors',
|
||||
'Sec-Fetch-Site': 'cross-site',
|
||||
},
|
||||
referrer: baseUrl,
|
||||
body: JSON.stringify({
|
||||
operationName: 'EReaderData',
|
||||
query:
|
||||
'query EReaderData($url:String!,$chapterSelector:Selector!){project(project:{fullUrl:$url}){title url}chapter(chapter:$chapterSelector){title text{text}}}',
|
||||
variables: {
|
||||
chapterSelector: {
|
||||
fullUrl: this.domain + '/r/' + novelUrl + '/' + chapterUrl,
|
||||
},
|
||||
url: this.domain + '/r/' + novelUrl + '/' + chapterUrl,
|
||||
},
|
||||
}),
|
||||
});
|
||||
const json = await result.json();
|
||||
|
||||
const loadedCheerio = cheerio.load(json.data.chapter.text.text);
|
||||
loadedCheerio('img').each(function () {
|
||||
if (!loadedCheerio(this).attr('src')?.startsWith('http')) {
|
||||
let src = loadedCheerio(this).attr('src');
|
||||
loadedCheerio(this).attr('src', baseUrl + src);
|
||||
}
|
||||
});
|
||||
|
||||
const chapter = {
|
||||
sourceId,
|
||||
novelUrl,
|
||||
chapterUrl,
|
||||
chapterName: json.data.chapter.title,
|
||||
chapterText: loadedCheerio.html(),
|
||||
};
|
||||
|
||||
return chapter;
|
||||
}
|
||||
|
||||
async searchNovels(searchTerm) {
|
||||
const result = await fetch('https://api.novel.tl/api/site/v2/graphql', {
|
||||
method: 'post',
|
||||
headers: {
|
||||
'User-Agent':
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/111.0',
|
||||
'Accept': 'application/json, text/plain, */*',
|
||||
'Accept-Language': 'ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3',
|
||||
'Content-Type': 'application/json',
|
||||
'Alt-Used': 'api.novel.tl',
|
||||
'Sec-Fetch-Dest': 'empty',
|
||||
'Sec-Fetch-Mode': 'cors',
|
||||
'Sec-Fetch-Site': 'cross-site',
|
||||
},
|
||||
referrer: this.baseUrl,
|
||||
body: JSON.stringify({
|
||||
operationName: 'Projects',
|
||||
query:
|
||||
'query Projects($hostname:String! $filter:SearchFilter $page:Int $limit:Int){projects(section:{fullUrl:$hostname}filter:$filter page:{pageSize:$limit,pageNumber:$page}){content{title url covers{url}}}}',
|
||||
variables: {
|
||||
filter: {
|
||||
query: searchTerm,
|
||||
},
|
||||
hostname: this.domain,
|
||||
limit: 40,
|
||||
page: 1,
|
||||
},
|
||||
}),
|
||||
});
|
||||
const json = await result.json();
|
||||
|
||||
let novels = [];
|
||||
json?.data?.projects?.content?.forEach(novel =>
|
||||
novels.push({
|
||||
novelName: novel.title,
|
||||
novelUrl: novel.url,
|
||||
sourceId: this.sourceId,
|
||||
novelCover: novel.covers[0]?.url
|
||||
? this.baseUrl + novel.covers[0].url
|
||||
: defaultCoverUri,
|
||||
}),
|
||||
);
|
||||
|
||||
return novels;
|
||||
}
|
||||
}
|
||||
|
||||
export default NovelTlScraper;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,282 @@
|
||||
import { FilterInputs } from '../../types/filterTypes';
|
||||
import { htmlToText } from '../../helpers/htmlToText';
|
||||
import { Status } from '../../helpers/constants';
|
||||
import * as cheerio from 'cheerio';
|
||||
import { defaultTo } from 'lodash-es';
|
||||
|
||||
class RulateScraper {
|
||||
constructor(sourceId, baseUrl, sourceName, filter) {
|
||||
this.sourceId = sourceId;
|
||||
this.baseUrl = baseUrl;
|
||||
this.sourceName = sourceName;
|
||||
this.filters = [
|
||||
{
|
||||
key: 'sort',
|
||||
label: 'Сортировка',
|
||||
values: [
|
||||
{ label: 'По рейтингу', value: '6' },
|
||||
{ label: 'По степени готовности', value: '0' },
|
||||
{ label: 'По названию на языке оригинала', value: '1' },
|
||||
{ label: 'По названию на языке перевода', value: '2' },
|
||||
{ label: 'По дате создания', value: '3' },
|
||||
{ label: 'По дате последней активности', value: '4' },
|
||||
{ label: 'По просмотрам', value: '5' },
|
||||
{ label: 'По кол-ву переведённых глав', value: '7' },
|
||||
{ label: 'По кол-ву лайков', value: '8' },
|
||||
{ label: 'По кол-ву страниц', value: '10' },
|
||||
{ label: 'По кол-ву бесплатных глав', value: '11' },
|
||||
{ label: 'По кол-ву рецензий', value: '12' },
|
||||
{ label: 'По кол-ву в закладках', value: '13' },
|
||||
{ label: 'По кол-ву в избранном', value: '14' },
|
||||
],
|
||||
inputType: FilterInputs.Picker,
|
||||
},
|
||||
{
|
||||
key: 'type',
|
||||
label: 'Тип',
|
||||
values: [
|
||||
{ label: 'Неважно', value: '0' },
|
||||
{ label: 'Только переводы', value: '1' },
|
||||
{ label: 'Только авторские', value: '2' },
|
||||
],
|
||||
inputType: FilterInputs.Picker,
|
||||
},
|
||||
{
|
||||
key: 'atmosphere',
|
||||
label: 'Атмосфера',
|
||||
values: [
|
||||
{ label: 'Неважно', value: '0' },
|
||||
{ label: 'Позитивная', value: '1' },
|
||||
{ label: 'Dark', value: '2' },
|
||||
],
|
||||
inputType: FilterInputs.Picker,
|
||||
},
|
||||
{
|
||||
key: 'trash',
|
||||
label: 'Другое',
|
||||
values: [
|
||||
{ label: 'Готовые на 100%', value: 'ready' },
|
||||
{ label: 'Доступные для перевода', value: 'tr' },
|
||||
{ label: 'Доступные для скачивания', value: 'gen' },
|
||||
{ label: 'Завершённые проекты', value: 'wealth' },
|
||||
{ label: 'Распродажа', value: 'discount' },
|
||||
{ label: 'Только онгоинги', value: 'ongoings' },
|
||||
{ label: 'Убрать машинный', value: 'remove_machinelate' },
|
||||
],
|
||||
inputType: FilterInputs.Checkbox,
|
||||
},
|
||||
...filter,
|
||||
{
|
||||
key: 'adult',
|
||||
label: 'Возрастные ограничения',
|
||||
values: [
|
||||
{ label: 'Все', value: '0' },
|
||||
{ label: 'Убрать 18+', value: '1' },
|
||||
{ label: 'Только 18+', value: '2' },
|
||||
],
|
||||
inputType: FilterInputs.Picker,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
async popularNovels(page, { showLatestNovels, filters }) {
|
||||
const baseUrl = this.baseUrl;
|
||||
const sourceId = this.sourceId;
|
||||
let url = baseUrl + '/search?t=&cat=2';
|
||||
url += '&sort=' + defaultTo(filters?.sort, showLatestNovels ? '4' : '6');
|
||||
url += '&type=' + defaultTo(filters?.type, '0');
|
||||
url += '&atmosphere=' + defaultTo(filters?.atmosphere, '0');
|
||||
url += '&adult=' + defaultTo(filters?.adult, '0');
|
||||
|
||||
if (filters?.genres?.length) {
|
||||
url += filters.genres.map(i => `&genres[]=${i}`).join('');
|
||||
}
|
||||
|
||||
if (filters?.tags?.length) {
|
||||
url += filters.tags.map(i => `&tags[]=${i}`).join('');
|
||||
}
|
||||
|
||||
if (filters?.trash?.length) {
|
||||
url += filters.trash.map(i => `&${i}=1`);
|
||||
}
|
||||
|
||||
url += `&Book_page=${page}`;
|
||||
|
||||
const result = await fetch(url);
|
||||
const body = await result.text();
|
||||
const loadedCheerio = cheerio.load(body);
|
||||
|
||||
let novels = [];
|
||||
|
||||
loadedCheerio(
|
||||
'ul[class="search-results"] > li:not([class="ad_type_catalog"])',
|
||||
).each(function () {
|
||||
novels.push({
|
||||
sourceId,
|
||||
novelName: loadedCheerio(this).find('p > a').text(),
|
||||
novelCover: baseUrl + loadedCheerio(this).find('img').attr('src'),
|
||||
novelUrl: loadedCheerio(this).find('p > a').attr('href'),
|
||||
});
|
||||
});
|
||||
|
||||
return { novels };
|
||||
}
|
||||
|
||||
async parseNovelAndChapters(novelUrl) {
|
||||
let result = await fetch(this.baseUrl + novelUrl);
|
||||
if (result.url.includes('mature?path=')) {
|
||||
const formData = new FormData();
|
||||
formData.append('path', novelUrl);
|
||||
formData.append('ok', 'Да');
|
||||
|
||||
await fetch(result.url, {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
});
|
||||
|
||||
result = await fetch(this.baseUrl + novelUrl);
|
||||
}
|
||||
const body = await result.text();
|
||||
const loadedCheerio = cheerio.load(body);
|
||||
|
||||
let novel = {
|
||||
sourceId: this.sourceId,
|
||||
sourceName: this.sourceName,
|
||||
url: this.baseUrl + novelUrl,
|
||||
novelUrl,
|
||||
};
|
||||
|
||||
novel.novelName = loadedCheerio('div[class="container"] > div > div > h1')
|
||||
.text()
|
||||
.trim();
|
||||
novel.novelCover =
|
||||
this.baseUrl + loadedCheerio('div[class="images"] > div img').attr('src');
|
||||
novel.summary = loadedCheerio('#Info > div:nth-child(3)').html();
|
||||
let genre = [];
|
||||
|
||||
if (novel?.summary) {
|
||||
novel.summary = htmlToText(novel.summary);
|
||||
}
|
||||
|
||||
loadedCheerio('div[class="span5"] > p').each(function () {
|
||||
switch (loadedCheerio(this).find('strong').text()) {
|
||||
case 'Автор:':
|
||||
novel.author = loadedCheerio(this).find('em > a').text().trim();
|
||||
break;
|
||||
case 'Выпуск:':
|
||||
novel.status =
|
||||
loadedCheerio(this).find('em').text().trim() === 'продолжается'
|
||||
? Status.ONGOING
|
||||
: Status.COMPLETED;
|
||||
break;
|
||||
case 'Тэги:':
|
||||
loadedCheerio(this)
|
||||
.find('em > a')
|
||||
.each(function () {
|
||||
genre.push(loadedCheerio(this).text());
|
||||
});
|
||||
break;
|
||||
case 'Жанры:':
|
||||
loadedCheerio(this)
|
||||
.find('em > a')
|
||||
.each(function () {
|
||||
genre.push(loadedCheerio(this).text());
|
||||
});
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
if (genre.length > 0) {
|
||||
novel.genre = genre.reverse().join(',');
|
||||
}
|
||||
|
||||
let chapters = [];
|
||||
loadedCheerio('table > tbody > tr.chapter_row').each(function () {
|
||||
const chapterName = loadedCheerio(this)
|
||||
.find('td[class="t"] > a')
|
||||
.text()
|
||||
.trim();
|
||||
const releaseDate = loadedCheerio(this)
|
||||
.find('td > span')
|
||||
.attr('title')
|
||||
?.trim();
|
||||
const chapterUrl = loadedCheerio(this)
|
||||
.find('td[class="t"] > a')
|
||||
.attr('href');
|
||||
|
||||
if (
|
||||
loadedCheerio(this).find('td > span[class="disabled"]').length < 1 &&
|
||||
releaseDate
|
||||
) {
|
||||
chapters.push({ chapterName, releaseDate, chapterUrl });
|
||||
}
|
||||
});
|
||||
|
||||
novel.chapters = chapters;
|
||||
return novel;
|
||||
}
|
||||
|
||||
async parseChapter(novelUrl, chapterUrl) {
|
||||
const baseUrl = this.baseUrl;
|
||||
const sourceId = this.sourceId;
|
||||
let result = await fetch(baseUrl + chapterUrl);
|
||||
if (result.url.includes('mature?path=')) {
|
||||
const formData = new FormData();
|
||||
formData.append('path', novelUrl);
|
||||
formData.append('ok', 'Да');
|
||||
|
||||
await fetch(result.url, {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
});
|
||||
|
||||
result = await fetch(baseUrl + chapterUrl);
|
||||
}
|
||||
const body = await result.text();
|
||||
const loadedCheerio = cheerio.load(body);
|
||||
|
||||
loadedCheerio('.content-text img').each(function () {
|
||||
if (!loadedCheerio(this).attr('src')?.startsWith('http')) {
|
||||
let src = loadedCheerio(this).attr('src');
|
||||
loadedCheerio(this).attr('src', baseUrl + src);
|
||||
}
|
||||
});
|
||||
|
||||
const chapterName = loadedCheerio(
|
||||
'.chapter_select > select > option[selected]',
|
||||
)
|
||||
.text()
|
||||
.trim();
|
||||
const chapterText = loadedCheerio('.content-text').html();
|
||||
|
||||
const chapter = {
|
||||
sourceId,
|
||||
novelUrl,
|
||||
chapterUrl,
|
||||
chapterName,
|
||||
chapterText,
|
||||
};
|
||||
|
||||
return chapter;
|
||||
}
|
||||
|
||||
async searchNovels(searchTerm) {
|
||||
const result = await fetch(
|
||||
this.baseUrl + '/search/autocomplete?query=' + searchTerm,
|
||||
);
|
||||
let json = await result.json();
|
||||
let novels = [];
|
||||
|
||||
json.forEach(item => {
|
||||
const novelName = item.title_one + ' / ' + item.title_two;
|
||||
const novelCover = this.baseUrl + item.img;
|
||||
const novelUrl = item.url;
|
||||
|
||||
novels.push({ sourceId: this.sourceId, novelName, novelCover, novelUrl });
|
||||
});
|
||||
|
||||
return novels;
|
||||
}
|
||||
}
|
||||
|
||||
export default RulateScraper;
|
||||
@@ -9,7 +9,7 @@ const sourceName = 'Jaomix';
|
||||
const baseUrl = 'https://jaomix.ru';
|
||||
|
||||
const popularNovels = async (page, { showLatestNovels, filters }) => {
|
||||
let url = baseUrl + '/?search=&sortby=';
|
||||
let url = baseUrl + '/?searchrn&sortby=';
|
||||
url += defaultTo(filters?.sort, showLatestNovels ? 'upd' : 'count');
|
||||
|
||||
if (filters?.type?.length) {
|
||||
@@ -110,7 +110,7 @@ const parseChapter = async (novelUrl, chapterUrl) => {
|
||||
};
|
||||
|
||||
const searchNovels = async searchTerm => {
|
||||
const url = `${baseUrl}/?search=${searchTerm}&but=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA+%D0%BF%D0%BE+%D0%BD%D0%B0%D0%B7%D0%B2%D0%B0%D0%BD%D0%B8%D1%8E&sortby=new`;
|
||||
const url = `${baseUrl}/?searchrn=${searchTerm}&but=Поиск по названию&sortby=upd`;
|
||||
const result = await fetch(url);
|
||||
let body = await result.text();
|
||||
const loadedCheerio = cheerio.load(body);
|
||||
|
||||
@@ -85,8 +85,8 @@ const parseNovelAndChapters = async novelUrl => {
|
||||
const chaptersRaw = await fetch(fetchChaptersUrl);
|
||||
const chaptersJSON = await chaptersRaw.json();
|
||||
|
||||
chaptersJSON.volumes.map(volume =>
|
||||
volume.chapters.map(chapter =>
|
||||
chaptersJSON.volumes.forEach(volume =>
|
||||
volume.chapters.forEach(chapter =>
|
||||
novelChapters.push({
|
||||
chapterName: chapter.name,
|
||||
chapterUrl: chapter.url,
|
||||
@@ -153,7 +153,7 @@ const searchNovels = async searchTerm => {
|
||||
|
||||
data
|
||||
.find(item => item.meta.key === 'ranobe')
|
||||
?.data.map(novel =>
|
||||
?.data.forEach(novel =>
|
||||
novels.push({
|
||||
sourceId,
|
||||
novelName: novel.names.rus,
|
||||
|
||||
+16
-12
@@ -8,6 +8,7 @@ const sourceId = 93;
|
||||
const sourceName = 'RanobeLib';
|
||||
|
||||
const baseUrl = 'https://ranobelib.me';
|
||||
var ui = null;
|
||||
|
||||
const popularNovels = async (page, { showLatestNovels, filters }) => {
|
||||
let url = `${baseUrl}/manga-list?sort=`;
|
||||
@@ -46,6 +47,9 @@ const popularNovels = async (page, { showLatestNovels, filters }) => {
|
||||
const body = await result.text();
|
||||
|
||||
const loadedCheerio = cheerio.load(body);
|
||||
ui = loadedCheerio('a.header-right-menu__item')
|
||||
.attr('href')
|
||||
?.replace?.(/[^0-9]/g, '');
|
||||
|
||||
let novels = [];
|
||||
|
||||
@@ -122,25 +126,25 @@ const parseNovelAndChapters = async novelUrl => {
|
||||
?.slice(0, -1);
|
||||
|
||||
chaptersJson = JSON.parse(chaptersJson);
|
||||
ui = chaptersJson?.user?.id;
|
||||
|
||||
const novelSlug = chaptersJson.manga.slug;
|
||||
|
||||
chaptersJson.chapters.list.map(item => {
|
||||
const chapterName =
|
||||
`Том ${item.chapter_volume} Глава ${item.chapter_number} ${item.chapter_name}`?.trim();
|
||||
const releaseDate = dayjs(item.chapter_created_at).format('LLL');
|
||||
const chapterUrl = `${baseUrl}/${novelSlug}/v${item.chapter_volume}/c${item.chapter_number}`;
|
||||
|
||||
chapters.push({ chapterName, releaseDate, chapterUrl });
|
||||
chaptersJson.chapters.list.forEach(chapter => {
|
||||
chapters.push({
|
||||
chapterName:
|
||||
`Том ${chapter.chapter_volume} Глава ${chapter.chapter_number} ${chapter.chapter_name}`?.trim(),
|
||||
releaseDate: dayjs(chapter.chapter_created_at).format('LLL'),
|
||||
chapterUrl:
|
||||
`${baseUrl}/${chaptersJson.manga.slug}/v${chapter.chapter_volume}/c${chapter.chapter_number}?bid=` +
|
||||
(chapter?.branch_id || ''),
|
||||
});
|
||||
});
|
||||
|
||||
novel.chapters = chapters.reverse();
|
||||
|
||||
return novel;
|
||||
};
|
||||
|
||||
const parseChapter = async (novelUrl, chapterUrl) => {
|
||||
const result = await fetch(chapterUrl);
|
||||
const result = await fetch(chapterUrl + (ui ? `&ui=${ui}` : ''));
|
||||
const body = await result.text();
|
||||
|
||||
const loadedCheerio = cheerio.load(body);
|
||||
@@ -179,7 +183,7 @@ const searchNovels = async searchTerm => {
|
||||
const body = await result.json();
|
||||
let novels = [];
|
||||
|
||||
body.map(novel => {
|
||||
body.forEach(novel => {
|
||||
const novelName = novel?.rus_name || novel.name;
|
||||
const novelUrl = novel?.href || baseUrl + '/' + novel.slug;
|
||||
const novelCover = novel.coverImage;
|
||||
|
||||
@@ -26,7 +26,7 @@ const popularNovels = async (page, { showLatestNovels, filters }) => {
|
||||
json = JSON.parse(json);
|
||||
let novels = [];
|
||||
|
||||
json.props.pageProps.totalData.items.map(item => {
|
||||
json.props.pageProps.totalData.items.forEach(item => {
|
||||
const novelName = item.title;
|
||||
const novelUrl = baseUrl + '/' + item.slug;
|
||||
const novelCover = item?.verticalImage?.url
|
||||
@@ -65,7 +65,7 @@ const parseNovelAndChapters = async novelUrl => {
|
||||
|
||||
let chapters = [];
|
||||
|
||||
json.props.pageProps.book.chapters.map(item => {
|
||||
json.props.pageProps.book.chapters.forEach(item => {
|
||||
const chapterName = item.title;
|
||||
const releaseDate = dayjs(item.publishedAt).format('LLL');
|
||||
const chapterUrl = baseUrl + item.url;
|
||||
@@ -115,7 +115,7 @@ const searchNovels = async searchTerm => {
|
||||
const body = await result.json();
|
||||
let novels = [];
|
||||
|
||||
body.items.map(item => {
|
||||
body.items.forEach(item => {
|
||||
const novelName = item.title;
|
||||
const novelUrl = baseUrl + '/' + item.slug;
|
||||
const novelCover = item?.verticalImage?.url
|
||||
|
||||
@@ -37,7 +37,7 @@ const popularNovels = async (page, { showLatestNovels, filters }) => {
|
||||
|
||||
let novels = [];
|
||||
|
||||
body.content.map(item => {
|
||||
body.content.forEach(item => {
|
||||
const novelName = item.rus_name;
|
||||
const novelUrl = item.dir;
|
||||
const novelCover =
|
||||
@@ -89,7 +89,7 @@ const parseNovelAndChapters = async novelUrl => {
|
||||
);
|
||||
let volumes = await chapterResult.json();
|
||||
|
||||
volumes.content.map(item => {
|
||||
volumes.content.forEach(item => {
|
||||
const chapterName =
|
||||
`Том ${item.tome} Глава ${item.chapter} ${item.name}`?.trim();
|
||||
const releaseDate = dayjs(item.upload_date).format('LLL');
|
||||
@@ -126,7 +126,7 @@ const searchNovels = async searchTerm => {
|
||||
let body = await result.json();
|
||||
let novels = [];
|
||||
|
||||
body.content.map(item => {
|
||||
body.content.forEach(item => {
|
||||
const novelName = item.rus_name;
|
||||
const novelUrl = item.dir;
|
||||
const novelCover =
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -91,7 +91,6 @@ import RanobeHubScraper from './ru/ranobehub';
|
||||
import RanobeLibScraper from './ru/ranobelib';
|
||||
import RanobeRFScraper from './ru/ranoberf';
|
||||
import RenovelsScraper from './ru/renovels';
|
||||
import RulateScraper from './ru/rulate';
|
||||
import FicbookScraper from './ru/ficbook';
|
||||
import DivineDaoLibraryScraper from './en/divinedaolibrary';
|
||||
import NovelOnlineFullScraper from './en/novelonlinefull';
|
||||
@@ -135,6 +134,26 @@ import {
|
||||
} from './multisrc/wqmangastream/WQMangaStreamGenerator';
|
||||
import AgitoonScraper from './kr/Agitoon';
|
||||
import PawReadScraper from './en/pawread';
|
||||
import {
|
||||
RulateScraper,
|
||||
ErolateScraper,
|
||||
} from './multisrc/rulate/RulateGenerator';
|
||||
import {
|
||||
RuRanobeScraper,
|
||||
UkrRanobeScraper,
|
||||
NightnovelScraper,
|
||||
KgRanobeScraper,
|
||||
AxelScraper,
|
||||
SferdrakonScraper,
|
||||
OriginalScraper,
|
||||
SnailulitkaScraper,
|
||||
TsundokuScraper,
|
||||
JapitScraper,
|
||||
BelScraper,
|
||||
RedScraper,
|
||||
SiScraper,
|
||||
KodScraper,
|
||||
} from './multisrc/noveltl/NovelTlGenerator';
|
||||
|
||||
interface PopularNovelsResponse {
|
||||
novels: SourceNovelItem[];
|
||||
@@ -288,6 +307,21 @@ export const sourceManager = (sourceId: number): Scraper => {
|
||||
145: AgitoonScraper, // @ts-ignore
|
||||
146: PandaMtlScraper, // @ts-ignore
|
||||
147: PawReadScraper, // @ts-ignore
|
||||
148: ErolateScraper, // @ts-ignore
|
||||
149: RuRanobeScraper, // @ts-ignore
|
||||
150: UkrRanobeScraper, // @ts-ignore
|
||||
151: NightnovelScraper, // @ts-ignore
|
||||
152: KgRanobeScraper, // @ts-ignore
|
||||
153: AxelScraper, // @ts-ignore
|
||||
154: SferdrakonScraper, // @ts-ignore
|
||||
155: OriginalScraper, // @ts-ignore
|
||||
156: SnailulitkaScraper, // @ts-ignore
|
||||
157: TsundokuScraper, // @ts-ignore
|
||||
158: JapitScraper, // @ts-ignore
|
||||
159: BelScraper, // @ts-ignore
|
||||
160: RedScraper, // @ts-ignore
|
||||
161: SiScraper, // @ts-ignore
|
||||
162: KodScraper, // @ts-ignore
|
||||
};
|
||||
|
||||
return scrapers[sourceId];
|
||||
|
||||
@@ -880,5 +880,110 @@
|
||||
"icon": "https://www.pawread.com/favicon.ico",
|
||||
"url": "https://pawread.com/",
|
||||
"lang": "English"
|
||||
},
|
||||
{
|
||||
"sourceId": 148,
|
||||
"sourceName": "Erolate",
|
||||
"url": "https://erolate.com",
|
||||
"lang": "Russian",
|
||||
"icon": "https://erolate.com/i/logo/erolate.png"
|
||||
},
|
||||
{
|
||||
"sourceId": 149,
|
||||
"sourceName": "РуРанобэ",
|
||||
"url": "https://ruranobe.ru",
|
||||
"lang": "Russian",
|
||||
"icon": "https://ruranobe.ru/logo.png"
|
||||
},
|
||||
{
|
||||
"sourceId": 150,
|
||||
"sourceName": "Переклади українською мовою",
|
||||
"url": "https://ukr.novel.tl",
|
||||
"lang": "Ukraine",
|
||||
"icon": "https://ukr.novel.tl/logo.png"
|
||||
},
|
||||
{
|
||||
"sourceId": 151,
|
||||
"sourceName": "Nightnovel",
|
||||
"url": "https://nightnovel.online",
|
||||
"lang": "Russian",
|
||||
"icon": "https://nightnovel.online/logo.png"
|
||||
},
|
||||
{
|
||||
"sourceId": 152,
|
||||
"sourceName": "Карманная галактика",
|
||||
"url": "https://kg.novel.tl",
|
||||
"lang": "Russian",
|
||||
"icon": "https://kg.novel.tl/logo.png"
|
||||
},
|
||||
{
|
||||
"sourceId": 153,
|
||||
"sourceName": "Аксел",
|
||||
"url": "https://axel.znovel.space",
|
||||
"lang": "Russian",
|
||||
"icon": "https://axel.znovel.space/logo.png"
|
||||
},
|
||||
{
|
||||
"sourceId": 154,
|
||||
"sourceName": "Уголок дракона",
|
||||
"url": "https://sferdrakon.novel.tl",
|
||||
"lang": "Russian",
|
||||
"icon": "https://sferdrakon.novel.tl/logo.png"
|
||||
},
|
||||
{
|
||||
"sourceId": 155,
|
||||
"sourceName": "Авторские работы",
|
||||
"url": "https://original.novel.tl",
|
||||
"lang": "Russian",
|
||||
"icon": "https://original.novel.tl/logo.png"
|
||||
},
|
||||
{
|
||||
"sourceId": 156,
|
||||
"sourceName": "Snailulitka",
|
||||
"url": "https://snailulitka.novel.tl",
|
||||
"lang": "Russian",
|
||||
"icon": "https://snailulitka.novel.tl/logo.png"
|
||||
},
|
||||
{
|
||||
"sourceId": 157,
|
||||
"sourceName": "TsundokuTrans",
|
||||
"url": "https://tsundoku.novel.tl",
|
||||
"lang": "Russian",
|
||||
"icon": "https://tsundoku.novel.tl/logo.png"
|
||||
},
|
||||
{
|
||||
"sourceId": 158,
|
||||
"sourceName": "Japit Comics",
|
||||
"url": "https://japit.novel.tl",
|
||||
"lang": "Russian",
|
||||
"icon": "https://japit.novel.tl/logo.png"
|
||||
},
|
||||
{
|
||||
"sourceId": 159,
|
||||
"sourceName": "Белманга",
|
||||
"url": "https://bel.novel.tl",
|
||||
"lang": "Belarus",
|
||||
"icon": "https://bel.novel.tl/logo.png"
|
||||
},
|
||||
{
|
||||
"sourceId": 160,
|
||||
"sourceName": "Север и Юг",
|
||||
"url": "https://red.novel.tl",
|
||||
"lang": "Russian",
|
||||
"icon": "https://red.novel.tl/logo.png"
|
||||
},
|
||||
{
|
||||
"sourceId": 161,
|
||||
"sourceName": "Сказочные истории",
|
||||
"url": "https://si.novel.tl",
|
||||
"lang": "Russian",
|
||||
"icon": "https://si.novel.tl/logo.png"
|
||||
},
|
||||
{
|
||||
"sourceId": 162,
|
||||
"sourceName": "K.O.D.",
|
||||
"url": "https://kod.novel.tl",
|
||||
"lang": "Russian",
|
||||
"icon": "https://kod.novel.tl/logo.png"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export const languages = [
|
||||
'Arabic',
|
||||
'Belarus',
|
||||
'Chinese',
|
||||
'English',
|
||||
'French',
|
||||
@@ -10,5 +11,6 @@ export const languages = [
|
||||
'Russian',
|
||||
'Spanish',
|
||||
'Turkish',
|
||||
'Ukraine',
|
||||
'Vietnamese',
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user