From 715549f927c28b43365343f04b5ba96caa9b2431 Mon Sep 17 00:00:00 2001 From: Rider21 <58046032+Rider21@users.noreply.github.com> Date: Sun, 7 Jan 2024 00:37:50 +0300 Subject: [PATCH 01/12] update --- src/sources/ch/linovelib.js | 4 +- .../multisrc/ifreedom/IfreedomGenerator.js | 106 ++++++++++++ .../multisrc/ifreedom/IfreedomScraper.js | 154 ++++++++++++++++++ .../multisrc/readwn/ReadwnGenerator.js | 114 ------------- src/sources/multisrc/readwn/ReadwnScraper.js | 44 ++++- .../multisrc/rulate/RulateGenerator.js | 69 +++++++- src/sources/multisrc/rulate/RulateScraper.js | 7 +- src/sources/ru/freedlit.ts | 37 ++--- src/sources/sourceManager.ts | 6 + src/sources/sources.json | 14 ++ src/sources/ua/smakolykytl.ts | 17 +- 11 files changed, 419 insertions(+), 153 deletions(-) create mode 100644 src/sources/multisrc/ifreedom/IfreedomGenerator.js create mode 100644 src/sources/multisrc/ifreedom/IfreedomScraper.js diff --git a/src/sources/ch/linovelib.js b/src/sources/ch/linovelib.js index 52110450f..ad388ec35 100644 --- a/src/sources/ch/linovelib.js +++ b/src/sources/ch/linovelib.js @@ -74,8 +74,8 @@ const parseNovelAndChapters = async novelUrl => { genres.push(loadedCheerio(this).text()); }); - if (genres && genres.length > 0) { - novel.genres = genres.join(', '); + if (genres.length) { + novel.genre = genres.join(', '); } // Table of Content is on a different page than the summary page diff --git a/src/sources/multisrc/ifreedom/IfreedomGenerator.js b/src/sources/multisrc/ifreedom/IfreedomGenerator.js new file mode 100644 index 000000000..db39a0c95 --- /dev/null +++ b/src/sources/multisrc/ifreedom/IfreedomGenerator.js @@ -0,0 +1,106 @@ +import { FilterInputs } from '../../types/filterTypes'; +import MultiSrcScraper from './IfreedomScraper'; + +const defaultFilter = [ + { + key: 'sort', + inputType: FilterInputs.Picker, + label: 'Сортировка:', + values: [ + { label: 'По дате добавления', value: 'По дате добавления' }, + { label: 'По дате обновления', value: 'По дате обновления' }, + { label: 'По количеству глав', value: 'По количеству глав' }, + { label: 'По названию', value: 'По названию' }, + { label: 'По просмотрам', value: 'По просмотрам' }, + { label: 'По рейтингу', value: 'По рейтингу' }, + ], + }, + { + key: 'status', + inputType: FilterInputs.Checkbox, + label: 'Статус:', + values: [ + { label: 'Перевод активен', value: 'Перевод активен' }, + { label: 'Перевод приостановлен', value: 'Перевод приостановлен' }, + { label: 'Произведение завершено', value: 'Произведение завершено' }, + ], + }, + { + key: 'lang', + inputType: FilterInputs.Checkbox, + label: 'Язык:', + values: [ + { label: 'Английский', value: 'Английский' }, + { label: 'Китайский', value: 'Китайский' }, + { label: 'Корейский', value: 'Корейский' }, + { label: 'Японский', value: 'Японский' }, + ], + }, + { + key: 'genre', + inputType: FilterInputs.Checkbox, + label: 'Жанры:', + values: [ + { label: 'Боевик', value: 'Боевик' }, + { label: 'Боевые Искусства', value: 'Боевые Искусства' }, + { label: 'Вампиры', value: 'Вампиры' }, + { label: 'Виртуальный Мир', value: 'Виртуальный Мир' }, + { label: 'Гарем', value: 'Гарем' }, + { label: 'Героическое фэнтези', value: 'Героическое фэнтези' }, + { label: 'Детектив', value: 'Детектив' }, + { label: 'Дзёсэй', value: 'Дзёсэй' }, + { label: 'Драма', value: 'Драма' }, + { label: 'Игра', value: 'Игра' }, + { label: 'История', value: 'История' }, + { label: 'Киберпанк', value: 'Киберпанк' }, + { label: 'Комедия', value: 'Комедия' }, + { label: 'ЛитРПГ', value: 'ЛитРПГ' }, + { label: 'Меха', value: 'Меха' }, + { label: 'Милитари', value: 'Милитари' }, + { label: 'Мистика', value: 'Мистика' }, + { label: 'Научная Фантастика', value: 'Научная Фантастика' }, + { label: 'Повседневность', value: 'Повседневность' }, + { label: 'Постапокалипсис', value: 'Постапокалипсис' }, + { label: 'Приключения', value: 'Приключения' }, + { label: 'Психология', value: 'Психология' }, + { label: 'Романтика', value: 'Романтика' }, + { label: 'Сверхъестественное', value: 'Сверхъестественное' }, + { label: 'Сёдзё', value: 'Сёдзё' }, + { label: 'Сёнэн', value: 'Сёнэн' }, + { label: 'Сёнэн-ай', value: 'Сёнэн-ай' }, + { label: 'Спорт', value: 'Спорт' }, + { label: 'Сэйнэн', value: 'Сэйнэн' }, + { label: 'Сюаньхуа', value: 'Сюаньхуа' }, + { label: 'Трагедия', value: 'Трагедия' }, + { label: 'Триллер', value: 'Триллер' }, + { label: 'Ужасы', value: 'Ужасы' }, + { label: 'Фантастика', value: 'Фантастика' }, + { label: 'Фэнтези', value: 'Фэнтези' }, + { label: 'Школьная жизнь', value: 'Школьная жизнь' }, + { label: 'Экшн', value: 'Экшн' }, + { label: 'Эротика', value: 'Эротика' }, + { label: 'Этти', value: 'Этти' }, + { label: 'Яой', value: 'Яой' }, + { label: 'Adult', value: 'Adult' }, + { label: 'Mature', value: 'Mature' }, + { label: 'Xianxia', value: 'Xianxia' }, + { label: 'Xuanhuan', value: 'Xuanhuan' }, + ], + }, +]; + +const IfreedomScraper = new MultiSrcScraper( + 171, + 'https://ifreedom.su', + 'Свободный Мир Ранобэ', + defaultFilter, +); + +const BookhamsterScraper = new MultiSrcScraper( + 172, + 'https://bookhamster.ru', + 'Bookhamster', + defaultFilter, +); + +export { IfreedomScraper, BookhamsterScraper }; diff --git a/src/sources/multisrc/ifreedom/IfreedomScraper.js b/src/sources/multisrc/ifreedom/IfreedomScraper.js new file mode 100644 index 000000000..13cfd00ae --- /dev/null +++ b/src/sources/multisrc/ifreedom/IfreedomScraper.js @@ -0,0 +1,154 @@ +import { Status } from '../../helpers/constants'; +import * as cheerio from 'cheerio'; + +class IfreedomScraper { + constructor(sourceId, baseUrl, sourceName, filters) { + this.sourceId = sourceId; + this.baseUrl = baseUrl; + this.sourceName = sourceName; + this.filters = filters; + } + + async popularNovels(page, { showLatestNovels, filters }) { + let url = + this.baseUrl + + '/vse-knigi/?sort=' + + (showLatestNovels + ? 'По дате обновления' + : filters?.sort || 'По рейтингу'); + + if (filters?.status instanceof Array) { + url += filters.status.map(i => '&status[]=' + i).join(''); + } + if (filters?.lang instanceof Array) { + url += filters.lang.map(i => '&lang[]=' + i).join(''); + } + if (filters?.genre instanceof Array) { + url += filters.genre.map(i => '&genre[]=' + i).join(''); + } + url += '&bpage=' + page; + + const body = await fetch(url).then(res => res.text()); + const loadedCheerio = cheerio.load(body); + + const novels = loadedCheerio('div.one-book-home > div.img-home a') + .map((index, element) => ({ + sourceId: this.sourceId, + novelName: loadedCheerio(element).attr('title'), + novelCover: loadedCheerio(element).find('img').attr('src'), + novelUrl: loadedCheerio(element).attr('href'), + })) + .get() + .filter(novel => novel.novelName && novel.novelUrl); + + return { novels }; + } + + async parseNovelAndChapters(novelUrl) { + const body = await fetch(novelUrl).then(res => res.text()); + const loadedCheerio = cheerio.load(body); + + const novel = { + sourceId: this.sourceId, + sourceName: this.sourceName, + url: novelUrl, + novelUrl, + novelName: loadedCheerio('.entry-title').text(), + novelCover: loadedCheerio('.img-ranobe > img').attr('src'), + summary: loadedCheerio('meta[name="description"]').attr('content'), + }; + + loadedCheerio('div.data-ranobe').each(function () { + switch (loadedCheerio(this).find('b').text()) { + case 'Автор': + novel.author = loadedCheerio(this) + .find('div.data-value') + .text() + .trim(); + break; + case 'Жанры': + novel.genre = loadedCheerio('div.data-value > a') + .map((index, element) => loadedCheerio(element).text()?.trim()) + .get() + .join(','); + break; + case 'Статус книги': + novel.status = loadedCheerio('div.data-value') + .text() + .includes('активен') + ? Status.ONGOING + : Status.COMPLETED; + break; + } + }); + + if (novel.author == 'Не указан') delete novel.author; + + const chapters = []; + loadedCheerio('div.li-ranobe').each(function () { + const chapterName = loadedCheerio(this).find('a').text(); + const chapterUrl = loadedCheerio(this).find('a').attr('href'); + if ( + !loadedCheerio(this).find('label.buy-ranobe').length && + chapterName && + chapterUrl + ) { + const releaseDate = loadedCheerio(this) + .find('div.li-col2-ranobe') + .text() + .trim(); + + chapters.push({ chapterName, releaseDate, chapterUrl }); + } + }); + + novel.chapters = chapters.reverse(); + return novel; + } + + async parseChapter(novelUrl, chapterUrl) { + const body = await fetch(chapterUrl).then(res => res.text()); + const loadedCheerio = cheerio.load(body); + + loadedCheerio('.entry-content img').each(function () { + const srcset = loadedCheerio(this).attr('srcset')?.split?.(' '); + if (srcset?.length) { + const bestlink = srcset + .filter(url => url.startsWith('http')) + ?.unshift(); + if (bestlink) loadedCheerio(this).attr('src', bestlink); + } + }); + + const chapter = { + sourceId: this.sourceId, + novelUrl, + chapterUrl, + chapterName: loadedCheerio('.entry-title').text(), + chapterText: loadedCheerio('.entry-content').html(), + }; + + return chapter; + } + + async searchNovels(searchTerm) { + const url = + this.baseUrl + '/vse-knigi/?searchname=' + searchTerm + '&bpage=' + page; + const result = await fetch(url).then(res => res.text()); + const loadedCheerio = cheerio.load(result); + + const novels = loadedCheerio('div.one-book-home > div.img-home a') + .map((index, element) => ({ + sourceId: this.sourceId, + novelName: loadedCheerio(element).attr('title'), + novelCover: loadedCheerio(element).find('img').attr('src'), + novelUrl: loadedCheerio(element).attr('href'), + })) + .get() + .filter(novel => novel.novelName && novel.novelUrl); + + return novels; + } +} + +export default IfreedomScraper; diff --git a/src/sources/multisrc/readwn/ReadwnGenerator.js b/src/sources/multisrc/readwn/ReadwnGenerator.js index 029e0663c..6a4dd757f 100644 --- a/src/sources/multisrc/readwn/ReadwnGenerator.js +++ b/src/sources/multisrc/readwn/ReadwnGenerator.js @@ -7,64 +7,26 @@ const ReadwnScraper = new MultiSrcScraper( { label: 'Genre / Category', values: [ - { label: 'All', value: 'all' }, - { label: 'Action', value: 'action' }, - { label: 'Adventure', value: 'adventure' }, { label: 'Chinese', value: 'chinese' }, - { label: 'Comedy', value: 'comedy' }, - { label: 'Contemporary Romance', value: 'contemporary-romance' }, - { label: 'Drama', value: 'drama' }, - { label: 'Eastern Fantasy', value: 'eastern-fantasy' }, - { label: 'Ecchi', value: 'ecchi' }, { label: 'Erciyuan', value: 'erciyuan' }, { label: 'Faloo', value: 'faloo' }, { label: 'Fan-Fiction', value: 'fan-fiction' }, - { label: 'Fantasy', value: 'fantasy' }, - { label: 'Fantasy Romance', value: 'fantasy-romance' }, - { label: 'Game', value: 'game' }, - { label: 'Gender Bender', value: 'gender-bender' }, - { label: 'Harem', value: 'harem' }, { label: 'Hentai', value: 'hentai' }, - { label: 'Historical', value: 'historical' }, - { label: 'Horror', value: 'horror' }, { label: 'Isekai', value: 'isekai' }, { label: 'Japanese', value: 'japanese' }, - { label: 'Josei', value: 'josei' }, { label: 'Korean', value: 'korean' }, - { label: 'Lolicon', value: 'lolicon' }, { label: 'Magic', value: 'magic' }, - { label: 'Magical Realism', value: 'magical-realism' }, - { label: 'Martial Arts', value: 'martial-arts' }, - { label: 'Mecha', value: 'mecha' }, { label: 'Military', value: 'military' }, - { label: 'Mystery', value: 'mystery' }, { label: 'Official Circles', value: 'official_circles' }, - { label: 'Psychological', value: 'psychological' }, - { label: 'Romance', value: 'romance' }, - { label: 'School Life', value: 'school-life' }, - { label: 'Sci-fi', value: 'sci-fi' }, { label: 'Science Fiction', value: 'science_fiction' }, - { label: 'Seinen', value: 'seinen' }, - { label: 'Shoujo', value: 'shoujo' }, { label: 'Shoujo Ai', value: 'shoujo-ai' }, - { label: 'Shounen', value: 'shounen' }, - { label: 'Shounen Ai', value: 'shounen-ai' }, - { label: 'Slice of Life', value: 'slice-of-life' }, - { label: 'Sports', value: 'sports' }, - { label: 'Supernatural', value: 'supernatural' }, { label: 'Suspense Thriller', value: 'suspense_thriller' }, - { label: 'Tragedy', value: 'tragedy' }, { label: 'Travel Through Time', value: 'travel_through_time' }, { label: 'Two-dimensional', value: 'two-dimensional' }, { label: 'Urban', value: 'urban' }, { label: 'Urban Life', value: 'urban-life' }, - { label: 'Video Games', value: 'video-games' }, { label: 'Virtual Reality', value: 'virtual-reality' }, - { label: 'Wuxia', value: 'wuxia' }, { label: 'Wuxia Xianxia', value: 'wuxia_xianxia' }, - { label: 'Xianxia', value: 'xianxia' }, - { label: 'Xuanhuan', value: 'xuanhuan' }, - { label: 'Yaoi', value: 'yaoi' }, { label: 'Yuri', value: 'yuri' }, ], }, @@ -77,70 +39,31 @@ const NovelmtScraper = new MultiSrcScraper( { label: 'Genre / Category', values: [ - { label: 'All', value: 'all' }, - { label: 'Action', value: 'action' }, - { label: 'Adult', value: 'adult' }, - { label: 'Adventure', value: 'adventure' }, { label: 'Billionaire', value: 'billionaire' }, { label: 'CEO', value: 'ceo' }, { label: 'Chinese', value: 'chinese' }, - { label: 'Comedy', value: 'comedy' }, - { label: 'Contemporary Romance', value: 'contemporary-romance' }, - { label: 'Drama', value: 'drama' }, - { label: 'Eastern Fantasy', value: 'eastern-fantasy' }, { label: 'Ecchi', value: 'ecchi' }, { label: 'Erciyuan', value: 'erciyuan' }, { label: 'Faloo', value: 'faloo' }, { label: 'Fan-Fiction', value: 'fan-fiction' }, - { label: 'Fantasy', value: 'fantasy' }, - { label: 'Fantasy Romance', value: 'fantasy-romance' }, { label: 'Farming', value: 'farming' }, - { label: 'Game', value: 'game' }, { label: 'Games', value: 'games' }, { label: 'Gay Romance', value: 'gay-romance' }, - { label: 'Gender Bender', value: 'gender-bender' }, - { label: 'Harem', value: 'harem' }, - { label: 'Historical', value: 'historical' }, { label: 'Historical Romance', value: 'historical-romance' }, - { label: 'Horror', value: 'horror' }, { label: 'Isekai', value: 'isekai' }, { label: 'Japanese', value: 'japanese' }, - { label: 'Josei', value: 'josei' }, { label: 'Korean', value: 'korean' }, - { label: 'Lolicon', value: 'lolicon' }, { label: 'Magic', value: 'magic' }, - { label: 'Magical Realism', value: 'magical-realism' }, - { label: 'Martial Arts', value: 'martial-arts' }, - { label: 'Mature', value: 'mature' }, - { label: 'Mecha', value: 'mecha' }, { label: 'Military', value: 'military' }, { label: 'Modern Life', value: 'modern-life' }, { label: 'Modern Romance', value: 'modern-romance' }, - { label: 'Mystery', value: 'mystery' }, - { label: 'Psychological', value: 'psychological' }, - { label: 'Romance', value: 'romance' }, { label: 'Romantic', value: 'romantic' }, - { label: 'School Life', value: 'school-life' }, - { label: 'Sci-fi', value: 'sci-fi' }, - { label: 'Seinen', value: 'seinen' }, - { label: 'Shoujo', value: 'shoujo' }, { label: 'Shoujo Ai', value: 'shoujo-ai' }, - { label: 'Shounen', value: 'shounen' }, - { label: 'Shounen Ai', value: 'shounen-ai' }, - { label: 'Slice of Life', value: 'slice-of-life' }, { label: 'Smut', value: 'smut' }, - { label: 'Sports', value: 'sports' }, - { label: 'Supernatural', value: 'supernatural' }, - { label: 'Tragedy', value: 'tragedy' }, { label: 'Two-dimensional', value: 'two-dimensional' }, { label: 'Urban', value: 'urban' }, { label: 'Urban Life', value: 'urban-life' }, - { label: 'Video Games', value: 'video-games' }, { label: 'Virtual Reality', value: 'virtual-reality' }, - { label: 'Wuxia', value: 'wuxia' }, - { label: 'Xianxia', value: 'xianxia' }, - { label: 'Xuanhuan', value: 'xuanhuan' }, - { label: 'Yaoi', value: 'yaoi' }, { label: 'Yuri', value: 'yuri' }, ], }, @@ -153,47 +76,10 @@ const LtnovelScraper = new MultiSrcScraper( { label: 'Genre / Category', values: [ - { label: 'All', value: 'all' }, - { label: 'Action', value: 'action' }, { label: 'Adult', value: 'adult' }, - { label: 'Adventure', value: 'adventure' }, - { label: 'Comedy', value: 'comedy' }, - { label: 'Contemporary Romance', value: 'contemporary-romance' }, - { label: 'Drama', value: 'drama' }, - { label: 'Eastern Fantasy', value: 'eastern-fantasy' }, { label: 'Ecchi', value: 'ecchi' }, - { label: 'Fantasy', value: 'fantasy' }, - { label: 'Fantasy Romance', value: 'fantasy-romance' }, - { label: 'Game', value: 'game' }, - { label: 'Gender Bender', value: 'gender-bender' }, - { label: 'Harem', value: 'harem' }, - { label: 'Historical', value: 'historical' }, - { label: 'Horror', value: 'horror' }, - { label: 'Josei', value: 'josei' }, - { label: 'Lolicon', value: 'lolicon' }, - { label: 'Magical Realism', value: 'magical-realism' }, - { label: 'Martial Arts', value: 'martial-arts' }, { label: 'Mature', value: 'mature' }, - { label: 'Mecha', value: 'mecha' }, - { label: 'Mystery', value: 'mystery' }, - { label: 'Psychological', value: 'psychological' }, - { label: 'Romance', value: 'romance' }, - { label: 'School Life', value: 'school-life' }, - { label: 'Sci-fi', value: 'sci-fi' }, - { label: 'Seinen', value: 'seinen' }, - { label: 'Shoujo', value: 'shoujo' }, - { label: 'Shounen', value: 'shounen' }, - { label: 'Shounen Ai', value: 'shounen-ai' }, - { label: 'Slice of Life', value: 'slice-of-life' }, { label: 'Smut', value: 'smut' }, - { label: 'Sports', value: 'sports' }, - { label: 'Supernatural', value: 'supernatural' }, - { label: 'Tragedy', value: 'tragedy' }, - { label: 'Video Games', value: 'video-games' }, - { label: 'Wuxia', value: 'wuxia' }, - { label: 'Xianxia', value: 'xianxia' }, - { label: 'Xuanhuan', value: 'xuanhuan' }, - { label: 'Yaoi', value: 'yaoi' }, ], }, ); diff --git a/src/sources/multisrc/readwn/ReadwnScraper.js b/src/sources/multisrc/readwn/ReadwnScraper.js index c933cc7de..1b0b56de8 100644 --- a/src/sources/multisrc/readwn/ReadwnScraper.js +++ b/src/sources/multisrc/readwn/ReadwnScraper.js @@ -33,9 +33,47 @@ class ReadwnScraper { }, { key: 'genres', - label: genres.label, - values: genres.values, - inputType: FilterInputs.Picker, + label: 'Genre / Category', + values: [ + { label: 'All', value: 'all' }, + { label: 'Action', value: 'action' }, + { label: 'Adventure', value: 'adventure' }, + { label: 'Comedy', value: 'comedy' }, + { label: 'Contemporary Romance', value: 'contemporary-romance' }, + { label: 'Drama', value: 'drama' }, + { label: 'Eastern Fantasy', value: 'eastern-fantasy' }, + { label: 'Fantasy', value: 'fantasy' }, + { label: 'Fantasy Romance', value: 'fantasy-romance' }, + { label: 'Game', value: 'game' }, + { label: 'Gender Bender', value: 'gender-bender' }, + { label: 'Harem', value: 'harem' }, + { label: 'Historical', value: 'historical' }, + { label: 'Horror', value: 'horror' }, + { label: 'Josei', value: 'josei' }, + { label: 'Lolicon', value: 'lolicon' }, + { label: 'Magical Realism', value: 'magical-realism' }, + { label: 'Martial Arts', value: 'martial-arts' }, + { label: 'Mecha', value: 'mecha' }, + { label: 'Mystery', value: 'mystery' }, + { label: 'Psychological', value: 'psychological' }, + { label: 'Romance', value: 'romance' }, + { label: 'School Life', value: 'school-life' }, + { label: 'Sci-fi', value: 'sci-fi' }, + { label: 'Seinen', value: 'seinen' }, + { label: 'Shoujo', value: 'shoujo' }, + { label: 'Shounen', value: 'shounen' }, + { label: 'Shounen Ai', value: 'shounen-ai' }, + { label: 'Slice of Life', value: 'slice-of-life' }, + { label: 'Sports', value: 'sports' }, + { label: 'Supernatural', value: 'supernatural' }, + { label: 'Tragedy', value: 'tragedy' }, + { label: 'Video Games', value: 'video-games' }, + { label: 'Wuxia', value: 'wuxia' }, + { label: 'Xianxia', value: 'xianxia' }, + { label: 'Xuanhuan', value: 'xuanhuan' }, + { label: 'Yaoi', value: 'yaoi' }, + ...genres.values, + ], }, ]; } diff --git a/src/sources/multisrc/rulate/RulateGenerator.js b/src/sources/multisrc/rulate/RulateGenerator.js index af60f7177..43d66a74f 100644 --- a/src/sources/multisrc/rulate/RulateGenerator.js +++ b/src/sources/multisrc/rulate/RulateGenerator.js @@ -54,8 +54,6 @@ const RulateScraper = new MultiSrcScraper( { label: 'Фэнтези', value: '37' }, { label: 'Школа', value: '38' }, { label: 'Этти', value: '39' }, - { label: 'Bl', value: '43' }, - { label: 'Gl', value: '40' }, ], inputType: FilterInputs.Checkbox, }, @@ -311,7 +309,6 @@ const RulateScraper = new MultiSrcScraper( { label: 'Горничные', value: '8091' }, { label: 'Гробница', value: '388' }, { label: 'Дазай осаму', value: '4112' }, - { label: 'Даньмэй', value: '3411' }, { label: 'Даосизм', value: '258' }, { label: 'Дарк соулс', value: '6057' }, { label: 'Дарт вейдер', value: '6826' }, @@ -383,6 +380,7 @@ const RulateScraper = new MultiSrcScraper( { label: 'Жестокость', value: '7880' }, { label: 'Животные', value: '7488' }, { label: 'Животные компаньоны', value: '2061' }, + { label: 'Жизнь в небесах', value: '8426' }, { label: 'Жизнь и смерть', value: '2014' }, { label: 'Жнец', value: '6288' }, { label: 'Жойен рид', value: '3926' }, @@ -420,6 +418,7 @@ const RulateScraper = new MultiSrcScraper( { label: 'Игра на выживание', value: '2182' }, { label: 'Игровая система', value: '384' }, { label: 'Игровые элементы', value: '720' }, + { label: 'Избранный', value: '8425' }, { label: 'Извращения', value: '651' }, { label: 'Измена', value: '1874' }, { label: 'Изменение характера', value: '1113' }, @@ -1061,6 +1060,7 @@ const RulateScraper = new MultiSrcScraper( { label: 'Церковь', value: '1980' }, { label: 'Цундере', value: '1078' }, { label: 'Чакра', value: '1393' }, + { label: 'Чат-система', value: '8407' }, { label: 'Черный юмор', value: '2868' }, { label: 'Честная главная героиня', value: '5855' }, { label: 'Честный главный герой', value: '129' }, @@ -1083,6 +1083,7 @@ const RulateScraper = new MultiSrcScraper( { label: 'Элементальная магия', value: '1264' }, { label: 'Эльфы', value: '1362' }, { label: 'Эротика', value: '2053' }, + { label: 'Это история без главной героини', value: '8417' }, { label: 'Юмор', value: '7360' }, { label: 'Яды', value: '201' }, { label: 'Якудза', value: '4599' }, @@ -1161,21 +1162,26 @@ const ErolateScraper = new MultiSrcScraper( { label: 'Айдолы', value: '364' }, { label: 'Акула', value: '53' }, { label: 'Альтернативное развитие событий', value: '338' }, + { label: 'Альфа самец', value: '453' }, { label: 'Аморальный главный герой', value: '219' }, { label: 'Анал', value: '7' }, { label: 'Анальный секс', value: '67' }, { label: 'Ангелы', value: '114' }, { label: 'Антигерой', value: '226' }, { label: 'Аристократия', value: '260' }, + { label: 'Армия', value: '415' }, { label: 'Артефакт', value: '379' }, { label: 'Афродизиак', value: '276' }, { label: 'Ахегао', value: '240' }, { label: 'Бабушка беременна от внука', value: '393' }, { label: 'Бабушка и внук', value: '380' }, + { label: 'Бандиты', value: '441' }, + { label: 'Бдс', value: '420' }, { label: 'Бдсм', value: '131' }, { label: 'Беременность', value: '148' }, { label: 'Бесплатно', value: '79' }, { label: 'Бесстрашные персонажи', value: '339' }, + { label: 'Бесстыдный главный герой', value: '433' }, { label: 'Библиотека', value: '265' }, { label: 'Бистиалити', value: '391' }, { label: 'Битва за трон', value: '404' }, @@ -1184,6 +1190,7 @@ const ErolateScraper = new MultiSrcScraper( { label: 'Богатые персонажи', value: '248' }, { label: 'Боги', value: '115' }, { label: 'Боевик', value: '252' }, + { label: 'Боевые искусства', value: '430' }, { label: 'Большая грудь', value: '33' }, { label: 'Большая попка', value: '328' }, { label: 'Большой член', value: '76' }, @@ -1193,7 +1200,9 @@ const ErolateScraper = new MultiSrcScraper( { label: 'Брак по расчёту', value: '346' }, { label: 'Брат', value: '29' }, { label: 'Брат и сестра', value: '30' }, + { label: 'Братский комплекс', value: '466' }, { label: 'Брюнетка', value: '28' }, + { label: 'Будущее', value: '457' }, { label: 'Бэтмен', value: '369' }, { label: 'Бэтмен', value: '170' }, { label: 'В первый раз', value: '179' }, @@ -1208,7 +1217,9 @@ const ErolateScraper = new MultiSrcScraper( { label: 'Виртуальная реальность', value: '382' }, { label: 'Вирус', value: '203' }, { label: 'Внучка', value: '189' }, + { label: 'Возвращение домой', value: '448' }, { label: 'Война', value: '387' }, + { label: 'Волшебство', value: '438' }, { label: 'Воспоминания из прошлого', value: '405' }, { label: 'Враги становятся любовниками', value: '410' }, { label: 'Время', value: '81' }, @@ -1220,7 +1231,9 @@ const ErolateScraper = new MultiSrcScraper( { label: 'Гвен', value: '402' }, { label: 'Гг имба', value: '87' }, { label: 'Генетические модификации', value: '314' }, + { label: 'Гениальный главный герой', value: '471' }, { label: 'Гипноз', value: '143' }, + { label: 'Гл', value: '427' }, { label: 'Главная героиня девушка', value: '147' }, { label: 'Главный герой женщина', value: '213' }, { label: 'Главный герой извращенец', value: '116' }, @@ -1269,6 +1282,7 @@ const ErolateScraper = new MultiSrcScraper( { label: 'Замок', value: '140' }, { label: 'Запретная любовь', value: '273' }, { label: 'Заражение', value: '206' }, + { label: 'Зачарованные', value: '467' }, { label: 'Звёздные войны', value: '120' }, { label: 'Зверодевочки', value: '158' }, { label: 'Зло', value: '343' }, @@ -1276,6 +1290,7 @@ const ErolateScraper = new MultiSrcScraper( { label: 'Золотой дождь', value: '146' }, { label: 'Зомби апокалипсис', value: '241' }, { label: 'Зоофилия', value: '144' }, + { label: 'Зрелы', value: '419' }, { label: 'Зрелые женщины', value: '398' }, { label: 'Игровые элементы', value: '385' }, { label: 'Извращения', value: '192' }, @@ -1284,26 +1299,35 @@ const ErolateScraper = new MultiSrcScraper( { label: 'Изменения личности', value: '316' }, { label: 'Изнасилование', value: '275' }, { label: 'Изуку мидория', value: '337' }, + { label: 'Инвалидность', value: '458' }, { label: 'Ино', value: '127' }, { label: 'Инопланетяне', value: '304' }, { label: 'Интересный сюжет', value: '349' }, { label: 'Интимные сцены', value: '322' }, + { label: 'Интрига', value: '435' }, { label: 'Интриги и заговоры', value: '262' }, { label: 'Интроверт', value: '38' }, { label: 'Инфекция', value: '207' }, { label: 'Инцест', value: '35' }, { label: 'Исторический роман', value: '406' }, { label: 'Камера', value: '92' }, + { label: 'Киберспорт', value: '426' }, + { label: 'Китай', value: '445' }, { label: 'Колледж', value: '82' }, + { label: 'Кольца', value: '455' }, { label: 'Комикс', value: '286' }, { label: 'Контроль', value: '19' }, { label: 'Контроль над разумом', value: '181' }, { label: 'Контроль разума', value: '65' }, { label: 'Кончил внутрь', value: '221' }, + { label: 'Корея', value: '446' }, + { label: 'Кормление грудью', value: '440' }, { label: 'Королевская семья', value: '358' }, + { label: 'Королевство', value: '465' }, { label: 'Коррупция', value: '49' }, { label: 'Космос', value: '204' }, { label: 'Красивая главная героиня', value: '255' }, + { label: 'Красивы', value: '462' }, { label: 'Красивые женщины', value: '321' }, { label: 'Красивый главный герой', value: '266' }, { label: 'Ксенофилия', value: '386' }, @@ -1316,9 +1340,13 @@ const ErolateScraper = new MultiSrcScraper( { label: 'Лишение девственности', value: '212' }, { label: 'Лоли', value: '218' }, { label: 'Лунатизм', value: '279' }, + { label: 'Любовный треугольник', value: '473' }, { label: 'Любовь', value: '89' }, + { label: 'Любовь с первого взгляда', value: '436' }, + { label: 'Магические предметы', value: '454' }, { label: 'Магический мир', value: '357' }, { label: 'Магия', value: '80' }, + { label: 'Магия природы', value: '437' }, { label: 'Мама', value: '3' }, { label: 'Мама беременна от сына', value: '274' }, { label: 'Мама и дочь', value: '163' }, @@ -1337,6 +1365,7 @@ const ErolateScraper = new MultiSrcScraper( { label: 'Мачеха', value: '197' }, { label: 'Мачеха беременна от сына', value: '395' }, { label: 'Мачеха и сын', value: '394' }, + { label: 'Медленная романтика', value: '431' }, { label: 'Медсестра', value: '341' }, { label: 'Межрассовый', value: '54' }, { label: 'Месть', value: '306' }, @@ -1349,8 +1378,10 @@ const ErolateScraper = new MultiSrcScraper( { label: 'Младшая сестра', value: '154' }, { label: 'Много спермы', value: '409' }, { label: 'Модель', value: '324' }, + { label: 'Монахиня', value: '470' }, { label: 'Монстры', value: '333' }, { label: 'Мошеничество', value: '44' }, + { label: 'Муж куколд', value: '452' }, { label: 'Мужская беременность', value: '363' }, { label: 'Мужчина протагонист', value: '108' }, { label: 'Музыка', value: '331' }, @@ -1360,6 +1391,7 @@ const ErolateScraper = new MultiSrcScraper( { label: 'Насилие', value: '128' }, { label: 'Насилие и жестокость', value: '193' }, { label: 'Научный эксперимент', value: '313' }, + { label: 'Невеста', value: '421' }, { label: 'Нежить', value: '373' }, { label: 'Некромантия', value: '298' }, { label: 'Ненормативная лексика', value: '124' }, @@ -1367,6 +1399,7 @@ const ErolateScraper = new MultiSrcScraper( { label: 'Нетори', value: '284' }, { label: 'Нижнее бельё', value: '392' }, { label: 'Ниндзя', value: '296' }, + { label: 'Новый год', value: '459' }, { label: 'Ношеные трусики', value: '166' }, { label: 'Нудизм', value: '156' }, { label: 'Няня', value: '88' }, @@ -1379,11 +1412,13 @@ const ErolateScraper = new MultiSrcScraper( { label: 'Оральный секс', value: '10' }, { label: 'Оргия', value: '356' }, { label: 'Орки', value: '388' }, + { label: 'От бедности к богатству', value: '444' }, { label: 'От слабого к сильному', value: '351' }, { label: 'Отец', value: '2' }, { label: 'Отец делится с сыном', value: '6' }, { label: 'Отец и дочь', value: '205' }, { label: 'Отец куколд', value: '272' }, + { label: 'Отчим', value: '417' }, { label: 'Офис', value: '234' }, { label: 'Падчерица', value: '271' }, { label: 'Пайзури', value: '195' }, @@ -1391,6 +1426,7 @@ const ErolateScraper = new MultiSrcScraper( { label: 'Паразиты', value: '208' }, { label: 'Параллельный мир', value: '320' }, { label: 'Пародия', value: '371' }, + { label: 'Первая любовь', value: '434' }, { label: 'Первый раз', value: '159' }, { label: 'Перемещение в другой мир', value: '347' }, { label: 'Перемещение во времени', value: '245' }, @@ -1417,8 +1453,11 @@ const ErolateScraper = new MultiSrcScraper( { label: 'Потеря девственности', value: '142' }, { label: 'Похищение', value: '161' }, { label: 'Преданные любовный интерес', value: '407' }, + { label: 'Призраки', value: '412' }, { label: 'Приключения', value: '134' }, { label: 'Принуждение', value: '66' }, + { label: 'Проклятие', value: '456' }, + { label: 'Психические расстройства', value: '463' }, { label: 'Психология', value: '238' }, { label: 'Публично', value: '85' }, { label: 'Публичный секс', value: '233' }, @@ -1428,11 +1467,15 @@ const ErolateScraper = new MultiSrcScraper( { label: 'Разврат', value: '64' }, { label: 'Райзен', value: '95' }, { label: 'Раса инопланетных космических лесбиянок', value: '294' }, + { label: 'Ревность', value: '414' }, + { label: 'Реинкарнация', value: '428' }, { label: 'Религия', value: '403' }, { label: 'Рестленг', value: '84' }, { label: 'Риас гремори', value: '389' }, + { label: 'Рождество', value: '460' }, { label: 'Романтика', value: '178' }, { label: 'Рыжий', value: '90' }, + { label: 'С', value: '413' }, { label: 'Сакура', value: '126' }, { label: 'Самолет', value: '93' }, { label: 'Санса старк', value: '277' }, @@ -1454,6 +1497,7 @@ const ErolateScraper = new MultiSrcScraper( { label: 'Секса будет много', value: '151' }, { label: 'Сексуальное желание', value: '209' }, { label: 'Селфцест', value: '397' }, + { label: 'Семейные традиции', value: '449' }, { label: 'Семья', value: '5' }, { label: 'Сестра', value: '31' }, { label: 'Сестра беременна от брата', value: '355' }, @@ -1474,14 +1518,24 @@ const ErolateScraper = new MultiSrcScraper( { label: 'Соседка', value: '312' }, { label: 'Сперма', value: '229' }, { label: 'Сперма на лицо', value: '199' }, + { label: 'Спорт', value: '450' }, { label: 'Спящие', value: '187' }, + { label: 'Средневековье', value: '443' }, + { label: 'Сталкер', value: '418' }, { label: 'Старшая сестра', value: '311' }, { label: 'Стеб', value: '111' }, { label: 'Студенты', value: '177' }, { label: 'Суккуб', value: '390' }, { label: 'Супергерои', value: '305' }, { label: 'Суперспособности', value: '300' }, + { label: 'Счастливый конец', value: '461' }, { label: 'Сын', value: '4' }, + { label: 'Сын солдат', value: '416' }, + { label: 'Сюаньхуа', value: '425' }, + { label: 'Сянься', value: '424' }, + { label: 'Таблетки для развития', value: '429' }, + { label: 'Тайная любовь', value: '432' }, + { label: 'Тайные отношения', value: '451' }, { label: 'Твинцест', value: '188' }, { label: 'Темное фэнтези', value: '293' }, { label: 'Тентакли', value: '303' }, @@ -1505,6 +1559,8 @@ const ErolateScraper = new MultiSrcScraper( { label: 'Умные персонажи', value: '257' }, { label: 'Умный главный герой', value: '354' }, { label: 'Университет', value: '323' }, + { label: 'Уся', value: '423' }, + { label: 'Учеба в университете', value: '472' }, { label: 'Ученик', value: '83' }, { label: 'Ф', value: '149' }, { label: 'Фанатичная любовь', value: '366' }, @@ -1516,16 +1572,22 @@ const ErolateScraper = new MultiSrcScraper( { label: 'Фетиш', value: '60' }, { label: 'Флэш', value: '121' }, { label: 'Футанария', value: '150' }, + { label: 'Фэн', value: '422' }, { label: 'Фэнтези', value: '359' }, + { label: 'Фэнтезийный мир', value: '464' }, { label: 'Хентай', value: '75' }, { label: 'Хината', value: '110' }, { label: 'Холодная главная героиня', value: '263' }, + { label: 'Христианство', value: '468' }, + { label: 'Хулиганы', value: '442' }, + { label: 'Церковь', value: '469' }, { label: 'Черная вдова', value: '220' }, { label: 'Черная Вдова', value: '175' }, { label: 'Черный юмор', value: '374' }, { label: 'Читерство', value: '45' }, { label: 'Чтение мыслей', value: '227' }, { label: 'Чудовища', value: '378' }, + { label: 'Чулки', value: '439' }, { label: 'Шантаж', value: '129' }, { label: 'Школа', value: '118' }, { label: 'Школьная жизнь', value: '318' }, @@ -1540,6 +1602,7 @@ const ErolateScraper = new MultiSrcScraper( { label: 'Этти', value: '327' }, { label: 'Юмор', value: '105' }, { label: 'Яндере', value: '155' }, + { label: 'Япония', value: '447' }, { label: 'Bl', value: '125' }, { label: 'Dc', value: '411' }, { label: 'Gl', value: '96' }, diff --git a/src/sources/multisrc/rulate/RulateScraper.js b/src/sources/multisrc/rulate/RulateScraper.js index 300ee7d3c..189b02d92 100644 --- a/src/sources/multisrc/rulate/RulateScraper.js +++ b/src/sources/multisrc/rulate/RulateScraper.js @@ -150,6 +150,9 @@ class RulateScraper { ) .text() .trim(); + if (novel.novelName?.includes?.('[')) { + novel.novelName = novel.novelName.split('[')[0].trim(); + } novel.novelCover = this.baseUrl + loadedCheerio('div[class="images"] > div img').attr('src'); novel.summary = loadedCheerio( @@ -191,7 +194,7 @@ class RulateScraper { }, ); - if (genre.length > 0) { + if (genre.length) { novel.genre = genre.reverse().join(','); } @@ -210,7 +213,7 @@ class RulateScraper { .attr('href'); if ( - loadedCheerio(this).find('td > span[class="disabled"]').length < 1 && + !loadedCheerio(this).find('td > span[class="disabled"]').length && releaseDate ) { chapters.push({ chapterName, releaseDate, chapterUrl }); diff --git a/src/sources/ru/freedlit.ts b/src/sources/ru/freedlit.ts index 6dcf71ec2..4a9c9e0f3 100644 --- a/src/sources/ru/freedlit.ts +++ b/src/sources/ru/freedlit.ts @@ -13,17 +13,14 @@ const sourceName = 'LitSpace'; const baseUrl = 'https://freedlit.space'; const popularNovels = async (page, { showLatestNovels, filters }) => { - let url = baseUrl + '/books/'; - url += (filters?.genre || 'all') + '?sort='; + let url = baseUrl + '/get-books/all/list/' + page + '?sort='; url += showLatestNovels ? 'recent' : filters?.sort || 'popular'; url += '&status=' + (filters?.status || 'all'); url += '&access=' + (filters?.access || 'all'); url += '&adult=' + (filters?.adult || 'hide'); - url += '&page=' + page; - const result = await fetch(url); - const body = await result.text(); - const loadedCheerio = cheerio.load(body); + const result = await fetch(url).then(res => res.text()); + const loadedCheerio = cheerio.load(result); const novels: SourceNovelItem[] = []; loadedCheerio('#bookListBlock > div > div').each(function () { @@ -44,31 +41,30 @@ const popularNovels = async (page, { showLatestNovels, filters }) => { }; const parseNovelAndChapters = async novelUrl => { - const result = await fetch(novelUrl); - const body = await result.text(); - const loadedCheerio = cheerio.load(body); + const result = await fetch(novelUrl).then(res => res.text()); + const loadedCheerio = cheerio.load(result); const novel: SourceNovel = { sourceId, sourceName, novelUrl, url: novelUrl, - novelName: loadedCheerio('.book-info > h4').text(), + novelName: loadedCheerio('.book-info h4').text(), novelCover: loadedCheerio('.book-cover > div > img').attr('src')?.trim(), summary: loadedCheerio('#nav-home').text()?.trim(), - author: loadedCheerio('.book-info > h5 > a').text(), + author: loadedCheerio('.book-info h5 > a').text(), genre: loadedCheerio('.genre-list > a') .map((index, element) => loadedCheerio(element).text()) .get() .join(','), }; - let chapters: SourceChapterItem[] = []; + const chapters: SourceChapterItem[] = []; - loadedCheerio('#nav-contents > div').each(function () { - const chapterName = loadedCheerio(this).find('a').text(); + loadedCheerio('a.chapter-line').each(function () { + const chapterName = loadedCheerio(this).find('h6').text(); const releaseDate = loadedCheerio(this).find('span[class="date"]').text(); - const chapterUrl = loadedCheerio(this).find('a').attr('href'); + const chapterUrl = loadedCheerio(this).attr('href'); if (chapterName && chapterUrl) { chapters.push({ chapterName, releaseDate, chapterUrl }); } @@ -83,8 +79,8 @@ const parseChapter = async (novelUrl, chapterUrl) => { const body = await result.text(); const loadedCheerio = cheerio.load(body); - loadedCheerio('div[class="standart-block"]').remove(); - loadedCheerio('div[class="mobile-block"]').remove(); + loadedCheerio('div.mobile-block').remove(); + loadedCheerio('div.standart-block').remove(); const chapter: SourceChapter = { sourceId, @@ -99,11 +95,10 @@ const parseChapter = async (novelUrl, chapterUrl) => { const searchNovels = async searchTerm => { const url = `${baseUrl}/search?query=${searchTerm}&type=all`; - const result = await fetch(url); - const body = await result.text(); - const loadedCheerio = cheerio.load(body); + const result = await fetch(url).then(res => res.text()); + const loadedCheerio = cheerio.load(result); - let novels: Novel.Item[] = []; + const novels: Novel.Item[] = []; loadedCheerio('#bookListBlock > div').each(function () { const novelName = loadedCheerio(this).find('h4 > a').text()?.trim(); const novelCover = loadedCheerio(this).find('a > img').attr('src')?.trim(); diff --git a/src/sources/sourceManager.ts b/src/sources/sourceManager.ts index 61e106873..e4ff7af14 100644 --- a/src/sources/sourceManager.ts +++ b/src/sources/sourceManager.ts @@ -144,6 +144,10 @@ import { RulateScraper, ErolateScraper, } from './multisrc/rulate/RulateGenerator'; +import { + IfreedomScraper, + BookhamsterScraper, +} from './multisrc/ifreedom/IfreedomGenerator'; import { RuRanobeScraper, UkrRanobeScraper, @@ -340,6 +344,8 @@ export const sourceManager = (sourceId: number): Scraper => { 168: LitSpaceScraper, // @ts-ignore 169: AsuraLightNovelScraper, // @ts-ignore 170: ICantReadJPTLScraper, // @ts-ignore + 171: IfreedomScraper, // @ts-ignore + 172: BookhamsterScraper, // @ts-ignore }; return scrapers[sourceId]; diff --git a/src/sources/sources.json b/src/sources/sources.json index 5ecd2c1bc..c118e0e29 100644 --- a/src/sources/sources.json +++ b/src/sources/sources.json @@ -1041,5 +1041,19 @@ "sourceName": "I Cant Read Japanese TL", "icon": "https://icantreadjapanese.files.wordpress.com/2021/03/cropped-cropped-site-icon-1.png?w=16", "lang": "English" + }, + { + "sourceId": 171, + "url": "https://ifreedom.su", + "sourceName": "Свободный Мир Ранобэ", + "icon": "https://ifreedom.su/wp-content/uploads/2021/03/logo.png", + "lang": "Russian" + }, + { + "sourceId": 172, + "url": "https://bookhamster.ru", + "sourceName": "Bookhamster", + "icon": "https://bookhamster.ru/wp-content/uploads/2023/01/log.jpg", + "lang": "Russian" } ] diff --git a/src/sources/ua/smakolykytl.ts b/src/sources/ua/smakolykytl.ts index 8c72be92d..ac59963be 100644 --- a/src/sources/ua/smakolykytl.ts +++ b/src/sources/ua/smakolykytl.ts @@ -63,14 +63,15 @@ const parseNovelAndChapters = async (novelUrl: string) => { ); const data = (await res.json()) as response; - data?.books?.forEach(volume => - volume?.chapters?.map(chapter => - novel.chapters.push({ - chapterName: volume.title + ' ' + chapter.title, - releaseDate: dayjs(chapter.modifiedAt).format('LLL'), - chapterUrl: baseUrl + 'read/' + chapter.id, - }), - ), + data?.books?.forEach( + volume => + volume?.chapters?.map(chapter => + novel.chapters.push({ + chapterName: volume.title + ' ' + chapter.title, + releaseDate: dayjs(chapter.modifiedAt).format('LLL'), + chapterUrl: baseUrl + 'read/' + chapter.id, + }), + ), ); return novel; From a57b7491953054f5c50c92b1726061eadd6bdf0b Mon Sep 17 00:00:00 2001 From: Rider21 <58046032+Rider21@users.noreply.github.com> Date: Sun, 7 Jan 2024 17:36:32 +0300 Subject: [PATCH 02/12] fix --- src/sources/en/freewebnovel.js | 187 --------------------------------- src/sources/en/freewebnovel.ts | 118 +++++++++++++++++++++ src/sources/ua/smakolykytl.ts | 17 ++- 3 files changed, 126 insertions(+), 196 deletions(-) delete mode 100644 src/sources/en/freewebnovel.js create mode 100644 src/sources/en/freewebnovel.ts diff --git a/src/sources/en/freewebnovel.js b/src/sources/en/freewebnovel.js deleted file mode 100644 index 42b972870..000000000 --- a/src/sources/en/freewebnovel.js +++ /dev/null @@ -1,187 +0,0 @@ -import * as cheerio from 'cheerio'; -const baseUrl = 'https://freewebnovel.com/'; - -const popularNovels = async page => { - let url = baseUrl + 'completed-novel/' + page; - - const result = await fetch(url); - const body = await result.text(); - - const loadedCheerio = cheerio.load(body); - - let novels = []; - - loadedCheerio('.li-row').each(function () { - const novelName = loadedCheerio(this).find('.tit').text(); - const novelCover = loadedCheerio(this).find('img').attr('src'); - - let novelUrl = loadedCheerio(this) - .find('h3 > a') - .attr('href') - .replace('.html', '') - .slice(1); - - const novel = { - sourceId: 13, - novelName, - novelCover, - novelUrl, - }; - - novels.push(novel); - }); - - return { novels }; -}; - -const parseNovelAndChapters = async novelUrl => { - const url = `${baseUrl}${novelUrl.replace('/', '')}.html`; - - const result = await fetch(url); - const body = await result.text(); - - const loadedCheerio = cheerio.load(body); - - let novel = {}; - - novel.sourceId = 13; - - novel.sourceName = 'FreeWebNovel'; - - novel.url = url; - - novel.novelUrl = novelUrl; - - novel.novelName = loadedCheerio('h1.tit').text(); - - novel.novelCover = loadedCheerio('.pic > img').attr('src'); - - novel.genre = loadedCheerio('[title=Genre]') - .next() - .text() - .replace(/[\t\n]/g, ''); - - novel.author = loadedCheerio('[title=Author]') - .next() - .text() - .replace(/[\t\n]/g, ''); - - novel.artist = null; - - novel.status = loadedCheerio('[title=Status]') - .next() - .text() - .replace(/[\t\n]/g, ''); - - let novelSummary = loadedCheerio('.inner').text().trim(); - novel.summary = novelSummary; - - let novelChapters = []; - - let latestChapter; - - loadedCheerio('h3.tit').each(function (res) { - if (loadedCheerio(this).find('a').text() === novel.novelName) { - latestChapter = loadedCheerio(this) - .next() - .find('span.s3') - .text() - .match(/\d+/); - } - }); - - latestChapter = latestChapter[0]; - - for (let i = 1; i <= parseInt(latestChapter, 10); i++) { - const chapterName = 'Chapter ' + i; - - const releaseDate = null; - - const chapterUrl = 'chapter-' + i; - - const chapter = { chapterName, releaseDate, chapterUrl }; - - novelChapters.push(chapter); - } - - novel.chapters = novelChapters; - - return novel; -}; - -const parseChapter = async (novelUrl, chapterUrl) => { - let novelId = novelUrl.replace('/', ''); - - const url = `${baseUrl}${novelId}/${chapterUrl}.html`; - - const result = await fetch(url); - const body = await result.text(); - - const loadedCheerio = cheerio.load(body); - - let chapterName = loadedCheerio('h1.tit').text(); - - let chapterText = loadedCheerio('div.txt').html(); - - const chapter = { - sourceId: 13, - novelUrl, - chapterUrl, - chapterName, - chapterText, - }; - - return chapter; -}; - -const searchNovels = async searchTerm => { - const url = baseUrl + 'search/'; - - const formData = new FormData(); - formData.append('searchkey', searchTerm); - - const result = await fetch(url, { - method: 'POST', - body: formData, - }); - const body = await result.text(); - - const loadedCheerio = cheerio.load(body); - - let novels = []; - - loadedCheerio('.li-row > .li > .con').each(function () { - const novelName = loadedCheerio(this).find('.tit').text(); - const novelCover = loadedCheerio(this) - .find('.pic > a > img') - .attr('data-cfsrc'); - - let novelUrl = loadedCheerio(this) - .find('h3 > a') - .attr('href') - .replace('.html', '') - .slice(1); - - novelUrl += '/'; - - const novel = { - sourceId: 13, - novelName, - novelCover, - novelUrl, - }; - - novels.push(novel); - }); - - return novels; -}; - -const FreeWebNovelScraper = { - popularNovels, - parseNovelAndChapters, - parseChapter, - searchNovels, -}; - -export default FreeWebNovelScraper; diff --git a/src/sources/en/freewebnovel.ts b/src/sources/en/freewebnovel.ts new file mode 100644 index 000000000..2cc939be6 --- /dev/null +++ b/src/sources/en/freewebnovel.ts @@ -0,0 +1,118 @@ +import * as cheerio from 'cheerio'; +import { + SourceChapter, + SourceChapterItem, + SourceNovel, + SourceNovelItem, +} from '../types'; + +const sourceId = 13; +const sourceName = 'FreeWebNovel'; +const baseUrl = 'https://freewebnovel.com'; + +const popularNovels = async (page: number, { showLatestNovels }) => { + const sort = showLatestNovels + ? '/latest-release-novels/' + : '/completed-novels/'; + + const result = await fetch(baseUrl + sort + page).then(res => res.text()); + const loadedCheerio = cheerio.load(result); + + const novels: SourceNovelItem[] = loadedCheerio('.li-row') + .map((index, element) => ({ + sourceId, + novelName: loadedCheerio(element).find('.tit').text(), + novelCover: loadedCheerio(element).find('img').attr('src'), + novelUrl: baseUrl + loadedCheerio(element).find('h3 > a').attr('href'), + })) + .get(); + + return { novels }; +}; + +const parseNovelAndChapters = async (novelUrl: string) => { + const result = await fetch(result).then(res => res.text()); + const loadedCheerio = cheerio.load(result); + + const novel: SourceNovel = { + sourceId, + sourceName, + novelUrl, + url: novelUrl, + novelName: loadedCheerio('h1.tit').text(), + novelCover: loadedCheerio('.pic > img').attr('src'), + summary: loadedCheerio('.inner').text().trim(), + }; + + novel.genre = loadedCheerio('[title=Genre]') + .next() + .text() + .replace(/[\t\n]/g, ''); + + novel.author = loadedCheerio('[title=Author]') + .next() + .text() + .replace(/[\t\n]/g, ''); + + novel.status = loadedCheerio('[title=Status]') + .next() + .text() + .replace(/[\t\n]/g, ''); + + const chapters: SourceChapterItem[] = loadedCheerio('#idData > li > a') + .map((index, element) => ({ + chapterName: loadedCheerio(element).attr('title') || 'Chapter ' + index, + releaseDate: null, + chapterUrl: baseUrl + loadedCheerio(element).attr('href'), + })) + .get(); + + novel.chapters = chapters; + return novel; +}; + +const parseChapter = async (novelUrl: string, chapterUrl: string) => { + const result = await fetch(chapterUrl).then(res => res.text()); + const loadedCheerio = cheerio.load(result); + + const chapter: SourceChapter = { + sourceId, + novelUrl, + chapterUrl, + chapterName: loadedCheerio('h1.tit').text(), + chapterText: loadedCheerio('div.txt').html(), + }; + + return chapter; +}; + +const searchNovels = async (searchTerm: string) => { + const result = await fetch(baseUrl + '/search/', { + method: 'POST', + body: 'searchkey=' + encodeURIComponent(searchTerm), + headers: { + 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', + }, + }).then(res => res.text()); + + const loadedCheerio = cheerio.load(result); + const novels: SourceNovelItem[] = loadedCheerio('.li-row > .li > .con') + .map((index, element) => ({ + sourceId, + novelName: loadedCheerio(element).find('.tit').text(), + novelCover: loadedCheerio(element).find('.pic > a > img').attr('src'), + novelUrl: baseUrl + loadedCheerio(element).find('h3 > a').attr('href'), + })) + .get(); + + return novels; +}; + +const FreeWebNovelScraper = { + popularNovels, + parseNovelAndChapters, + parseChapter, + searchNovels, +}; + +export default FreeWebNovelScraper; diff --git a/src/sources/ua/smakolykytl.ts b/src/sources/ua/smakolykytl.ts index ac59963be..8c72be92d 100644 --- a/src/sources/ua/smakolykytl.ts +++ b/src/sources/ua/smakolykytl.ts @@ -63,15 +63,14 @@ const parseNovelAndChapters = async (novelUrl: string) => { ); const data = (await res.json()) as response; - data?.books?.forEach( - volume => - volume?.chapters?.map(chapter => - novel.chapters.push({ - chapterName: volume.title + ' ' + chapter.title, - releaseDate: dayjs(chapter.modifiedAt).format('LLL'), - chapterUrl: baseUrl + 'read/' + chapter.id, - }), - ), + data?.books?.forEach(volume => + volume?.chapters?.map(chapter => + novel.chapters.push({ + chapterName: volume.title + ' ' + chapter.title, + releaseDate: dayjs(chapter.modifiedAt).format('LLL'), + chapterUrl: baseUrl + 'read/' + chapter.id, + }), + ), ); return novel; From 8759afc9b27cf8762dd03004e784d01c35323eed Mon Sep 17 00:00:00 2001 From: Rider21 <58046032+Rider21@users.noreply.github.com> Date: Sun, 7 Jan 2024 17:56:48 +0300 Subject: [PATCH 03/12] genre --- src/sources/ru/freedlit.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/sources/ru/freedlit.ts b/src/sources/ru/freedlit.ts index 4a9c9e0f3..7c1a5c91c 100644 --- a/src/sources/ru/freedlit.ts +++ b/src/sources/ru/freedlit.ts @@ -19,6 +19,10 @@ const popularNovels = async (page, { showLatestNovels, filters }) => { url += '&access=' + (filters?.access || 'all'); url += '&adult=' + (filters?.adult || 'hide'); + if (filters?.genre?.value?.length) { + url += filters.genre.value.map(id => '&genres_included[]=' + id).join(''); + } + const result = await fetch(url).then(res => res.text()); const loadedCheerio = cheerio.load(result); @@ -98,7 +102,7 @@ const searchNovels = async searchTerm => { const result = await fetch(url).then(res => res.text()); const loadedCheerio = cheerio.load(result); - const novels: Novel.Item[] = []; + const novels: SourceNovelItem[] = []; loadedCheerio('#bookListBlock > div').each(function () { const novelName = loadedCheerio(this).find('h4 > a').text()?.trim(); const novelCover = loadedCheerio(this).find('a > img').attr('src')?.trim(); @@ -127,7 +131,6 @@ const filters = [ key: 'genre', label: 'Жанры:', values: [ - { label: 'Любой жанр', value: 'all' }, { label: 'Альтернативная история', value: 'alternative-history' }, { label: 'Антиутопия', value: 'dystopia' }, { label: 'Бизнес-литература', value: 'business-literature' }, @@ -205,7 +208,7 @@ const filters = [ { label: 'Юмористическое фэнтези', value: 'humor-fantasy' }, { label: 'RPS', value: 'rps' }, ], - inputType: FilterInputs.Picker, + inputType: FilterInputs.Checkbox, }, { key: 'status', From 888139e4b428a62b4c5aa6c582a01b465cc2cdd5 Mon Sep 17 00:00:00 2001 From: Rider21 <58046032+Rider21@users.noreply.github.com> Date: Tue, 9 Jan 2024 00:34:57 +0300 Subject: [PATCH 04/12] test --- package-lock.json | 269 ++++++++++++++++++++++-- package.json | 2 + patches/expo-modules-core+0.11.10.patch | 13 ++ patches/react-native+0.69.9.patch | 13 ++ patches/react-native-mmkv+2.5.1.patch | 13 ++ src/sources/ru/freedlit.ts | 4 +- 6 files changed, 291 insertions(+), 23 deletions(-) create mode 100644 patches/expo-modules-core+0.11.10.patch create mode 100644 patches/react-native+0.69.9.patch create mode 100644 patches/react-native-mmkv+2.5.1.patch diff --git a/package-lock.json b/package-lock.json index f891daffe..b091e991e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -87,6 +87,7 @@ "eslint-plugin-react-native": "^4.0.0", "husky": "^7.0.4", "lint-staged": "^12.3.7", + "patch-package": "^8.0.0", "prettier": "^2.8.8", "typescript": "^4.6.3" } @@ -6663,12 +6664,13 @@ } }, "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", + "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.1", + "set-function-length": "^1.1.1" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -7536,6 +7538,19 @@ "node": ">=0.8" } }, + "node_modules/define-data-property": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", + "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "dependencies": { + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/define-lazy-prop": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", @@ -9639,9 +9654,12 @@ } }, "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/function.prototype.name": { "version": "1.1.5", @@ -11660,12 +11678,36 @@ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, + "node_modules/json-stable-stringify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.1.0.tgz", + "integrity": "sha512-zfA+5SuwYN2VWqN1/5HZaDzQKLJHaBVMZIIM+wuYjdptkaQsqzDdqjqf+lZZJUuJq1aanHiY8LhH8LmH+qBYJA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "isarray": "^2.0.5", + "jsonify": "^0.0.1", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, + "node_modules/json-stable-stringify/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -11685,6 +11727,15 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/jsonify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz", + "integrity": "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/jsx-ast-utils": { "version": "3.3.4", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.4.tgz", @@ -14057,9 +14108,10 @@ } }, "node_modules/patch-package": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-7.0.0.tgz", - "integrity": "sha512-eYunHbnnB2ghjTNc5iL1Uo7TsGMuXk0vibX3RFcE/CdVdXzmdbMsG/4K4IgoSuIkLTI5oHrMQk4+NkFqSed0BQ==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-8.0.0.tgz", + "integrity": "sha512-da8BVIhzjtgScwDJ2TtKsfT5JFWz1hYoBl9rUQ1f38MC2HwnEIkK8VN3dKMKcP7P7bvvgzNDbfNHtx3MsQb5vA==", + "dev": true, "dependencies": { "@yarnpkg/lockfile": "^1.1.0", "chalk": "^4.1.2", @@ -14067,11 +14119,12 @@ "cross-spawn": "^7.0.3", "find-yarn-workspace-root": "^2.0.0", "fs-extra": "^9.0.0", + "json-stable-stringify": "^1.0.2", "klaw-sync": "^6.0.0", "minimist": "^1.2.6", "open": "^7.4.2", "rimraf": "^2.6.3", - "semver": "^5.6.0", + "semver": "^7.5.3", "slash": "^2.0.0", "tmp": "^0.0.33", "yaml": "^2.2.2" @@ -14088,6 +14141,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -14102,6 +14156,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -14117,6 +14172,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -14127,12 +14183,14 @@ "node_modules/patch-package/node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/patch-package/node_modules/fs-extra": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", @@ -14147,6 +14205,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, "engines": { "node": ">=8" } @@ -14155,6 +14214,7 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, "dependencies": { "universalify": "^2.0.0" }, @@ -14166,6 +14226,7 @@ "version": "7.4.2", "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "dev": true, "dependencies": { "is-docker": "^2.0.0", "is-wsl": "^2.1.1" @@ -14177,18 +14238,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/patch-package/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "bin": { - "semver": "bin/semver" - } - }, "node_modules/patch-package/node_modules/slash": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true, "engines": { "node": ">=6" } @@ -14197,6 +14251,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -14208,6 +14263,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true, "engines": { "node": ">= 10.0.0" } @@ -14216,6 +14272,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz", "integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==", + "dev": true, "engines": { "node": ">= 14" } @@ -15067,6 +15124,46 @@ "react-native-vector-icons": "*" } }, + "node_modules/react-native-paper/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/react-native-paper/node_modules/ansi-styles/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/react-native-paper/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/react-native-paper/node_modules/color": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", @@ -15076,6 +15173,122 @@ "color-string": "^1.6.0" } }, + "node_modules/react-native-paper/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/react-native-paper/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/react-native-paper/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/react-native-paper/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/react-native-paper/node_modules/open": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", + "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "dependencies": { + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-native-paper/node_modules/patch-package": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-7.0.2.tgz", + "integrity": "sha512-PMYfL8LXxGIRmxXLqlEaBxzKPu7/SdP13ld6GSfAUJUZRmBDPp8chZs0dpzaAFn9TSPnFiMwkC6PJt6pBiAl8Q==", + "dependencies": { + "@yarnpkg/lockfile": "^1.1.0", + "chalk": "^4.1.2", + "ci-info": "^3.7.0", + "cross-spawn": "^7.0.3", + "find-yarn-workspace-root": "^2.0.0", + "fs-extra": "^9.0.0", + "klaw-sync": "^6.0.0", + "minimist": "^1.2.6", + "open": "^7.4.2", + "rimraf": "^2.6.3", + "semver": "^7.5.3", + "slash": "^2.0.0", + "tmp": "^0.0.33", + "yaml": "^2.2.2" + }, + "bin": { + "patch-package": "index.js" + }, + "engines": { + "node": ">=14", + "npm": ">5" + } + }, + "node_modules/react-native-paper/node_modules/slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/react-native-paper/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/react-native-paper/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/react-native-paper/node_modules/yaml": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", + "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", + "engines": { + "node": ">= 14" + } + }, "node_modules/react-native-reanimated": { "version": "2.9.1", "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-2.9.1.tgz", @@ -16260,6 +16473,20 @@ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" }, + "node_modules/set-function-length": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", + "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", + "dependencies": { + "define-data-property": "^1.1.1", + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/set-value": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", diff --git a/package.json b/package.json index 71858976a..a94b96b26 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "sources:madara": "node scripts/generateMadaraSource.cjs", "source:generator": "node scripts/generateSource.cjs", "strings": "node scripts/stringTypes.cjs", + "postinstall": "patch-package", "prepare": "husky install" }, "dependencies": { @@ -95,6 +96,7 @@ "eslint-plugin-react-native": "^4.0.0", "husky": "^7.0.4", "lint-staged": "^12.3.7", + "patch-package": "^8.0.0", "prettier": "^2.8.8", "typescript": "^4.6.3" }, diff --git a/patches/expo-modules-core+0.11.10.patch b/patches/expo-modules-core+0.11.10.patch new file mode 100644 index 000000000..135a3e64a --- /dev/null +++ b/patches/expo-modules-core+0.11.10.patch @@ -0,0 +1,13 @@ +diff --git a/node_modules/expo-modules-core/android/build.gradle b/node_modules/expo-modules-core/android/build.gradle +index ae4ff33..9a95164 100644 +--- a/node_modules/expo-modules-core/android/build.gradle ++++ b/node_modules/expo-modules-core/android/build.gradle +@@ -375,7 +375,7 @@ task extractJNIFiles { + // BOOST + task downloadBoost(dependsOn: createNativeDepsDirectories, type: Download) { + def srcUrl = REACT_NATIVE_TARGET_VERSION >= 69 +- ? "https://boostorg.jfrog.io/artifactory/main/release/${BOOST_VERSION.replace("_", ".")}/source/boost_${BOOST_VERSION}.tar.gz" ++ ? "https://sourceforge.net/projects/boost/files/boost/${BOOST_VERSION.replace("_", ".")}/boost_${BOOST_VERSION}.tar.gz" + : "https://github.com/react-native-community/boost-for-react-native/releases/download/v${BOOST_VERSION.replace("_", ".")}-0/boost_${BOOST_VERSION}.tar.gz" + src(srcUrl) + onlyIfNewer(true) diff --git a/patches/react-native+0.69.9.patch b/patches/react-native+0.69.9.patch new file mode 100644 index 000000000..394553ade --- /dev/null +++ b/patches/react-native+0.69.9.patch @@ -0,0 +1,13 @@ +diff --git a/node_modules/react-native/third-party-podspecs/boost.podspec b/node_modules/react-native/third-party-podspecs/boost.podspec +index 2f1fcc4..fa82134 100644 +--- a/node_modules/react-native/third-party-podspecs/boost.podspec ++++ b/node_modules/react-native/third-party-podspecs/boost.podspec +@@ -10,7 +10,7 @@ Pod::Spec.new do |spec| + spec.homepage = 'http://www.boost.org' + spec.summary = 'Boost provides free peer-reviewed portable C++ source libraries.' + spec.authors = 'Rene Rivera' +- spec.source = { :http => 'https://boostorg.jfrog.io/artifactory/main/release/1.76.0/source/boost_1_76_0.tar.bz2', ++ spec.source = { :http => 'https://archives.boost.io/release/1.76.0/source/boost_1_76_0.tar.bz2', + :sha256 => 'f0397ba6e982c4450f27bf32a2a83292aba035b827a5623a14636ea583318c41' } + + # Pinning to the same version as React.podspec. diff --git a/patches/react-native-mmkv+2.5.1.patch b/patches/react-native-mmkv+2.5.1.patch new file mode 100644 index 000000000..56c4be0fb --- /dev/null +++ b/patches/react-native-mmkv+2.5.1.patch @@ -0,0 +1,13 @@ +diff --git a/node_modules/react-native-mmkv/android/build.gradle b/node_modules/react-native-mmkv/android/build.gradle +index 13eae76..98b2a12 100644 +--- a/node_modules/react-native-mmkv/android/build.gradle ++++ b/node_modules/react-native-mmkv/android/build.gradle +@@ -204,7 +204,7 @@ task createNativeDepsDirectories { + + task downloadBoost(dependsOn: createNativeDepsDirectories, type: Download) { + def transformedVersion = BOOST_VERSION.replace("_", ".") +- def srcUrl = "https://boostorg.jfrog.io/artifactory/main/release/${transformedVersion}/source/boost_${BOOST_VERSION}.tar.gz" ++ def srcUrl = "https://sourceforge.net/projects/boost/files/boost/${transformedVersion}/boost_${BOOST_VERSION}.tar.gz" + if (REACT_NATIVE_VERSION < 69) { + srcUrl = "https://github.com/react-native-community/boost-for-react-native/releases/download/v${transformedVersion}-0/boost_${BOOST_VERSION}.tar.gz" + } diff --git a/src/sources/ru/freedlit.ts b/src/sources/ru/freedlit.ts index 7c1a5c91c..177d2748f 100644 --- a/src/sources/ru/freedlit.ts +++ b/src/sources/ru/freedlit.ts @@ -19,8 +19,8 @@ const popularNovels = async (page, { showLatestNovels, filters }) => { url += '&access=' + (filters?.access || 'all'); url += '&adult=' + (filters?.adult || 'hide'); - if (filters?.genre?.value?.length) { - url += filters.genre.value.map(id => '&genres_included[]=' + id).join(''); + if (filters?.genre?.length) { + url += filters.genre.map(id => '&genres_included[]=' + id).join(''); } const result = await fetch(url).then(res => res.text()); From 95082dc275bf3cb93248bc33eee8e1d253548be3 Mon Sep 17 00:00:00 2001 From: Rider21 <58046032+Rider21@users.noreply.github.com> Date: Tue, 9 Jan 2024 01:03:16 +0300 Subject: [PATCH 05/12] Update freewebnovel.ts --- src/sources/en/freewebnovel.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sources/en/freewebnovel.ts b/src/sources/en/freewebnovel.ts index 2cc939be6..a3c731901 100644 --- a/src/sources/en/freewebnovel.ts +++ b/src/sources/en/freewebnovel.ts @@ -31,7 +31,7 @@ const popularNovels = async (page: number, { showLatestNovels }) => { }; const parseNovelAndChapters = async (novelUrl: string) => { - const result = await fetch(result).then(res => res.text()); + const result = await fetch(novelUrl).then(res => res.text()); const loadedCheerio = cheerio.load(result); const novel: SourceNovel = { From 5aac2d9a6011e49fc6865ed44cd058d8018719a6 Mon Sep 17 00:00:00 2001 From: Rider21 <58046032+Rider21@users.noreply.github.com> Date: Tue, 9 Jan 2024 13:48:59 +0300 Subject: [PATCH 06/12] status parsing --- src/sources/en/freewebnovel.ts | 12 +++++++---- .../multisrc/ifreedom/IfreedomScraper.js | 6 +++--- src/sources/multisrc/readwn/ReadwnScraper.js | 1 + src/sources/multisrc/rulate/RulateScraper.js | 4 ++-- src/sources/ru/jaomix.js | 6 +++--- src/sources/ru/ranobelib.js | 12 +++++------ src/sources/ru/renovels.js | 20 +++++++++---------- 7 files changed, 33 insertions(+), 28 deletions(-) diff --git a/src/sources/en/freewebnovel.ts b/src/sources/en/freewebnovel.ts index a3c731901..744a06375 100644 --- a/src/sources/en/freewebnovel.ts +++ b/src/sources/en/freewebnovel.ts @@ -1,3 +1,4 @@ +import { Status } from '../helpers/constants'; import * as cheerio from 'cheerio'; import { SourceChapter, @@ -54,10 +55,13 @@ const parseNovelAndChapters = async (novelUrl: string) => { .text() .replace(/[\t\n]/g, ''); - novel.status = loadedCheerio('[title=Status]') - .next() - .text() - .replace(/[\t\n]/g, ''); + novel.status = + loadedCheerio('[title=Status]') + .next() + .text() + .replace(/[\t\n]/g, '') === 'OnGoing' + ? Status.ONGOING + : Status.COMPLETED; const chapters: SourceChapterItem[] = loadedCheerio('#idData > li > a') .map((index, element) => ({ diff --git a/src/sources/multisrc/ifreedom/IfreedomScraper.js b/src/sources/multisrc/ifreedom/IfreedomScraper.js index 13cfd00ae..bcae1e422 100644 --- a/src/sources/multisrc/ifreedom/IfreedomScraper.js +++ b/src/sources/multisrc/ifreedom/IfreedomScraper.js @@ -17,13 +17,13 @@ class IfreedomScraper { ? 'По дате обновления' : filters?.sort || 'По рейтингу'); - if (filters?.status instanceof Array) { + if (filters?.status?.length) { url += filters.status.map(i => '&status[]=' + i).join(''); } - if (filters?.lang instanceof Array) { + if (filters?.lang?.length) { url += filters.lang.map(i => '&lang[]=' + i).join(''); } - if (filters?.genre instanceof Array) { + if (filters?.genre?.length) { url += filters.genre.map(i => '&genre[]=' + i).join(''); } url += '&bpage=' + page; diff --git a/src/sources/multisrc/readwn/ReadwnScraper.js b/src/sources/multisrc/readwn/ReadwnScraper.js index 1b0b56de8..6267a1afa 100644 --- a/src/sources/multisrc/readwn/ReadwnScraper.js +++ b/src/sources/multisrc/readwn/ReadwnScraper.js @@ -74,6 +74,7 @@ class ReadwnScraper { { label: 'Yaoi', value: 'yaoi' }, ...genres.values, ], + inputType: FilterInputs.Picker, }, ]; } diff --git a/src/sources/multisrc/rulate/RulateScraper.js b/src/sources/multisrc/rulate/RulateScraper.js index 189b02d92..e24fa1422 100644 --- a/src/sources/multisrc/rulate/RulateScraper.js +++ b/src/sources/multisrc/rulate/RulateScraper.js @@ -88,11 +88,11 @@ class RulateScraper { url += '&adult=' + (filters?.adult || '0'); if (filters?.genres?.length) { - url += filters.genres.map(i => `&genres[]=${i}`).join(''); + url += filters.genres.map(i => '&genres[]=' + i).join(''); } if (filters?.tags?.length) { - url += filters.tags.map(i => `&tags[]=${i}`).join(''); + url += filters.tags.map(i => '&tags[]=' + i).join(''); } if (filters?.trash?.length) { diff --git a/src/sources/ru/jaomix.js b/src/sources/ru/jaomix.js index 290680367..9359081e2 100644 --- a/src/sources/ru/jaomix.js +++ b/src/sources/ru/jaomix.js @@ -10,15 +10,15 @@ const baseUrl = 'https://jaomix.ru'; const popularNovels = async (page, { showLatestNovels, filters }) => { let url = baseUrl + '/?searchrn'; - if (filters?.lang instanceof Array) { + if (filters?.lang?.length) { url += filters.lang.map((lang, idx) => `&lang[${idx}]=${lang}`).join(''); } - if (filters?.genre instanceof Array) { + if (filters?.genre?.length) { url += filters.genre .map((genre, idx) => `&genre[${idx}]=${genre}`) .join(''); } - if (filters?.delgenre instanceof Array) { + if (filters?.delgenre?.length) { url += filters.delgenre .map((genre, idx) => `&delgenre[${idx}]=del ${genre}`) .join(''); diff --git a/src/sources/ru/ranobelib.js b/src/sources/ru/ranobelib.js index 8908d4229..a14edacde 100644 --- a/src/sources/ru/ranobelib.js +++ b/src/sources/ru/ranobelib.js @@ -16,27 +16,27 @@ const popularNovels = async (page, { showLatestNovels, filters }) => { url += '&page=' + page; if (filters?.type?.length) { - url += filters.type.map(i => `&types[]=${i}`).join(''); + url += filters.type.map(i => '&types[]=' + i).join(''); } if (filters?.format?.length) { - url += filters.format.map(i => `&format[include][]=${i}`).join(''); + url += filters.format.map(i => '&format[include][]=' + i).join(''); } if (filters?.status?.length) { - url += filters.status.map(i => `&status[]=${i}`).join(''); + url += filters.status.map(i => '&status[]=' + i).join(''); } if (filters?.statuss?.length) { - url += filters.statuss.map(i => `&manga_status[]=${i}`).join(''); + url += filters.statuss.map(i => '&manga_status[]=' + i).join(''); } if (filters?.genres?.length) { - url += filters.genres.map(i => `&genres[include][]=${i}`).join(''); + url += filters.genres.map(i => '&genres[include][]=' + i).join(''); } if (filters?.tags?.length) { - url += filters.tags.map(i => `&tags[include][]=${i}`).join(''); + url += filters.tags.map(i => '&tags[include][]=' + i).join(''); } const result = await fetch(url); diff --git a/src/sources/ru/renovels.js b/src/sources/ru/renovels.js index 11a09747f..16db550e5 100644 --- a/src/sources/ru/renovels.js +++ b/src/sources/ru/renovels.js @@ -13,24 +13,24 @@ const popularNovels = async (page, { showLatestNovels, filters }) => { url += filters?.order ? filters?.order?.replace('+', '') : '-'; url += showLatestNovels ? 'chapter_date' : filters?.sort || 'rating'; - if (filters?.genres instanceof Array) { - url += filters.genres.map(i => `&genres=${i}`).join(''); + if (filters?.genres?.length) { + url += filters.genres.map(i => '&genres=' + i).join(''); } - if (filters?.status instanceof Array) { - url += filters?.status.map(i => `&status=${i}`).join(''); + if (filters?.status?.length) { + url += filters.status.map(i => '&status=' + i).join(''); } - if (filters?.types instanceof Array) { - url += filters.types.map(i => `&types=${i}`).join(''); + if (filters?.types?.length) { + url += filters.types.map(i => '&types=' + i).join(''); } - if (filters?.categories instanceof Array) { - url += filters.categories.map(i => `&categories=${i}`).join(''); + if (filters?.categories?.length) { + url += filters.categories.map(i => '&categories=' + i).join(''); } - if (filters?.age_limit instanceof Array) { - url += filters.age_limit.map(i => `&age_limit=${i}`).join(''); + if (filters?.age_limit?.length) { + url += filters.age_limit.map(i => '&age_limit=' + i).join(''); } url += '&page=' + page; From b1a2ac565af444a7089bc45658a3203c956a63e0 Mon Sep 17 00:00:00 2001 From: Rider21 <58046032+Rider21@users.noreply.github.com> Date: Tue, 9 Jan 2024 14:26:43 +0300 Subject: [PATCH 07/12] test readwn --- src/sources/multisrc/readwn/ReadwnScraper.js | 197 ++++++++----------- 1 file changed, 83 insertions(+), 114 deletions(-) diff --git a/src/sources/multisrc/readwn/ReadwnScraper.js b/src/sources/multisrc/readwn/ReadwnScraper.js index 6267a1afa..a00d92cfd 100644 --- a/src/sources/multisrc/readwn/ReadwnScraper.js +++ b/src/sources/multisrc/readwn/ReadwnScraper.js @@ -1,6 +1,7 @@ import * as cheerio from 'cheerio'; import QueryString from 'qs'; +import { parseMadaraDate } from '../../helpers/parseDate'; import { fetchHtml } from '@utils/fetch/fetch'; import { FilterInputs } from '../../types/filterTypes'; @@ -95,60 +96,45 @@ class ReadwnScraper { const body = await fetchHtml({ url, sourceId }); const loadedCheerio = cheerio.load(body); - - let novels = []; - - loadedCheerio('li.novel-item').each(function () { - const novelName = loadedCheerio(this).find('h4').text(); - const novelUrl = baseUrl + loadedCheerio(this).find('a').attr('href'); - - const coverUri = loadedCheerio(this) - .find('.novel-cover > img') - .attr('data-src'); - - const novelCover = baseUrl + coverUri; - - const novel = { sourceId, novelName, novelCover, novelUrl }; - - novels.push(novel); - }); + const novels = loadedCheerio('li.novel-item') + .map((index, element) => ({ + sourceId: this.sourceId, + novelName: loadedCheerio(element).find('h4').text(), + novelCover: + this.baseUrl + + loadedCheerio(element).find('.novel-cover > img').attr('data-src'), + novelUrl: this.baseUrl + loadedCheerio(element).find('a').attr('href'), + })) + .get(); return { novels }; } async parseNovelAndChapters(novelUrl) { - const sourceId = this.sourceId; - const baseUrl = this.baseUrl; - const sourceName = this.sourceName; - - const url = novelUrl; - const body = await fetchHtml({ url, sourceId }); + const loadedCheerio = cheerio.load(body); - let loadedCheerio = cheerio.load(body); - - let novel = { - sourceId: sourceId, - sourceName: sourceName, - url, + const novel = { + sourceId: this.sourceId, + sourceName: this.sourceName, + url: novelUrl, novelUrl, }; novel.novelName = loadedCheerio('h1.novel-title').text(); - - const coverUri = loadedCheerio('figure.cover > img').attr('data-src'); - novel.novelCover = baseUrl + coverUri; + novel.novelCover = + this.baseUrl + loadedCheerio('figure.cover > img').attr('data-src'); + novel.author = loadedCheerio('span[itemprop=author]').text(); novel.summary = loadedCheerio('.summary') .text() .replace('Summary', '') .trim(); - novel.genre = ''; - - loadedCheerio('div.categories > ul > li').each(function () { - novel.genre += loadedCheerio(this).text().trim() + ','; - }); + novel.genre = loadedCheerio('div.categories > ul > li') + .map((index, element) => loadedCheerio(element).text()?.trim()) + .get() + .join(','); loadedCheerio('div.header-stats > span').each(function () { if (loadedCheerio(this).find('small').text() === 'Status') { @@ -156,72 +142,69 @@ class ReadwnScraper { } }); - novel.genre = novel.genre.slice(0, -1); - - novel.author = loadedCheerio('span[itemprop=author]').text(); - - let novelChapters = []; - - const novelId = novelUrl.replace('.html', '').replace(baseUrl, ''); - - const latestChapterNo = loadedCheerio('.header-stats') - .find('span > strong') - .first() - .text() - .trim(); - - let lastChapterNo = 1; - loadedCheerio('.chapter-list li').each(function () { - const chapterName = loadedCheerio(this) - .find('a .chapter-title') + const latestChapterNo = parseInt( + loadedCheerio('.header-stats') + .find('span > strong') + .first() .text() - .trim(); - - const chapterUrl = loadedCheerio(this).find('a').attr('href').trim(); - - const releaseDate = loadedCheerio(this) - .find('a .chapter-update') - .text() - .trim(); - - lastChapterNo = loadedCheerio(this).find('a .chapter-no').text().trim(); - - const chapter = { chapterName, releaseDate, chapterUrl }; - - novelChapters.push(chapter); - }); - - // Itterate once more before loop to finish off - lastChapterNo++; - for (let i = lastChapterNo; i <= latestChapterNo; i++) { - const chapterName = `Chapter ${i}`; - const chapterUrl = `${novelId}_${i}.html`; - const releaseDate = null; - - const chapter = { chapterName, releaseDate, chapterUrl }; - - novelChapters.push(chapter); + .trim(), + ); + + const chapters = loadedCheerio('.chapter-list li') + .map((index, element) => { + const chapterName = loadedCheerio(element) + .find('a .chapter-title') + .text() + .trim(); + const chapterUrl = loadedCheerio(element) + .find('a') + .attr('href') + ?.trim(); + const releaseDate = loadedCheerio(element) + .find('a .chapter-update') + .text() + .trim(); + + if (!chapterName || !chapterUrl) return null; + + return { + chapterName, + releaseDate: parseMadaraDate(releaseDate), + chapterUrl: this.baseUrl + chapterUrl, + }; + }) + .get() + .filter(chapter => chapter); + + if (latestChapterNo > chapters.length) { + const lastChapterNo = parseInt( + chapters[chapters.length - 1].chapterUrl.match(/_(\d+)\.html/)?.[1] || + chapters.length, + 10, + ); + + for (let i = lastChapterNo + 1; i <= latestChapterNo; i++) { + chapters.push({ + chapterName: 'Chapter ' + i, + releaseDate: null, + chapterUrl: novelUrl.replace('.html', '_' + i + '.html'), + }); + } } - - novel.chapters = novelChapters; + novel.chapters = chapters; return novel; } async parseChapter(novelUrl, chapterUrl) { - const baseUrl = this.baseUrl; - const url = baseUrl + chapterUrl; - const sourceId = this.sourceId; - - const body = await fetchHtml({ url, sourceId }); + const body = await fetchHtml({ url: this.baseUrl + chapterUrl, sourceId }); const loadedCheerio = cheerio.load(body); - const chapterName = loadedCheerio('.titles > h2').text(); const chapterText = loadedCheerio('.chapter-content').html(); const chapter = { - sourceId, + sourceId: this.sourceId, novelUrl, chapterUrl, chapterName, @@ -232,12 +215,8 @@ class ReadwnScraper { } async searchNovels(searchTerm) { - const baseUrl = this.baseUrl; - const sourceId = this.sourceId; - const searchUrl = `${baseUrl}e/search/index.php`; - const body = await fetchHtml({ - url: searchUrl, + url: `${baseUrl}e/search/index.php`, init: { headers: { 'Content-Type': 'application/x-www-form-urlencoded', @@ -257,25 +236,15 @@ class ReadwnScraper { }); const loadedCheerio = cheerio.load(body); - - let novels = []; - - loadedCheerio('li.novel-item').each(function () { - const novelName = loadedCheerio(this).find('h4').text(); - const novelUrl = baseUrl + loadedCheerio(this).find('a').attr('href'); - - const coverUri = loadedCheerio(this).find('img').attr('data-src'); - const novelCover = baseUrl + coverUri; - - const novel = { - sourceId, - novelName, - novelCover, - novelUrl, - }; - - novels.push(novel); - }); + const novels = loadedCheerio('li.novel-item') + .map((index, element) => ({ + sourceId: this.sourceId, + novelName: loadedCheerio(element).find('h4').text(), + novelCover: + this.baseUrl + loadedCheerio(element).find('img').attr('data-src'), + novelUrl: this.baseUrl + loadedCheerio(element).find('a').attr('href'), + })) + .get(); return novels; } From 4a16dd160ee2877c0151a06c3eb5f4c5f24afc2e Mon Sep 17 00:00:00 2001 From: Rider21 <58046032+Rider21@users.noreply.github.com> Date: Tue, 9 Jan 2024 14:43:47 +0300 Subject: [PATCH 08/12] fix typo --- src/sources/multisrc/readwn/ReadwnScraper.js | 26 +++++++++----------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/sources/multisrc/readwn/ReadwnScraper.js b/src/sources/multisrc/readwn/ReadwnScraper.js index a00d92cfd..7fef38a5b 100644 --- a/src/sources/multisrc/readwn/ReadwnScraper.js +++ b/src/sources/multisrc/readwn/ReadwnScraper.js @@ -2,9 +2,9 @@ import * as cheerio from 'cheerio'; import QueryString from 'qs'; import { parseMadaraDate } from '../../helpers/parseDate'; -import { fetchHtml } from '@utils/fetch/fetch'; - import { FilterInputs } from '../../types/filterTypes'; +import { Status } from '../../helpers/constants'; +import { fetchHtml } from '@utils/fetch/fetch'; class ReadwnScraper { constructor(sourceId, baseUrl, sourceName, genres) { @@ -81,17 +81,12 @@ class ReadwnScraper { } async popularNovels(page, { showLatestNovels, filters }) { - const baseUrl = this.baseUrl; - const sourceId = this.sourceId; - - const pageNo = page - 1; - let url = baseUrl + 'list/'; url += (filters?.genres || 'all') + '/'; url += (filters?.status || 'all') + '-'; url += (showLatestNovels ? 'lastdotime' : filters?.sort || 'newstime') + '-'; - url += pageNo + '.html'; + url += page - 1 + '.html'; const body = await fetchHtml({ url, sourceId }); @@ -111,7 +106,7 @@ class ReadwnScraper { } async parseNovelAndChapters(novelUrl) { - const body = await fetchHtml({ url, sourceId }); + const body = await fetchHtml({ url: novelUrl, sourceId }); const loadedCheerio = cheerio.load(body); const novel = { @@ -138,7 +133,10 @@ class ReadwnScraper { loadedCheerio('div.header-stats > span').each(function () { if (loadedCheerio(this).find('small').text() === 'Status') { - novel.status = loadedCheerio(this).find('strong').text(); + novel.status = + loadedCheerio(this).find('strong').text() == 'Ongoing' + ? Status.ONGOING + : Status.COMPLETED; } }); @@ -197,7 +195,7 @@ class ReadwnScraper { } async parseChapter(novelUrl, chapterUrl) { - const body = await fetchHtml({ url: this.baseUrl + chapterUrl, sourceId }); + const body = await fetchHtml({ url: chapterUrl, sourceId }); const loadedCheerio = cheerio.load(body); const chapterName = loadedCheerio('.titles > h2').text(); @@ -216,12 +214,12 @@ class ReadwnScraper { async searchNovels(searchTerm) { const body = await fetchHtml({ - url: `${baseUrl}e/search/index.php`, + url: this.baseUrl + 'e/search/index.php', init: { headers: { 'Content-Type': 'application/x-www-form-urlencoded', - Referer: `${baseUrl}search.html`, - Origin: baseUrl, + Referer: this.baseUrl + 'search.html', + Origin: this.baseUrl, 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36', }, From 6d10586284de292acb4917a0935a836c79d36aef Mon Sep 17 00:00:00 2001 From: Rider21 <58046032+Rider21@users.noreply.github.com> Date: Tue, 9 Jan 2024 14:47:07 +0300 Subject: [PATCH 09/12] Update IfreedomScraper.js --- src/sources/multisrc/ifreedom/IfreedomScraper.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sources/multisrc/ifreedom/IfreedomScraper.js b/src/sources/multisrc/ifreedom/IfreedomScraper.js index bcae1e422..988ffc466 100644 --- a/src/sources/multisrc/ifreedom/IfreedomScraper.js +++ b/src/sources/multisrc/ifreedom/IfreedomScraper.js @@ -133,7 +133,7 @@ class IfreedomScraper { async searchNovels(searchTerm) { const url = - this.baseUrl + '/vse-knigi/?searchname=' + searchTerm + '&bpage=' + page; + this.baseUrl + '/vse-knigi/?searchname=' + searchTerm; const result = await fetch(url).then(res => res.text()); const loadedCheerio = cheerio.load(result); From 1a71d967888d53ec1ffef15b684ba3f13a17e9ae Mon Sep 17 00:00:00 2001 From: Rider21 <58046032+Rider21@users.noreply.github.com> Date: Tue, 9 Jan 2024 15:09:06 +0300 Subject: [PATCH 10/12] fix ESlint --- src/sources/ch/linovelib.js | 2 +- .../multisrc/ifreedom/IfreedomScraper.js | 11 +++++--- src/sources/multisrc/readwn/ReadwnScraper.js | 28 ++++++++++--------- 3 files changed, 23 insertions(+), 18 deletions(-) diff --git a/src/sources/ch/linovelib.js b/src/sources/ch/linovelib.js index ad388ec35..16c3f5fb2 100644 --- a/src/sources/ch/linovelib.js +++ b/src/sources/ch/linovelib.js @@ -1,5 +1,5 @@ import { showToast } from '@hooks/showToast'; -import { fetchApi, fetchHtml } from '@utils/fetch/fetch'; +import { fetchHtml } from '@utils/fetch/fetch'; import * as cheerio from 'cheerio'; diff --git a/src/sources/multisrc/ifreedom/IfreedomScraper.js b/src/sources/multisrc/ifreedom/IfreedomScraper.js index 988ffc466..ba4cb94b3 100644 --- a/src/sources/multisrc/ifreedom/IfreedomScraper.js +++ b/src/sources/multisrc/ifreedom/IfreedomScraper.js @@ -82,7 +82,9 @@ class IfreedomScraper { } }); - if (novel.author == 'Не указан') delete novel.author; + if (novel.author === 'Не указан') { + delete novel.author; + } const chapters = []; loadedCheerio('div.li-ranobe').each(function () { @@ -116,7 +118,9 @@ class IfreedomScraper { const bestlink = srcset .filter(url => url.startsWith('http')) ?.unshift(); - if (bestlink) loadedCheerio(this).attr('src', bestlink); + if (bestlink) { + loadedCheerio(this).attr('src', bestlink); + } } }); @@ -132,8 +136,7 @@ class IfreedomScraper { } async searchNovels(searchTerm) { - const url = - this.baseUrl + '/vse-knigi/?searchname=' + searchTerm; + const url = this.baseUrl + '/vse-knigi/?searchname=' + searchTerm; const result = await fetch(url).then(res => res.text()); const loadedCheerio = cheerio.load(result); diff --git a/src/sources/multisrc/readwn/ReadwnScraper.js b/src/sources/multisrc/readwn/ReadwnScraper.js index 7fef38a5b..b1451c15f 100644 --- a/src/sources/multisrc/readwn/ReadwnScraper.js +++ b/src/sources/multisrc/readwn/ReadwnScraper.js @@ -81,14 +81,14 @@ class ReadwnScraper { } async popularNovels(page, { showLatestNovels, filters }) { - let url = baseUrl + 'list/'; + let url = this.baseUrl + 'list/'; url += (filters?.genres || 'all') + '/'; url += (filters?.status || 'all') + '-'; url += (showLatestNovels ? 'lastdotime' : filters?.sort || 'newstime') + '-'; url += page - 1 + '.html'; - const body = await fetchHtml({ url, sourceId }); + const body = await fetchHtml({ url, sourceId: this.sourceId }); const loadedCheerio = cheerio.load(body); const novels = loadedCheerio('li.novel-item') @@ -106,7 +106,7 @@ class ReadwnScraper { } async parseNovelAndChapters(novelUrl) { - const body = await fetchHtml({ url: novelUrl, sourceId }); + const body = await fetchHtml({ url: novelUrl, sourceId: this.sourceId }); const loadedCheerio = cheerio.load(body); const novel = { @@ -134,7 +134,7 @@ class ReadwnScraper { loadedCheerio('div.header-stats > span').each(function () { if (loadedCheerio(this).find('small').text() === 'Status') { novel.status = - loadedCheerio(this).find('strong').text() == 'Ongoing' + loadedCheerio(this).find('strong').text() === 'Ongoing' ? Status.ONGOING : Status.COMPLETED; } @@ -146,6 +146,7 @@ class ReadwnScraper { .first() .text() .trim(), + 10, ); const chapters = loadedCheerio('.chapter-list li') @@ -163,16 +164,16 @@ class ReadwnScraper { .text() .trim(); - if (!chapterName || !chapterUrl) return null; - - return { - chapterName, - releaseDate: parseMadaraDate(releaseDate), - chapterUrl: this.baseUrl + chapterUrl, - }; + if (chapterUrl) { + return { + chapterName, + releaseDate: parseMadaraDate(releaseDate), + chapterUrl: this.baseUrl + chapterUrl, + }; + } }) .get() - .filter(chapter => chapter); + .filter(chapter => chapter?.chapterName); if (latestChapterNo > chapters.length) { const lastChapterNo = parseInt( @@ -195,7 +196,7 @@ class ReadwnScraper { } async parseChapter(novelUrl, chapterUrl) { - const body = await fetchHtml({ url: chapterUrl, sourceId }); + const body = await fetchHtml({ url: chapterUrl, sourceId: this.sourceId }); const loadedCheerio = cheerio.load(body); const chapterName = loadedCheerio('.titles > h2').text(); @@ -215,6 +216,7 @@ class ReadwnScraper { async searchNovels(searchTerm) { const body = await fetchHtml({ url: this.baseUrl + 'e/search/index.php', + sourceId: this.baseUrl, init: { headers: { 'Content-Type': 'application/x-www-form-urlencoded', From 17a8e96c6aef6ae81191ccc6c2a1fd198e8fb8d2 Mon Sep 17 00:00:00 2001 From: Rider21 <58046032+Rider21@users.noreply.github.com> Date: Tue, 9 Jan 2024 15:32:06 +0300 Subject: [PATCH 11/12] Update ReadwnScraper.js --- src/sources/multisrc/readwn/ReadwnScraper.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sources/multisrc/readwn/ReadwnScraper.js b/src/sources/multisrc/readwn/ReadwnScraper.js index b1451c15f..ac8632a31 100644 --- a/src/sources/multisrc/readwn/ReadwnScraper.js +++ b/src/sources/multisrc/readwn/ReadwnScraper.js @@ -216,7 +216,7 @@ class ReadwnScraper { async searchNovels(searchTerm) { const body = await fetchHtml({ url: this.baseUrl + 'e/search/index.php', - sourceId: this.baseUrl, + sourceId: this.sourceId, init: { headers: { 'Content-Type': 'application/x-www-form-urlencoded', From e20bfe1d2068fc5dccd2dae45522b4e072057a09 Mon Sep 17 00:00:00 2001 From: Rider21 <58046032+Rider21@users.noreply.github.com> Date: Sun, 14 Jan 2024 21:42:58 +0300 Subject: [PATCH 12/12] deleted the build fix --- COMMON_ERRORS.md | 13 ++ CONTRIBUTING.md | 6 +- package-lock.json | 217 ------------------ package.json | 2 - patches/expo-modules-core+0.11.10.patch | 13 -- patches/react-native+0.69.9.patch | 13 -- patches/react-native-mmkv+2.5.1.patch | 13 -- src/components/ListView.js | 4 +- src/components/NovelCover.tsx | 5 +- src/hooks/useSourceStorage.ts | 64 ------ src/redux/settings/settings.reducer.js | 1 + .../components/GlobalSearchNovelItem.tsx | 5 +- src/screens/WebviewScreen/WebviewScreen.tsx | 19 +- .../components/HistoryCard/HistoryCard.tsx | 4 - .../novel/components/Info/NovelInfoHeader.js | 5 - src/screens/reader/ReaderScreen.js | 44 +++- .../ReaderBottomSheet/ReaderBottomSheet.tsx | 69 +++--- .../{ReaderSeekBar.js => ReaderSeekBar.tsx} | 34 ++- .../reader/components/WebViewReader.tsx | 191 ++++++--------- .../components/stringCreators/createStyle.ts | 167 ++++++++++++++ .../stringCreators/createSwipeGestures.ts | 18 ++ .../stringCreators/horizontalReaderPages.ts | 71 ++++++ .../settings/SettingsAdvancedScreen.js | 7 +- .../Settings/GeneralSettings.tsx | 7 + .../updates/components/UpdateNovelCard.tsx | 6 +- src/sources/helpers/cloudflareImagesBypass.ts | 5 +- src/utils/fetch/fetch.ts | 12 - strings/languages/en/strings.json | 1 + strings/types/index.ts | 1 + 29 files changed, 474 insertions(+), 543 deletions(-) create mode 100644 COMMON_ERRORS.md delete mode 100644 patches/expo-modules-core+0.11.10.patch delete mode 100644 patches/react-native+0.69.9.patch delete mode 100644 patches/react-native-mmkv+2.5.1.patch delete mode 100644 src/hooks/useSourceStorage.ts rename src/screens/reader/components/{ReaderSeekBar.js => ReaderSeekBar.tsx} (79%) create mode 100644 src/screens/reader/components/stringCreators/createStyle.ts create mode 100644 src/screens/reader/components/stringCreators/createSwipeGestures.ts create mode 100644 src/screens/reader/components/stringCreators/horizontalReaderPages.ts diff --git a/COMMON_ERRORS.md b/COMMON_ERRORS.md new file mode 100644 index 000000000..2d0ac12ab --- /dev/null +++ b/COMMON_ERRORS.md @@ -0,0 +1,13 @@ +# Common Problems with First Setup + +### SDK Location not found. Define location with an ANDROID_SDK_ROOT environment variable. (expo modules error) + +If you're on windows, you'll need to edit your system (system wide)/user (just you) environment variables. You'll want to add an environment variable named **ANDROID_SDK_ROOT** (legacy env variable name) and **ANDROID_HOME** that points to your android studio's sdk location. It'll most likely look something like this: + +C:\Users\\**Username**\AppData\Local\Android\Sdk (copy paste this into the box) + +Delete your node_modules folder, invalidate caches and restart in android studio and restart your machine. Follow the steps again in the [setup section](./CONTRIBUTING.md#setup). + +### '.' is not recognized as an internal or external command, operable program or batch file. (npm run buildRelease error) + +Run this line in the terminal instead: ```cd android ; ./gradlew clean ; ./gradlew assembleRelease``` diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d03b85fd2..b9b9929f3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -26,6 +26,10 @@ npm install npm run buildRelease ``` +### Common Problems with First Setup + +Please refer to this [file](./COMMON_ERRORS.md) + ### Developing on Android You will need an Android device or emulator connected to your computer as well as an IDE of your choice. (eg: vscode) @@ -57,4 +61,4 @@ codebase, however you can always check to see if the source code is compliant by ```bash npm run lint -``` +``` \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index b091e991e..825c01619 100644 --- a/package-lock.json +++ b/package-lock.json @@ -87,7 +87,6 @@ "eslint-plugin-react-native": "^4.0.0", "husky": "^7.0.4", "lint-staged": "^12.3.7", - "patch-package": "^8.0.0", "prettier": "^2.8.8", "typescript": "^4.6.3" } @@ -9640,19 +9639,6 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -11678,36 +11664,12 @@ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, - "node_modules/json-stable-stringify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.1.0.tgz", - "integrity": "sha512-zfA+5SuwYN2VWqN1/5HZaDzQKLJHaBVMZIIM+wuYjdptkaQsqzDdqjqf+lZZJUuJq1aanHiY8LhH8LmH+qBYJA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.5", - "isarray": "^2.0.5", - "jsonify": "^0.0.1", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, - "node_modules/json-stable-stringify/node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true - }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -11727,15 +11689,6 @@ "graceful-fs": "^4.1.6" } }, - "node_modules/jsonify": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz", - "integrity": "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/jsx-ast-utils": { "version": "3.3.4", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.4.tgz", @@ -14107,176 +14060,6 @@ "which": "bin/which" } }, - "node_modules/patch-package": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-8.0.0.tgz", - "integrity": "sha512-da8BVIhzjtgScwDJ2TtKsfT5JFWz1hYoBl9rUQ1f38MC2HwnEIkK8VN3dKMKcP7P7bvvgzNDbfNHtx3MsQb5vA==", - "dev": true, - "dependencies": { - "@yarnpkg/lockfile": "^1.1.0", - "chalk": "^4.1.2", - "ci-info": "^3.7.0", - "cross-spawn": "^7.0.3", - "find-yarn-workspace-root": "^2.0.0", - "fs-extra": "^9.0.0", - "json-stable-stringify": "^1.0.2", - "klaw-sync": "^6.0.0", - "minimist": "^1.2.6", - "open": "^7.4.2", - "rimraf": "^2.6.3", - "semver": "^7.5.3", - "slash": "^2.0.0", - "tmp": "^0.0.33", - "yaml": "^2.2.2" - }, - "bin": { - "patch-package": "index.js" - }, - "engines": { - "node": ">=14", - "npm": ">5" - } - }, - "node_modules/patch-package/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/patch-package/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/patch-package/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/patch-package/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/patch-package/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/patch-package/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/patch-package/node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/patch-package/node_modules/open": { - "version": "7.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", - "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", - "dev": true, - "dependencies": { - "is-docker": "^2.0.0", - "is-wsl": "^2.1.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/patch-package/node_modules/slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/patch-package/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/patch-package/node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/patch-package/node_modules/yaml": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz", - "integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==", - "dev": true, - "engines": { - "node": ">= 14" - } - }, "node_modules/path-browserify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", diff --git a/package.json b/package.json index a94b96b26..71858976a 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,6 @@ "sources:madara": "node scripts/generateMadaraSource.cjs", "source:generator": "node scripts/generateSource.cjs", "strings": "node scripts/stringTypes.cjs", - "postinstall": "patch-package", "prepare": "husky install" }, "dependencies": { @@ -96,7 +95,6 @@ "eslint-plugin-react-native": "^4.0.0", "husky": "^7.0.4", "lint-staged": "^12.3.7", - "patch-package": "^8.0.0", "prettier": "^2.8.8", "typescript": "^4.6.3" }, diff --git a/patches/expo-modules-core+0.11.10.patch b/patches/expo-modules-core+0.11.10.patch deleted file mode 100644 index 135a3e64a..000000000 --- a/patches/expo-modules-core+0.11.10.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/node_modules/expo-modules-core/android/build.gradle b/node_modules/expo-modules-core/android/build.gradle -index ae4ff33..9a95164 100644 ---- a/node_modules/expo-modules-core/android/build.gradle -+++ b/node_modules/expo-modules-core/android/build.gradle -@@ -375,7 +375,7 @@ task extractJNIFiles { - // BOOST - task downloadBoost(dependsOn: createNativeDepsDirectories, type: Download) { - def srcUrl = REACT_NATIVE_TARGET_VERSION >= 69 -- ? "https://boostorg.jfrog.io/artifactory/main/release/${BOOST_VERSION.replace("_", ".")}/source/boost_${BOOST_VERSION}.tar.gz" -+ ? "https://sourceforge.net/projects/boost/files/boost/${BOOST_VERSION.replace("_", ".")}/boost_${BOOST_VERSION}.tar.gz" - : "https://github.com/react-native-community/boost-for-react-native/releases/download/v${BOOST_VERSION.replace("_", ".")}-0/boost_${BOOST_VERSION}.tar.gz" - src(srcUrl) - onlyIfNewer(true) diff --git a/patches/react-native+0.69.9.patch b/patches/react-native+0.69.9.patch deleted file mode 100644 index 394553ade..000000000 --- a/patches/react-native+0.69.9.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/node_modules/react-native/third-party-podspecs/boost.podspec b/node_modules/react-native/third-party-podspecs/boost.podspec -index 2f1fcc4..fa82134 100644 ---- a/node_modules/react-native/third-party-podspecs/boost.podspec -+++ b/node_modules/react-native/third-party-podspecs/boost.podspec -@@ -10,7 +10,7 @@ Pod::Spec.new do |spec| - spec.homepage = 'http://www.boost.org' - spec.summary = 'Boost provides free peer-reviewed portable C++ source libraries.' - spec.authors = 'Rene Rivera' -- spec.source = { :http => 'https://boostorg.jfrog.io/artifactory/main/release/1.76.0/source/boost_1_76_0.tar.bz2', -+ spec.source = { :http => 'https://archives.boost.io/release/1.76.0/source/boost_1_76_0.tar.bz2', - :sha256 => 'f0397ba6e982c4450f27bf32a2a83292aba035b827a5623a14636ea583318c41' } - - # Pinning to the same version as React.podspec. diff --git a/patches/react-native-mmkv+2.5.1.patch b/patches/react-native-mmkv+2.5.1.patch deleted file mode 100644 index 56c4be0fb..000000000 --- a/patches/react-native-mmkv+2.5.1.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/node_modules/react-native-mmkv/android/build.gradle b/node_modules/react-native-mmkv/android/build.gradle -index 13eae76..98b2a12 100644 ---- a/node_modules/react-native-mmkv/android/build.gradle -+++ b/node_modules/react-native-mmkv/android/build.gradle -@@ -204,7 +204,7 @@ task createNativeDepsDirectories { - - task downloadBoost(dependsOn: createNativeDepsDirectories, type: Download) { - def transformedVersion = BOOST_VERSION.replace("_", ".") -- def srcUrl = "https://boostorg.jfrog.io/artifactory/main/release/${transformedVersion}/source/boost_${BOOST_VERSION}.tar.gz" -+ def srcUrl = "https://sourceforge.net/projects/boost/files/boost/${transformedVersion}/boost_${BOOST_VERSION}.tar.gz" - if (REACT_NATIVE_VERSION < 69) { - srcUrl = "https://github.com/react-native-community/boost-for-react-native/releases/download/v${transformedVersion}-0/boost_${BOOST_VERSION}.tar.gz" - } diff --git a/src/components/ListView.js b/src/components/ListView.js index dcc6acf36..743ee39b5 100644 --- a/src/components/ListView.js +++ b/src/components/ListView.js @@ -4,7 +4,6 @@ import { StyleSheet, View, Text, Pressable } from 'react-native'; import FastImage from 'react-native-fast-image'; import { coverPlaceholderColor } from '../theme/colors'; -import { getSourceStorage } from '@hooks/useSourceStorage'; import { defaultUserAgentString } from '@utils/fetch/fetch'; import color from 'color'; @@ -19,7 +18,6 @@ const ListView = ({ isSelected, onLongPress, }) => { - const { cookies = '' } = getSourceStorage(item.sourceId); return ( diff --git a/src/components/NovelCover.tsx b/src/components/NovelCover.tsx index 6da1b7d45..bfaa344b6 100644 --- a/src/components/NovelCover.tsx +++ b/src/components/NovelCover.tsx @@ -20,7 +20,6 @@ import { SourceNovelItem } from 'src/sources/types'; import { ThemeColors } from '@theme/types'; import SourceScreenSkeletonLoading from '@screens/browse/loadingAnimation/SourceScreenSkeletonLoading'; -import { getSourceStorage } from '@hooks/useSourceStorage'; import { defaultUserAgentString } from '@utils/fetch/fetch'; interface NovelCoverProps { @@ -68,8 +67,6 @@ const NovelCover: React.FC = ({ const uri = item.novelCover; - const { cookies = '' } = getSourceStorage(item.sourceId); - return item.sourceId < 0 ? ( ) : displayMode !== DisplayModes.List ? ( @@ -115,7 +112,7 @@ const NovelCover: React.FC = ({ >; - -const useSourceStorage = ({ sourceId = -1 }: { sourceId?: number }) => { - const [values, setValues] = useMMKVObject( - SOURCE_STORAGE, - MMKVStorage, - ); - - const setSourceStorage = (key: keyof SourceStorage, value: any): void => { - setValues({ - ...values, - [sourceId]: { - ...values?.[sourceId], - [key]: value, - }, - }); - }; - - const clearCookies = (): void => { - // let tempStorage = {}; - - // if (values) { - // tempStorage = Object.fromEntries( - // Object.entries(values).map(([id, sourceData]) => { - // delete sourceData.cookies; - - // return [id, sourceData]; - // }), - // ); - // } - CookieManager.clearAll(); - CookieManager.clearAll(true); //clears cookies in webkit - setValues({}); - showToast('Cookies cleared'); - }; - - const sourceStorage = values?.[sourceId]; - - return { - ...sourceStorage, - setSourceStorage, - clearCookies, - }; -}; - -export default useSourceStorage; - -export const getSourceStorage = (sourceId: number) => { - const rawSettings = MMKVStorage.getString(SOURCE_STORAGE) || '{}'; - const parsedSettings: Partial = JSON.parse(rawSettings); - - return parsedSettings[sourceId] || {}; -}; diff --git a/src/redux/settings/settings.reducer.js b/src/redux/settings/settings.reducer.js index b8057ab25..7fade68ef 100644 --- a/src/redux/settings/settings.reducer.js +++ b/src/redux/settings/settings.reducer.js @@ -67,6 +67,7 @@ export const initialState = { fullScreenMode: true, swipeGestures: false, + readerPages: false, showScrollPercentage: true, useVolumeButtons: false, scrollAmount: Math.round(Dimensions.get('window').height), diff --git a/src/screens/GlobalSearchScreen/components/GlobalSearchNovelItem.tsx b/src/screens/GlobalSearchScreen/components/GlobalSearchNovelItem.tsx index 06001154b..af0a0b2c9 100644 --- a/src/screens/GlobalSearchScreen/components/GlobalSearchNovelItem.tsx +++ b/src/screens/GlobalSearchScreen/components/GlobalSearchNovelItem.tsx @@ -8,7 +8,6 @@ import { SourceNovelItem } from '../../../sources/types'; import { ThemeColors } from '../../../theme/types'; import { getString } from '@strings/translations'; -import { getSourceStorage } from '@hooks/useSourceStorage'; import { defaultUserAgentString } from '@utils/fetch/fetch'; interface Props { @@ -35,8 +34,6 @@ const GlobalSearchNovelItem: React.FC = ({ [inLibrary], ); - const { cookies = '' } = getSourceStorage(novel.sourceId); - return ( = ({ diff --git a/src/screens/WebviewScreen/WebviewScreen.tsx b/src/screens/WebviewScreen/WebviewScreen.tsx index 7bf27044b..939e2b894 100644 --- a/src/screens/WebviewScreen/WebviewScreen.tsx +++ b/src/screens/WebviewScreen/WebviewScreen.tsx @@ -1,11 +1,10 @@ -import React, { useEffect } from 'react'; +import React from 'react'; import { RouteProp, useNavigation, useRoute } from '@react-navigation/native'; import WebView from 'react-native-webview'; import CookieManager from '@react-native-cookies/cookies'; import { Appbar } from '@components'; import { useTheme } from '@hooks/useTheme'; -import useSourceStorage from '@hooks/useSourceStorage'; import { defaultUserAgentString } from '@utils/fetch/fetch'; type ReaderScreenRouteProps = RouteProp<{ @@ -21,19 +20,12 @@ const WebviewScreen = () => { const { goBack } = useNavigation(); const { - params: { name, sourceId, url }, + params: { name, url }, } = useRoute(); - const { setSourceStorage } = useSourceStorage({ sourceId }); - useEffect(() => { - CookieManager.get(url, true).then(cookies => { - const cloudflareCookie = cookies?.cf_clearance; - if (cloudflareCookie) { - const cloudflareCookieString = `${cloudflareCookie.name}=${cloudflareCookie.value}`; - setSourceStorage('cookies', cloudflareCookieString); - } - }); - }, []); + const syncCookies = () => { + CookieManager.flush(); + }; return ( <> @@ -42,6 +34,7 @@ const WebviewScreen = () => { startInLoadingState userAgent={defaultUserAgentString} source={{ uri: url }} + onNavigationStateChange={syncCookies} /> ); diff --git a/src/screens/history/components/HistoryCard/HistoryCard.tsx b/src/screens/history/components/HistoryCard/HistoryCard.tsx index fc0c9e056..62d3b80eb 100644 --- a/src/screens/history/components/HistoryCard/HistoryCard.tsx +++ b/src/screens/history/components/HistoryCard/HistoryCard.tsx @@ -16,7 +16,6 @@ import { NovelScreenRouteParams, } from '@utils/NavigationUtils'; -import { getSourceStorage } from '@hooks/useSourceStorage'; import { defaultUserAgentString } from '@utils/fetch/fetch'; interface HistoryCardProps { @@ -59,8 +58,6 @@ const HistoryCard: React.FC = ({ [chapterName, historyTimeRead], ); - const { cookies = '' } = getSourceStorage(sourceId); - return ( = ({ source={{ uri: novelCover, headers: { - Cookie: cookies, 'User-Agent': defaultUserAgentString, }, }} diff --git a/src/screens/novel/components/Info/NovelInfoHeader.js b/src/screens/novel/components/Info/NovelInfoHeader.js index 8a48f4691..8ff48a677 100644 --- a/src/screens/novel/components/Info/NovelInfoHeader.js +++ b/src/screens/novel/components/Info/NovelInfoHeader.js @@ -11,7 +11,6 @@ import { followNovelAction } from '../../../../redux/novel/novel.actions'; import { useSettings } from '../../../../hooks/reduxHooks'; import { showToast } from '../../../../hooks/showToast'; -import { getSourceStorage } from '@hooks/useSourceStorage'; import { defaultUserAgentString } from '@utils/fetch/fetch'; import { @@ -57,15 +56,12 @@ const NovelInfoHeader = ({ [], ); - const { cookies = '' } = getSourceStorage(novel.sourceId); - return ( <> { const { swipeGestures = false, + readerPages = false, useVolumeButtons = false, autoScroll = false, autoScrollInterval = 10, @@ -105,6 +106,7 @@ const ChapterContent = ({ route, navigation }) => { const position = usePosition(novelId, chapterId); const minScroll = useRef(0); + const pages = useRef(0); const { tracker, trackedNovels } = useTrackerReducer(); const isTracked = trackedNovels.find(obj => obj.novelId === novelId); @@ -214,13 +216,41 @@ const ChapterContent = ({ route, navigation }) => { updateTracker(); } }); - + useEffect(() => { + if (!readerPages) { + webViewRef?.current?.injectJavaScript( + "document.querySelector('chapter').style.transform = 'translate(0%)';", + ); + } + }, [readerPages]); const scrollTo = useCallback( - offsetY => { + offset => { requestAnimationFrame(() => { - webViewRef?.current?.injectJavaScript(`(()=>{ - window.scrollTo({top:${offsetY},behavior:'smooth'}) - })()`); + webViewRef?.current?.injectJavaScript( + !readerPages + ? `(()=>{ + window.scrollTo({top:${offset},behavior:'smooth'}) + })()` + : `(()=>{ + document.querySelector('chapter').setAttribute('data-page', ${ + offset / 100 + }); + document.querySelector("chapter").style.transform = 'translate(-${offset}%)'; + window.ReactNativeWebView.postMessage( + JSON.stringify( + { + type:"scrollend", + data:{ + offSetY: ${offset}, + percentage: (${offset / 100 / pages.current} > 0 ? ${ + offset / 100 / pages.current + } * 100 : 1), + } + } + ) + ); + })()`, + ); }); }, [webViewRef], @@ -395,7 +425,9 @@ const ChapterContent = ({ route, navigation }) => { html={chapterText} chapterName={chapter.chapterName || chapterName} swipeGestures={swipeGestures} + readerPages={readerPages} minScroll={minScroll} + pages={pages} nextChapter={nextChapter} webViewRef={webViewRef} onLayout={onLayout} @@ -425,7 +457,9 @@ const ChapterContent = ({ route, navigation }) => { /> { return ( @@ -50,13 +52,14 @@ const GeneralTab: React.FC = () => { useVolumeButtons = false, scrollAmount = 200, swipeGestures = false, + readerPages = false, removeExtraParagraphSpacing = false, addChapterNameInReader = false, bionicReading = false, } = useSettingsV1(); return ( - + @@ -105,6 +108,12 @@ const GeneralTab: React.FC = () => { value={swipeGestures} theme={theme} /> + dispatch(setAppSettings('readerPages', !readerPages))} + value={readerPages} + theme={theme} + /> @@ -161,7 +170,7 @@ const GeneralTab: React.FC = () => { value={bionicReading} theme={theme} /> - + ); }; interface ReaderBottomSheetV2Props { @@ -198,31 +207,37 @@ const ReaderBottomSheetV2: React.FC = ({ [], ); - const renderTabBar = (props: any) => ( - ( - {route.title} - )} - inactiveColor={theme.onSurfaceVariant} - activeColor={theme.primary} - pressColor={color(theme.primary).alpha(0.12).string()} - /> - ); - + const renderTabBar = (props: any) => { + return ( + ( + {route.title} + )} + inactiveColor={theme.onSurfaceVariant} + activeColor={theme.primary} + pressColor={color(theme.primary).alpha(0.12).string()} + /> + ); + }; + const { bottom } = useSafeAreaInsets(); return ( - = ({ initialLayout={{ width: layout.width }} style={styles.tabView} /> - + ); }; diff --git a/src/screens/reader/components/ReaderSeekBar.js b/src/screens/reader/components/ReaderSeekBar.tsx similarity index 79% rename from src/screens/reader/components/ReaderSeekBar.js rename to src/screens/reader/components/ReaderSeekBar.tsx index 3349ab0e8..5923e472e 100644 --- a/src/screens/reader/components/ReaderSeekBar.js +++ b/src/screens/reader/components/ReaderSeekBar.tsx @@ -5,19 +5,39 @@ import color from 'color'; import Slider from '@react-native-community/slider'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { useDeviceOrientation } from '@hooks/useDeviceOrientation'; +import { ThemeColors } from '@theme/types'; -const VerticalScrollbar = ({ +interface IScrollbar { + theme: ThemeColors; + minScroll: number; + pages: number; + verticalSeekbar: boolean; + readerPages: boolean; + percentage?: number; + scrollTo: (offset: number) => void; +} + +const VerticalScrollbar: React.FC = ({ theme, minScroll = 0, + pages, verticalSeekbar, + readerPages, percentage, scrollTo, }) => { const { bottom } = useSafeAreaInsets(); - const onSlidingComplete = value => { - let offsetY = - ((value - minScroll) * Dimensions.get('window').height) / minScroll; - scrollTo(offsetY); + const onSlidingComplete = (value: number) => { + let offset; + if (readerPages) { + let newPage = Math.round(pages * (value / 100)) * 100; + // pages = { ...pages, current: newPage }; + offset = newPage; + } else { + offset = + ((value - minScroll) * Dimensions.get('window').height) / minScroll; + } + scrollTo(offset); }; const screenOrientation = useDeviceOrientation(); @@ -43,7 +63,7 @@ const VerticalScrollbar = ({ flex: 1, height: 40, }} - minimumValue={Math.round(minScroll)} + minimumValue={readerPages ? 1 : Math.round(minScroll)} maximumValue={100} step={1} value={percentage || Math.round(minScroll)} @@ -87,7 +107,7 @@ const VerticalScrollbar = ({ flex: 1, height: 40, }} - minimumValue={Math.round(minScroll)} + minimumValue={readerPages ? 1 : Math.round(minScroll)} maximumValue={100} step={1} value={percentage || Math.round(minScroll)} diff --git a/src/screens/reader/components/WebViewReader.tsx b/src/screens/reader/components/WebViewReader.tsx index 286ea73d3..09a52cc3b 100644 --- a/src/screens/reader/components/WebViewReader.tsx +++ b/src/screens/reader/components/WebViewReader.tsx @@ -10,6 +10,9 @@ import { useReaderSettings, useSettingsV1 } from '@redux/hooks'; import { getString } from '@strings/translations'; import { sourceManager } from '../../../sources/sourceManager'; +import { createHorizontalReaderPages } from './stringCreators/horizontalReaderPages'; +import { createStyles } from './stringCreators/createStyle'; +import { createSwipeGestures } from './stringCreators/createSwipeGestures'; type WebViewPostEvent = { type: string; @@ -30,7 +33,9 @@ type WebViewReaderProps = { html: string; chapterName: string; swipeGestures: boolean; + readerPages: boolean; minScroll: React.MutableRefObject; + pages: React.MutableRefObject; nextChapter: ChapterItem; webViewRef: React.MutableRefObject; onPress(): void; @@ -52,9 +57,11 @@ const WebViewReader: React.FC = props => { html, chapterName, swipeGestures, + readerPages, minScroll, nextChapter, webViewRef, + pages, onPress, onLayout, doSaveProgress, @@ -112,6 +119,7 @@ const WebViewReader: React.FC = props => { if (event.data) { const offSetY = Number(event.data?.offSetY); const percentage = Math.round(Number(event.data?.percentage)); + doSaveProgress(offSetY, percentage); } break; @@ -121,6 +129,11 @@ const WebViewReader: React.FC = props => { minScroll.current = (layoutHeight / contentHeight) * 100; } break; + case 'pages': + if (event.data) { + pages.current = Number(event.data); + } + break; } }} source={{ @@ -128,85 +141,13 @@ const WebViewReader: React.FC = props => { - + ${createStyles( + StatusBar.currentHeight ?? 0, + readerSettings, + theme, + layoutHeight, + readerPages, + )} -
+
+
+ +
@@ -233,7 +186,29 @@ const WebViewReader: React.FC = props => { : '' } ${html} +

+
+
+
+ ${getString( + 'readerScreen.finished', + )}: ${chapterName?.trim()} +
+ ${ + nextChapter + ? `` + : `
${getString( + 'readerScreen.noNextChapter', + )}
` + } +
+
-
- ${getString( - 'readerScreen.finished', - )}: ${chapterName?.trim()} -
- ${ - nextChapter - ? `` - : `
${getString( - 'readerScreen.noNextChapter', - )}
` - } - ${ - swipeGestures - ? ` - ` - : '' - } - + + ${swipeGestures ? createSwipeGestures() : ''} @@ -328,5 +268,4 @@ const WebViewReader: React.FC = props => { /> ); }; - export default React.memo(WebViewReader, isEqual); diff --git a/src/screens/reader/components/stringCreators/createStyle.ts b/src/screens/reader/components/stringCreators/createStyle.ts new file mode 100644 index 000000000..3783cdf04 --- /dev/null +++ b/src/screens/reader/components/stringCreators/createStyle.ts @@ -0,0 +1,167 @@ +import { ThemeColors } from '@theme/types'; + +export const createStyles = ( + currentHeight: number, + readerSettings: { + theme: string; + textColor: string; + textSize: number; + textAlign: string; + padding: number; + fontFamily: string; + lineHeight: number; + customCSS: string; + customJS: string; + }, + theme: ThemeColors, + layoutHeight: number, + readerPages: boolean, +) => ` +`; diff --git a/src/screens/reader/components/stringCreators/createSwipeGestures.ts b/src/screens/reader/components/stringCreators/createSwipeGestures.ts new file mode 100644 index 000000000..cd9d2c3ba --- /dev/null +++ b/src/screens/reader/components/stringCreators/createSwipeGestures.ts @@ -0,0 +1,18 @@ +export function createSwipeGestures() { + return ``; +} diff --git a/src/screens/reader/components/stringCreators/horizontalReaderPages.ts b/src/screens/reader/components/stringCreators/horizontalReaderPages.ts new file mode 100644 index 000000000..e34ebb9b3 --- /dev/null +++ b/src/screens/reader/components/stringCreators/horizontalReaderPages.ts @@ -0,0 +1,71 @@ +export const createHorizontalReaderPages = () => { + return ` +const id = (key) => { + return document.getElementById(key) +} +const select = (key) => { + return document.querySelector(key) +} + +const chapter = select("chapter"); +const clientWidth = document.documentElement.clientWidth; +const textWidth = chapter.scrollWidth; +const navLeft = id("left"); +const navRight = id("right"); +const infoBox = id("infoContainer"); +infoBox.classList.add("hidden"); +id("spacer").style.height = infoBox.scrollHeight + 'px'; + +const pages = (Math.ceil(textWidth / clientWidth) - 1); +let page = 0; + + +navRight.addEventListener("click", () => { + page = select("chapter").getAttribute('data-page'); + if (isNaN(page)) { + page = 0; + } + if (page < pages ) { + page++; + movePage(); + if (page === pages){ + infoBox.classList.add("show") + } + } +}); +navLeft.addEventListener("click", () => { + page = select("chapter").getAttribute('data-page'); + if (page > 0) { + infoBox.classList.remove("show") + page--; + movePage(); + } +}) +function movePage(){ + chapter.style.transform = 'translate(-'+page*100+'%)'; + select('chapter').setAttribute('data-page', + page + ); + window.ReactNativeWebView.postMessage( + JSON.stringify( + { + type:"scrollend", + data:{ + offSetY: page * 100, percentage: page === 0 ? 1 : page / pages * 100, + } + } + ) + ); +} +let sendWidthTimeout; +const sendPages = (timeOut) => { + clearTimeout(sendHeightTimeout); + sendHeightTimeout = setTimeout( + window.ReactNativeWebView.postMessage( + JSON.stringify({type:"pages",data: pages}) + ), timeOut + ); +} +sendPages(200); +;`; +}; diff --git a/src/screens/settings/SettingsAdvancedScreen.js b/src/screens/settings/SettingsAdvancedScreen.js index 9fb20952e..172a7eda1 100644 --- a/src/screens/settings/SettingsAdvancedScreen.js +++ b/src/screens/settings/SettingsAdvancedScreen.js @@ -16,11 +16,14 @@ import { openDirectory } from '../../native/epubParser'; import { Appbar, Button, List } from '@components'; import { ScreenContainer } from '@components/Common'; -import useSourceStorage from '@hooks/useSourceStorage'; +import CookieManager from '@react-native-cookies/cookies'; const AdvancedSettings = ({ navigation }) => { const theme = useTheme(); - const { clearCookies } = useSourceStorage({}); + const clearCookies = () => { + CookieManager.clearAll(); + showToast('Cookies cleared'); + }; /** * Confirm Clear Database Dialog diff --git a/src/screens/settings/SettingsReaderScreen/Settings/GeneralSettings.tsx b/src/screens/settings/SettingsReaderScreen/Settings/GeneralSettings.tsx index c49650e99..a6b2e8ada 100644 --- a/src/screens/settings/SettingsReaderScreen/Settings/GeneralSettings.tsx +++ b/src/screens/settings/SettingsReaderScreen/Settings/GeneralSettings.tsx @@ -24,6 +24,7 @@ const GeneralSettings: React.FC = () => { useVolumeButtons = false, verticalSeekbar = true, swipeGestures = false, + readerPages = false, autoScroll = false, autoScrollInterval = 10, autoScrollOffset = null, @@ -76,6 +77,12 @@ const GeneralSettings: React.FC = () => { } theme={theme} /> + dispatch(setAppSettings('readerPages', !readerPages))} + theme={theme} + /> void; sourceId: number; }) => { - const { cookies = '' } = getSourceStorage(sourceId); return ( diff --git a/src/sources/helpers/cloudflareImagesBypass.ts b/src/sources/helpers/cloudflareImagesBypass.ts index b382153fa..97342fb31 100644 --- a/src/sources/helpers/cloudflareImagesBypass.ts +++ b/src/sources/helpers/cloudflareImagesBypass.ts @@ -1,13 +1,10 @@ -import { getSourceStorage } from '@hooks/useSourceStorage'; import { defaultUserAgentString, fetchApi } from '@utils/fetch/fetch'; import { Cheerio, CheerioAPI, Element } from 'cheerio'; export const bypassImages = async ( loadedCheerio: CheerioAPI, element: Cheerio, - sourceId: number, ): Promise => { - const { cookies = '' } = getSourceStorage(sourceId); let promises: Promise[] = []; element.find('noscript').each(function () { @@ -28,7 +25,7 @@ export const bypassImages = async ( const response = await fetchApi({ url: attr.value, init: { - headers: { Cookie: cookies, 'User-Agent': defaultUserAgentString }, + headers: { 'User-Agent': defaultUserAgentString }, }, }); diff --git a/src/utils/fetch/fetch.ts b/src/utils/fetch/fetch.ts index fa24ada92..ec470c528 100644 --- a/src/utils/fetch/fetch.ts +++ b/src/utils/fetch/fetch.ts @@ -1,12 +1,9 @@ -import { getSourceStorage } from '@hooks/useSourceStorage'; - export const defaultUserAgentString = 'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Mobile Safari/537.36'; interface FetchParams { url: string; // URL of request init?: RequestInit; // Variable for passing headers and other information - sourceId?: number; // ID number of source for cookies } // Checks if we bypassed cloudflare. If we failed to bypass, throw error. @@ -27,7 +24,6 @@ export const cloudflareCheck = (text: string) => { export const fetchApi = async ({ url, init, - sourceId, }: FetchParams): Promise => { let headers = new Headers({ 'User-Agent': defaultUserAgentString, @@ -38,14 +34,6 @@ export const fetchApi = async ({ // You can have NO user agent by doing this: // init: { headers: { 'User-Agent': undefined } }, - if (sourceId) { - const { cookies = '' } = getSourceStorage(sourceId); - - if (cookies) { - headers.append('cookie', cookies); - } - } - return fetch(url, { ...init, headers }); }; diff --git a/strings/languages/en/strings.json b/strings/languages/en/strings.json index 39f1bdf4d..ddffe85cd 100644 --- a/strings/languages/en/strings.json +++ b/strings/languages/en/strings.json @@ -95,6 +95,7 @@ "showBatteryAndTime": "Show battery and time", "showProgressPercentage": "Show progress percentage", "swipeGestures": "Swipe left or right to navigate between chapters", + "readerPages": "Tap left or right to navigate through pages in the reader (Experimental)", "allowTextSelection": "Allow text selection", "useChapterDrawerSwipeNavigation": "Swipe right to open drawer", "removeExtraSpacing": "Remove extra paragraph spacing", diff --git a/strings/types/index.ts b/strings/types/index.ts index 44f0f924f..e656abb5c 100644 --- a/strings/types/index.ts +++ b/strings/types/index.ts @@ -79,6 +79,7 @@ export interface StringMap { 'readerScreen.bottomSheet.showBatteryAndTime': 'string'; 'readerScreen.bottomSheet.showProgressPercentage': 'string'; 'readerScreen.bottomSheet.swipeGestures': 'string'; + 'readerScreen.bottomSheet.readerPages': 'string'; 'readerScreen.bottomSheet.allowTextSelection': 'string'; 'readerScreen.bottomSheet.useChapterDrawerSwipeNavigation': 'string'; 'readerScreen.bottomSheet.removeExtraSpacing': 'string';