From 8e032ad82369d4324f319a6b497dc54a2ee4d0e2 Mon Sep 17 00:00:00 2001 From: Rider21 <58046032+Rider21@users.noreply.github.com> Date: Sat, 4 Jan 2025 09:59:32 +0300 Subject: [PATCH] plugin fix (#1391) * updates * test * test * fix * typo --- scripts/multisrc/rulate/sources.json | 6 +- scripts/multisrc/rulate/template.ts | 138 +++++---- src/plugins/russian/LitSpace.ts | 430 ++++++++++++++------------- src/plugins/russian/jaomix.ts | 7 +- src/plugins/russian/renovels.ts | 56 ++-- 5 files changed, 344 insertions(+), 293 deletions(-) diff --git a/scripts/multisrc/rulate/sources.json b/scripts/multisrc/rulate/sources.json index 7501aa6..15d8922 100644 --- a/scripts/multisrc/rulate/sources.json +++ b/scripts/multisrc/rulate/sources.json @@ -7548,7 +7548,8 @@ ], "type": "CheckboxGroup" } - } + }, + "versionIncrements": 0 }, { "id": "erolate", @@ -10015,6 +10016,7 @@ ], "type": "CheckboxGroup" } - } + }, + "versionIncrements": 1 } ] diff --git a/scripts/multisrc/rulate/template.ts b/scripts/multisrc/rulate/template.ts index 58047f1..b6da103 100644 --- a/scripts/multisrc/rulate/template.ts +++ b/scripts/multisrc/rulate/template.ts @@ -10,6 +10,7 @@ export type RulateMetadata = { sourceSite: string; sourceName: string; filters?: Filters; + versionIncrements: number; }; class RulatePlugin implements Plugin.PluginBase { @@ -25,7 +26,7 @@ class RulatePlugin implements Plugin.PluginBase { this.name = metadata.sourceName; this.icon = `multisrc/rulate/${metadata.id.toLowerCase()}/icon.png`; this.site = metadata.sourceSite; - this.version = '1.0.1'; + this.version = '1.0.' + (1 + metadata.versionIncrements); this.filters = metadata.filters; } @@ -90,89 +91,106 @@ class RulatePlugin implements Plugin.PluginBase { const novel: Plugin.SourceNovel = { path: novelPath, - name: loadedCheerio( - 'div[class="container"] > div > div > h1, div.span8:nth-child(1) > h1:nth-child(2)', - ) - .text() - .trim(), + name: loadedCheerio('.span8 > h1, .book__title').text().trim(), }; if (novel.name?.includes?.('[')) { novel.name = novel.name.split('[')[0].trim(); } novel.cover = - this.site + loadedCheerio('div[class="images"] > div img').attr('src'); - novel.summary = loadedCheerio('#Info > div:nth-child(3), .book-description') + this.site + + loadedCheerio('div[class="images"] > div img, .book__cover > img').attr( + 'src', + ); + novel.summary = loadedCheerio( + '#Info > div:nth-child(3), .book__description', + ) .text() .trim(); - novel.author = loadedCheerio( - '.book-stats-icons_author > span:nth-child(2) > a:nth-child(1)', - ).text(); const genres: string[] = []; - loadedCheerio('div.span5 > p, .span5 > div:nth-child(2) > p').each( - function () { - switch (loadedCheerio(this).find('strong').text()) { - case 'Автор:': - novel.author = loadedCheerio(this).find('em > a').text().trim(); - break; - case 'Выпуск:': - novel.status = - loadedCheerio(this).find('em').text().trim() === 'продолжается' - ? NovelStatus.Ongoing - : NovelStatus.Completed; - break; - case 'Тэги:': - loadedCheerio(this) - .find('em > a') - .each(function () { - genres.push(loadedCheerio(this).text()); - }); - break; - case 'Жанры:': - loadedCheerio(this) - .find('em > a') - .each(function () { - genres.push(loadedCheerio(this).text()); - }); - break; - } - }, - ); + loadedCheerio('div.span5 > p').each(function () { + switch (loadedCheerio(this).find('strong').text()) { + case 'Автор:': + novel.author = loadedCheerio(this).find('em > a').text().trim(); + break; + case 'Выпуск:': + novel.status = + loadedCheerio(this).find('em').text().trim() === 'продолжается' + ? NovelStatus.Ongoing + : NovelStatus.Completed; + break; + case 'Тэги:': + loadedCheerio(this) + .find('em > a') + .each(function () { + genres.push(loadedCheerio(this).text()); + }); + break; + case 'Жанры:': + loadedCheerio(this) + .find('em > a') + .each(function () { + genres.push(loadedCheerio(this).text()); + }); + break; + } + }); if (genres.length) { novel.genres = genres.reverse().join(','); } const chapters: Plugin.ChapterItem[] = []; - loadedCheerio('table > tbody > tr.chapter_row').each( - (chapterIndex, element) => { + if (this.id === 'rulate') { + loadedCheerio('table > tbody > tr.chapter_row').each( + (chapterIndex, element) => { + const chapterName = loadedCheerio(element) + .find('td[class="t"] > a') + .text() + .trim(); + const releaseDate = loadedCheerio(element) + .find('td > span') + .attr('title') + ?.trim(); + const chapterUrl = loadedCheerio(element) + .find('td[class="t"] > a') + .attr('href'); + + if ( + !loadedCheerio(element).find('td > span[class="disabled"]') + .length && + releaseDate && + chapterUrl + ) { + chapters.push({ + name: chapterName, + path: chapterUrl, + releaseTime: this.parseDate(releaseDate), + chapterNumber: chapterIndex + 1, + }); + } + }, + ); + } else { + loadedCheerio('a.chapter').each((chapterIndex, element) => { const chapterName = loadedCheerio(element) - .find('td[class="t"] > a') + .find('div:nth-child(1) > span:nth-child(2)') .text() .trim(); - const releaseDate = loadedCheerio(element) - .find('td > span') - .attr('title') - ?.trim(); - const chapterUrl = loadedCheerio(element) - .find('td[class="t"] > a') - .attr('href'); + const chapterUrl = loadedCheerio(element).attr('href'); + const isPaid = loadedCheerio(element).find( + 'span[data-can-buy="true"]', + ).length; - if ( - !loadedCheerio(element).find('td > span[class="disabled"]').length && - releaseDate && - chapterUrl - ) { + if (!isPaid && chapterUrl) { chapters.push({ name: chapterName, path: chapterUrl, - releaseTime: this.parseDate(releaseDate), chapterNumber: chapterIndex + 1, }); } - }, - ); - + }); + } novel.chapters = chapters; return novel; } @@ -192,7 +210,7 @@ class RulatePlugin implements Plugin.PluginBase { const body = await result.text(); const loadedCheerio = parseHTML(body); - const chapterText = loadedCheerio('.content-text').html(); + const chapterText = loadedCheerio('.content-text, #read-text').html(); return chapterText || ''; } diff --git a/src/plugins/russian/LitSpace.ts b/src/plugins/russian/LitSpace.ts index d01a7b4..49e53ec 100644 --- a/src/plugins/russian/LitSpace.ts +++ b/src/plugins/russian/LitSpace.ts @@ -1,14 +1,19 @@ import { Plugin } from '@typings/plugin'; import { FilterTypes, Filters } from '@libs/filterInputs'; +import { defaultCover } from '@libs/defaultCover'; import { fetchApi } from '@libs/fetch'; -import { load as parseHTML } from 'cheerio'; -import dayjs from 'dayjs'; + +const headers: any = { + 'Content-Type': 'application/json', + 'X-Inertia': true, + 'X-Inertia-Version': '6666cd76f96956469e7be39d750cc7d9', +}; class freedlit implements Plugin.PluginBase { id = 'freedlit.space'; name = 'LitSpace'; site = 'https://freedlit.space'; - version = '1.0.0'; + version = '1.1.0'; icon = 'src/ru/freedlit/icon.png'; async popularNovels( @@ -18,136 +23,145 @@ class freedlit implements Plugin.PluginBase { filters, }: Plugin.PopularNovelsOptions, ): Promise { - let url = this.site + '/get-books/all/list/' + page + '?sort='; + let url = this.site + '/get-books/?sort='; url += showLatestNovels ? 'recent' : filters?.sort?.value || 'popular'; - url += '&status=' + (filters?.status?.value || 'all'); url += '&access=' + (filters?.access?.value || 'all'); - url += '&adult=' + (filters?.adult?.value || 'hide'); - - if (filters?.genre?.value?.include?.length) { - url += filters.genre.value.include - .map(id => '&genres_included[]=' + id) - .join(''); - } - - if (filters?.genre?.value?.exclude?.length) { - url += filters.genre.value.exclude - .map(id => '&genres_excluded[]=' + id) - .join(''); - } - - const body = await fetchApi(url).then(res => res.text()); - const loadedCheerio = parseHTML(body); - + url += '&hideAdult=' + (filters?.hideAdult?.value || false); + url += '&page=' + page; const novels: Plugin.NovelItem[] = []; - loadedCheerio('#bookListBlock > div > div').each((index, element) => { - const name = loadedCheerio(element).find('div > h4 > a').text()?.trim(); - const cover = loadedCheerio(element) - .find('div > a > img') - .attr('src') - ?.trim(); - const url = loadedCheerio(element) - .find('div > h4 > a') - .attr('href') - ?.trim(); - if (!name || !url) return; - novels.push({ name, cover, path: url.replace(this.site, '') }); + const { books }: { books: Books } = await fetchApi(url).then(res => { + this.getToken(res.headers); + return res.json(); }); + books.data.forEach(novel => + novels.push({ + name: novel.title, + cover: novel.cover + ? this.site + '/storage/' + novel.cover + : defaultCover, + path: novel.item_id.toString(), + }), + ); + return novels; } async parseNovel(novelPath: string): Promise { - const body = await fetchApi(this.site + novelPath).then(res => res.text()); - const loadedCheerio = parseHTML(body); + const { + props: { book }, + }: { props: { book: DataEntity } } = await fetchApi( + this.resolveUrl(novelPath, true), + { + headers, + Referer: this.resolveUrl(novelPath, true), + }, + ).then(res => { + this.getToken(res.headers); + return res.json(); + }); const novel: Plugin.SourceNovel = { path: novelPath, - name: loadedCheerio('.book-info h4').text(), - cover: loadedCheerio('.book-cover > div > img').attr('src')?.trim(), - summary: loadedCheerio('#nav-home').text()?.trim(), - author: loadedCheerio('.book-info h5 > a').text(), - genres: loadedCheerio('.genre-list > a') - .map((index, element) => loadedCheerio(element).text()) - .get() - .join(','), + name: book.title, + cover: book.cover ? this.site + '/storage/' + book.cover : defaultCover, + summary: book.annotation, + author: book.authors_names?.[0]?.name || '', + genres: book.tags?.map?.(tags => tags.name)?.join?.(', ') || '', }; + const { success }: { success: { items: chaptersData[] } } = await fetchApi( + this.site + '/api/bookpage/get-chapters', + { + method: 'post', + headers, + Referer: this.resolveUrl(novelPath), + body: JSON.stringify({ book_id: novelPath }), + }, + ).then(res => { + this.getToken(res.headers); + return res.json(); + }); const chapters: Plugin.ChapterItem[] = []; - loadedCheerio('a.chapter-line').each((chapterIndex, element) => { - const name = loadedCheerio(element).find('h6').text(); - const url = loadedCheerio(element).attr('href'); - if (!name || !url) return; - - const releaseDate = loadedCheerio(element) - .find('span[class="date"]') - .text(); - chapters.push({ - name, - path: url.replace(this.site, ''), - releaseTime: this.parseDate(releaseDate), - chapterNumber: chapterIndex + 1, - }); - }); + if (success?.items?.length) { + success.items.forEach((chapter, chapterIndex) => + chapters.push({ + name: chapter.header, + path: novelPath + '/' + chapter.id, + releaseTime: chapter.first_published_formated, + chapterNumber: chapterIndex + 1, + }), + ); + } novel.chapters = chapters; return novel; } async parseChapter(chapterPath: string): Promise { - const body = await fetchApi(this.site + chapterPath).then(res => - res.text(), - ); - const loadedCheerio = parseHTML(body); + const [book_id, chapter_id] = chapterPath.split('/'); + if (!headers['X-XSRF-TOKEN']) { + await fetchApi(this.site).then(res => this.getToken(res.headers)); + } - loadedCheerio('div.mobile-block').remove(); - loadedCheerio('div.standart-block').remove(); + const { success }: { success: chapterContent } = await fetchApi( + this.site + '/reader/get-content', + { + method: 'post', + headers, + Referer: this.resolveUrl(chapterPath), + body: JSON.stringify({ book_id, chapter_id }), + }, + ).then(res => { + this.getToken(res.headers); + return res.json(); + }); - const chapterText = loadedCheerio('div[class="chapter"]').html() || ''; + const chapterText = success.content; return chapterText; } async searchNovels(searchTerm: string): Promise { - const url = this.site + '/search?query=' + searchTerm + '&type=all'; - const body = await fetchApi(url).then(res => res.text()); - const loadedCheerio = parseHTML(body); - - const novels: Plugin.NovelItem[] = []; - loadedCheerio('#bookGridBlock > div').each((index, element) => { - const name = loadedCheerio(element).find('h4 > a').text()?.trim(); - const cover = loadedCheerio(element).find('a > img').attr('src')?.trim(); - const url = loadedCheerio(element).find('h4 > a').attr('href')?.trim(); - if (!name || !url) return; - - novels.push({ name, cover, path: url.replace(this.site, '') }); + const { success }: { success: book[] } = await fetchApi( + this.site + '/api/search?query=' + searchTerm, + ).then(res => { + this.getToken(res.headers); + return res.json(); }); + const novels: Plugin.NovelItem[] = []; + + if (success?.length) { + success.forEach(novel => { + if (novel.type === 'book' && novel.title) { + novels.push({ + name: novel.title, + cover: novel.cover + ? this.site + '/storage/' + novel.cover + : defaultCover, + path: novel.id.toString(), + }); + } + }); + } return novels; } - parseDate = (dateString: string | undefined = '') => { - const months: Record = { - января: 1, - февраля: 2, - марта: 3, - апреля: 4, - мая: 5, - июня: 6, - июля: 7, - августа: 8, - сентября: 9, - октября: 10, - ноября: 11, - декабря: 12, - }; + resolveUrl = (path: string, isNovel?: boolean) => + this.site + (isNovel ? '/book/' : '/reader/') + path; - const [day, month, year] = dateString.split(' '); - if (day && months[month] && year) { - return dayjs(year + '-' + months[month] + '-' + day).format('LL'); + getToken = (header: any) => { + const cookies = header.map['set-cookie'] || ''; + for (const cookie of cookies.split('; ') as string[]) { + const [key, val] = cookie.split('='); + if (key === 'XSRF-TOKEN') { + headers['X-XSRF-TOKEN'] = decodeURIComponent(val); + return; + } } - return dateString || null; + if (!headers['X-XSRF-TOKEN']) throw new Error('Failed to find the token'); }; filters = { @@ -156,104 +170,10 @@ class freedlit implements Plugin.PluginBase { value: 'popular', options: [ { label: 'По популярности', value: 'popular' }, - { label: 'По количеству комментариев', value: 'comments' }, - { label: 'По количеству лайков', value: 'likes' }, + { label: 'последние обновления', value: 'updated' }, { label: 'По новизне', value: 'recent' }, { label: 'По просмотрам', value: 'views' }, - ], - type: FilterTypes.Picker, - }, - genre: { - label: 'Жанры:', - value: { include: [], exclude: [] }, - options: [ - { label: 'Любой жанр', value: 'all' }, - { label: 'Альтернативная история', value: 'alternative-history' }, - { label: 'Антиутопия', value: 'dystopia' }, - { label: 'Бизнес-литература', value: 'business-literature' }, - { label: 'Боевая фантастика', value: 'combat-fiction' }, - { label: 'Боевик', value: 'action' }, - { label: 'Боевое фэнтези', value: 'combat-fantasy' }, - { label: 'Бояръ-Аниме', value: 'boyar-anime' }, - { label: 'Героическая фантастика', value: 'heroic-fiction' }, - { label: 'Героическое фэнтези', value: 'heroic-fantasy' }, - { label: 'Городское фэнтези', value: 'urban-fantasy' }, - { label: 'Гримдарк', value: 'grimdark' }, - { label: 'Детектив', value: 'mystery' }, - { label: 'Детская литература', value: 'kids-literature' }, - { label: 'Документальная проза', value: 'biography' }, - { label: 'Историческая проза', value: 'historical-fiction' }, - { label: 'Исторический детектив', value: 'historical-mystery' }, - { - label: 'Исторический любовный роман', - value: 'historical-romantic-novel', - }, - { label: 'Историческое фэнтези', value: 'historical-fantasy' }, - { label: 'Киберпанк', value: 'cyberpunk' }, - { label: 'Космическая фантастика', value: 'cosmic-fiction' }, - { label: 'ЛитРПГ', value: 'litrpg' }, - { label: 'Лоу / Низкое фэнтези', value: 'low-fantasy' }, - { label: 'Любовное фэнтези', value: 'romantic-fantasy' }, - { label: 'Любовный роман', value: 'romantic-novel' }, - { label: 'Мистика', value: 'mystic' }, - { label: 'Мистический детектив', value: 'mystic-detective' }, - { label: 'Научная фантастика', value: 'science-fiction' }, - { label: 'Подростковая проза', value: 'young-adult' }, - { label: 'Политический роман', value: 'political-romance' }, - { label: 'Попаданцы', value: 'accidental-travel' }, - { label: 'Попаданцы в магические миры', value: 'magic-worlds-travel' }, - { label: 'Попаданцы во времени', value: 'time-travel' }, - { label: 'Порнотика', value: 'pornotica' }, - { label: 'Постапокалипсис', value: 'post-apocalypse' }, - { label: 'Поэзия', value: 'poetry' }, - { label: 'Приключения', value: 'adventure' }, - { label: 'Публицистика', value: 'journalism' }, - { label: 'Пьеса', value: 'play' }, - { label: 'Развитие личности', value: 'how-to-book' }, - { label: 'Разное', value: 'other' }, - { label: 'Реализм', value: 'Realism' }, - { label: 'РеалРПГ', value: 'realrpg' }, - { label: 'Репликация', value: 'replication' }, - { label: 'Романтическая эротика', value: 'romantic-erotic-fiction' }, - { label: 'Сказка', value: 'fairy-tale' }, - { label: 'Слэш', value: 'slash' }, - { label: 'Современная проза', value: 'modern-prose' }, - { label: 'Современный любовный роман', value: 'modern-romantic-novel' }, - { label: 'Социальная фантастика', value: 'social-fiction' }, - { label: 'Стимпанк', value: 'steampunk' }, - { label: 'Сценарий', value: 'scenario' }, - { label: 'Сюаньхуань', value: 'xuanhuan' }, - { label: 'Сянься', value: 'xianxia' }, - { label: 'Тёмное фэнтези', value: 'dark-fantasy' }, - { label: 'Триллер', value: 'thriller' }, - { label: 'Уся', value: 'wuxia' }, - { label: 'Фантастика', value: 'fiction' }, - { label: 'Фантастический детектив', value: 'mystery-fiction' }, - { label: 'Фанфик', value: 'fan-fiction' }, - { label: 'Фемслэш', value: 'femslash' }, - { label: 'Фэнтези', value: 'fantasy' }, - { label: 'Хоррор', value: 'horror' }, - { label: 'Шпионский детектив', value: 'spy-crime' }, - { label: 'Эпическое фэнтези', value: 'epic-fantasy' }, - { label: 'Эротика', value: 'erotic-fiction' }, - { label: 'Эротическая фантастика', value: 'erotic-fiction' }, - { label: 'Эротический фанфик', value: 'erotic-fan-fiction' }, - { label: 'Эротическое фэнтези', value: 'erotic-fantasy' }, - { label: 'Этническое фэнтези', value: 'ethnic-fantasy' }, - { label: 'Юмор', value: 'humor' }, - { label: 'Юмористическая фантастика', value: 'humor-fiction' }, - { label: 'Юмористическое фэнтези', value: 'humor-fantasy' }, - { label: 'RPS', value: 'rps' }, - ], - type: FilterTypes.ExcludableCheckboxGroup, - }, - status: { - label: 'Статус:', - value: 'all', - options: [ - { label: 'Любой статус', value: 'all' }, - { label: 'В процессе', value: 'in-process' }, - { label: 'Завершено', value: 'finished' }, + { label: 'По количеству лайков', value: 'likes' }, ], type: FilterTypes.Picker, }, @@ -267,16 +187,124 @@ class freedlit implements Plugin.PluginBase { ], type: FilterTypes.Picker, }, - adult: { - label: 'Возрастные ограничения:', - value: 'hide', - options: [ - { label: 'Скрыть 18+', value: 'hide' }, - { label: 'Показать +18', value: 'show' }, - ], - type: FilterTypes.Picker, + hideAdult: { + label: 'Скрыть 18+', + value: true, + type: FilterTypes.Switch, }, } satisfies Filters; } export default new freedlit(); + +interface Books { + current_page: number; + data: DataEntity[]; + first_page_url: string; + from: number; + last_page: number; + last_page_url: string; + next_page_url: string; + path: string; + per_page: number; + prev_page_url?: null; + to: number; + total: number; +} +interface DataEntity { + id: number; + item_id: number; + type: string; + form_id: number; + title: string; + authors: string; + cover: string; + access: string; + language: string; + rating: number; + intermediate_rating?: number; + first_published: string; + chapter_updated_at: string; + adults: number; + exclusive: number; + status: number; + total_views: number; + total_likes: number; + show_main_widget: number; + is_audio: boolean; + authors_names?: AuthorsNamesEntity[]; + client_price?: number | null; + client_discount?: null; + seriesBook: boolean; + series_title: string; + main_author_link: string; + publisher_link?: null; + published_chapters_total: number; + total_characters: string; + total_author_sheet: number; + total_critics: number; + total_recommendations: number; + form_name: string; + genres?: GenresEntity[] | null; + tags?: TagsEntity[]; + series_book_num: number; + series_book_id?: number | null; + annotation: string; + total_comments: number; + total_libraries: number; +} +interface AuthorsNamesEntity { + id: number; + url: string; + name: string; + type: string; +} +interface GenresEntity { + id: number; +} +interface TagsEntity { + id: number; + name: string; +} + +interface chaptersData { + id: number; + header: string; + first_published_formated: string; + intro_snippet_end: number; +} + +interface chapterContent { + id: number; + book_id: number; + header: string; + content: string; + number: number; + user_files?: null; + intro_snippet_end: number; + status: number; + first_published: number; + auto_publication?: null; + first_published_at?: null; + is_chapter_avalaible: boolean; + total_characters: number; + total_characters_clear: string; +} + +export interface book { + id: number; + cover?: string | null; + title?: string | null; + type: string; + authors_names?: AuthorsNamesEntity[]; + form_name?: string | null; + relevance: number; + for_blog?: number | null; + for_book?: number | null; + items_count?: number | null; + name?: string | null; + blog_category_id?: number | null; + author_name?: string | null; + user_link?: string | null; + img?: string | null; +} diff --git a/src/plugins/russian/jaomix.ts b/src/plugins/russian/jaomix.ts index e472fba..33cc846 100644 --- a/src/plugins/russian/jaomix.ts +++ b/src/plugins/russian/jaomix.ts @@ -9,7 +9,7 @@ class Jaomix implements Plugin.PluginBase { id = 'jaomix.ru'; name = 'Jaomix'; site = 'https://jaomix.ru'; - version = '1.0.0'; + version = '1.0.1'; icon = 'src/ru/jaomix/icon.png'; async popularNovels( @@ -126,9 +126,10 @@ class Jaomix implements Plugin.PluginBase { const loadedCheerio = parseHTML(body); loadedCheerio('div[class="adblock-service"]').remove(); - const chapterText = loadedCheerio('div[class="entry-content"]').html(); + const chapterText = + loadedCheerio('div[class="entry-content"]').html() || ''; - return chapterText || ''; + return chapterText.replace(/]*>(.*?)<\/a>/gi, '$1'); } async searchNovels( diff --git a/src/plugins/russian/renovels.ts b/src/plugins/russian/renovels.ts index 6f489fb..aab6f67 100644 --- a/src/plugins/russian/renovels.ts +++ b/src/plugins/russian/renovels.ts @@ -11,7 +11,7 @@ class ReN implements Plugin.PluginBase { name = 'Renovels'; icon = 'src/ru/renovels/icon.png'; site = 'https://renovels.org'; - version = '1.0.2'; + version = '1.0.3'; async popularNovels( pageNo: number, @@ -20,7 +20,7 @@ class ReN implements Plugin.PluginBase { filters, }: Plugin.PopularNovelsOptions, ): Promise { - let url = this.site + '/api/search/catalog/?count=30&ordering=-'; + let url = this.site + '/api/v2/search/catalog/?count=30&ordering=-'; url += showLatestNovels ? 'chapter_date' : filters?.sort?.value || 'rating'; Object.entries(filters || {}).forEach(([type, { value }]: any) => { @@ -41,12 +41,13 @@ class ReN implements Plugin.PluginBase { url += '&page=' + pageNo; - const { content }: { content: responseNovels[] } = await fetchApi(url).then( + const { results }: { results: responseNovels[] } = await fetchApi(url).then( res => res.json(), ); - const novels: Plugin.NovelItem[] = content.map(novel => ({ + const novels: Plugin.NovelItem[] = results.map(novel => ({ name: novel.main_name || novel.secondary_name, - cover: this.site + (novel.img.high || novel.img.mid || novel.img.low), + cover: + this.site + (novel.cover.high || novel.cover.mid || novel.cover.low), path: novel.dir, })); @@ -63,7 +64,9 @@ class ReN implements Plugin.PluginBase { name: content.main_name || content.secondary_name || content.another_name, summary: content.description, cover: - this.site + (content.img.high || content.img.mid || content.img.low), + this.site + + '/media/' + + (content.cover.high || content.cover.mid || content.cover.low), status: content.status.name === 'Продолжается' ? NovelStatus.Ongoing @@ -80,7 +83,7 @@ class ReN implements Plugin.PluginBase { } const totalPages = - (content.branches?.[0]?.count_chapters || content.count_chapters) / 50; + (content.branches?.[0]?.count_chapters || content.count_chapters) / 100; const branch_id = content.branches?.[0]?.id || content.id; const chapters: Plugin.ChapterItem[] = []; @@ -92,7 +95,7 @@ class ReN implements Plugin.PluginBase { this.site + '/api/titles/chapters/?branch_id=' + branch_id + - '&ordering=index&user_data=1&count=50&page=' + + '&ordering=index&user_data=1&count=100&page=' + (page + 1), ).then(res => res.json()); let skip = false; @@ -133,16 +136,17 @@ class ReN implements Plugin.PluginBase { searchTerm: string, pageNo: number | undefined = 1, ): Promise { - const url = `${this.site}/api/search/?query=${searchTerm}&count=100&field=titles&page=${pageNo}`; - const { content }: { content: responseNovels[] } = await fetchApi(url).then( + const url = `${this.site}/api/v2/search/?query=${searchTerm}&count=100&field=titles&page=${pageNo}`; + const { results }: { results: responseNovels[] } = await fetchApi(url).then( res => res.json(), ); const novels: Plugin.NovelItem[] = []; - content.forEach(novel => + results.forEach(novel => novels.push({ name: novel.main_name || novel.secondary_name, - cover: this.site + (novel.img.high || novel.img.mid || novel.img.low), + cover: + this.site + (novel.cover.high || novel.cover.mid || novel.cover.low), path: novel.dir, }), ); @@ -951,27 +955,25 @@ export default new ReN(); type responseNovels = { id: number; - secondary_name: string; main_name: string; + secondary_name: string; + type: ItemEntity; + status: ItemEntity; + translate_status: ItemEntity; dir: string; - is_licensed: boolean; issue_year: number; avg_rating: string; - admin_rating: string; - type: string; - total_views: number; total_votes: number; - cover_high: string; - cover_mid: string; - cover_low: string; + total_views: number; + count_bookmarks: number; + is_licensed: boolean; + is_legal: boolean; + cover: Img; + count_chapters: number; is_yaoi: boolean; is_erotic: boolean; - bookmark_type?: null; - genres?: ItemEntity[] | null; - img: Img; - categories?: ItemEntity[] | null; - rus_name?: string; - en_name?: string; + bookmark_type: null; + is_forbidden: boolean; }; type Img = { @@ -982,7 +984,7 @@ type Img = { type responseNovel = { id: number; - img: Img; + cover: Img; secondary_name: string; main_name: string; another_name: string;