@@ -4,14 +4,22 @@ import { Filters } from '@libs/filterInputs';
44import { CheerioAPI , load as loadCheerio } from 'cheerio' ;
55import { defaultCover } from '@libs/defaultCover' ;
66
7+ const pluginHeaders = {
8+ 'User-Agent' :
9+ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36' ,
10+ } ;
11+
712class ReadFromPlugin implements Plugin . PluginBase {
813 id = 'readfrom' ;
914 name = 'Read From Net' ;
1015 icon = 'src/en/readfrom/icon.png' ;
11- site = 'https://readfrom.net' ;
12- version = '1.0.2 ' ;
16+ site = 'https://readfrom.net/ ' ;
17+ version = '1.1.0 ' ;
1318 filters : Filters | undefined = undefined ;
14- imageRequestInit ?: Plugin . ImageRequestInit | undefined = undefined ;
19+ headers = new Headers ( pluginHeaders ) ;
20+ imageRequestInit : Plugin . ImageRequestInit = {
21+ headers : pluginHeaders ,
22+ } ;
1523
1624 //flag indicates whether access to LocalStorage, SesesionStorage is required.
1725 webStorageUtilized ?: boolean ;
@@ -34,18 +42,17 @@ class ReadFromPlugin implements Plugin.PluginBase {
3442 ( isSearch ? 'div.text' : '#dle-content' ) + ' > article.box' ,
3543 )
3644 . map ( ( i , el ) => {
45+ const $el = loadedCheerio ( el ) ;
46+ const novelPath = $el . find ( 'h2.title a' ) . attr ( 'href' ) ;
47+ if ( ! novelPath ) return ;
3748 const summary = loadedCheerio ( el ) . find (
3849 isSearch ? 'div.text5' : 'div.text3' ,
3950 ) [ 0 ] ;
4051 loadedCheerio ( summary ) . find ( '.coll-ellipsis' ) . remove ( ) ;
4152 loadedCheerio ( summary ) . find ( 'a' ) . remove ( ) ;
4253 return {
4354 name : loadedCheerio ( el ) . find ( 'h2.title' ) . text ( ) . trim ( ) ,
44- path : loadedCheerio ( el )
45- . find ( 'h2.title > a' )
46- . attr ( 'href' ) !
47- . replace ( 'https://readfrom.net/' , '' )
48- . replace ( / ^ \/ / , '' ) ,
55+ path : new URL ( novelPath , this . site ) . pathname . substring ( 1 ) ,
4956 cover : loadedCheerio ( el ) . find ( 'img' ) . attr ( 'src' ) || defaultCover ,
5057 summary :
5158 loadedCheerio ( summary ) . text ( ) . trim ( ) +
@@ -78,21 +85,20 @@ class ReadFromPlugin implements Plugin.PluginBase {
7885
7986 async popularNovels (
8087 pageNo : number ,
81- {
82- showLatestNovels,
83- filters,
84- } : Plugin . PopularNovelsOptions < typeof this . filters > ,
88+ { showLatestNovels } : Plugin . PopularNovelsOptions < typeof this . filters > ,
8589 ) {
8690 const type = showLatestNovels ? 'last_added_books' : 'allbooks' ;
87- const res = await fetchApi (
88- 'https://readfrom.net/' + type + '/page/' + pageNo + '/' ,
89- ) ;
91+ const res = await fetchApi ( this . site + type + '/page/' + pageNo , {
92+ headers : this . headers ,
93+ } ) ;
9094 const text = await res . text ( ) ;
9195 return this . parseNovels ( loadCheerio ( text ) ) ;
9296 }
9397
9498 async parseNovel ( novelPath : string ) : Promise < Plugin . SourceNovel > {
95- const data = await fetchApi ( 'https://readfrom.net/' + novelPath ) ;
99+ const data = await fetchApi ( this . site + novelPath , {
100+ headers : this . headers ,
101+ } ) ;
96102 const text = await data . text ( ) ;
97103 const loadedCheerio = loadCheerio ( text ) ;
98104
@@ -109,25 +115,22 @@ class ReadFromPlugin implements Plugin.PluginBase {
109115 loadedCheerio ( 'article.box > div > center > div > a > img' ) . attr ( 'src' ) ||
110116 defaultCover ;
111117
112- novel . chapters = loadedCheerio ( loadedCheerio ( 'div.pages' ) . get ( ) [ 0 ] )
118+ const rawChapters = loadedCheerio ( 'div.pages' )
119+ . first ( )
113120 . find ( '> a' )
114- . map ( ( i , el ) => {
115- return {
116- name : loadedCheerio ( el ) . text ( ) . trim ( ) ,
117- path : loadedCheerio ( el )
118- . attr ( 'href' ) !
119- . replace ( 'https://readfrom.net/' , '' )
120- . replace ( / ^ \/ / , '' ) ,
121- // releaseTime: '',
122- chapterNumber : i + 2 ,
123- } ;
124- } )
125- . toArray ( ) ;
126- novel . chapters . unshift ( {
127- name : '1' ,
128- path : novelPath ,
129- chapterNumber : 1 ,
130- } ) ;
121+ . toArray ( )
122+ . flatMap ( el => {
123+ const href = loadedCheerio ( el ) . attr ( 'href' ) ;
124+ if ( ! href ) return [ ] ;
125+
126+ const path = new URL ( href , this . site ) . pathname . substring ( 1 ) ;
127+ return path ? [ { name : loadedCheerio ( el ) . text ( ) . trim ( ) , path } ] : [ ] ;
128+ } ) ;
129+
130+ novel . chapters = [
131+ { name : '1' , path : novelPath , chapterNumber : 1 } ,
132+ ...rawChapters . map ( ( ch , i ) => ( { ...ch , chapterNumber : i + 2 } ) ) ,
133+ ] ;
131134
132135 let moreNovelInfo = this . loadedNovelCache . find (
133136 novel => novel . path === novelPath ,
@@ -156,43 +159,45 @@ class ReadFromPlugin implements Plugin.PluginBase {
156159 }
157160
158161 async parseChapter ( chapterPath : string ) : Promise < string > {
159- const data = await fetchApi ( 'https://readfrom.net/' + chapterPath ) ;
160- const text = await data . text ( ) ;
161- const loadedCheerio = loadCheerio ( text ) ;
162- loadedCheerio ( '#textToRead > span:empty' ) . remove ( ) ;
163- loadedCheerio ( '#textToRead > center' ) . remove ( ) ;
164-
165- const textToRead = loadedCheerio ( '#textToRead' ) ;
162+ const response = await fetchApi ( this . site + chapterPath , {
163+ headers : this . headers ,
164+ } ) ;
165+ const $ = loadCheerio ( await response . text ( ) ) ;
166+ $ ( '#textToRead > span:empty, #textToRead > center' ) . remove ( ) ;
166167
167- let paragraph : string [ ] = [ ] ;
168168 const chapterHtml : string [ ] = [ ] ;
169+ let p : string [ ] = [ ] ;
170+
171+ const allowed = new Set ( [ 'b' , 'i' , 'u' , 'strong' , 'em' , 'a' ] ) ;
172+ const flush = ( ) =>
173+ p . length && ( chapterHtml . push ( `<p>${ p . join ( ' ' ) . trim ( ) } </p>` ) , ( p = [ ] ) ) ;
169174
170- textToRead . contents ( ) . each ( ( _ , element ) => {
171- switch ( element . type ) {
175+ for ( const el of $ ( '#textToRead' ) . contents ( ) . toArray ( ) ) {
176+ switch ( el . type ) {
177+ case 'comment' :
178+ continue ;
172179 case 'text' :
173- const content = element . data . trim ( ) ;
174- if ( content ) {
175- paragraph . push ( content ) ;
180+ if ( el . data . trim ( ) ) {
181+ // Convert _text_ to <i>text</i>
182+ const jbText = el . data . trim ( ) . replace ( / _ ( [ ^ _ ] + ) _ / g, '<i>$1</i>' ) ;
183+ p . push ( jbText ) ;
176184 }
177- break ;
178-
185+ continue ;
179186 case 'tag' :
180- if ( paragraph . length > 0 ) {
181- chapterHtml . push ( `<p> ${ paragraph . join ( ' ' ) } </p>` ) ;
182- paragraph = [ ] ;
187+ if ( allowed . has ( el . name ) ) {
188+ p . push ( $ . html ( el ) ) ;
189+ continue ;
183190 }
184- if ( element . tagName !== 'br' ) {
185- chapterHtml . push ( loadedCheerio . html ( element ) ) ;
191+ if ( el . name === 'br' ) {
192+ flush ( ) ;
193+ continue ;
186194 }
187- break ;
188195 }
189- } ) ;
190-
191- // Close any remaining paragraph
192- if ( paragraph . length > 0 ) {
193- chapterHtml . push ( `<p>${ paragraph . join ( ' ' ) } </p>` ) ;
196+ flush ( ) ;
197+ chapterHtml . push ( $ . html ( el ) ) ;
194198 }
195199
200+ flush ( ) ;
196201 return chapterHtml . join ( '' ) ;
197202 }
198203
@@ -201,12 +206,13 @@ class ReadFromPlugin implements Plugin.PluginBase {
201206 const res = await fetchApi (
202207 'https://readfrom.net/build_in_search/?q=' +
203208 encodeURIComponent ( searchTerm ) ,
209+ { headers : this . headers } ,
204210 ) ;
205211 const text = await res . text ( ) ;
206212 return this . parseNovels ( loadCheerio ( text ) , true ) ;
207213 }
208214
209- resolveUrl = ( path : string , isNovel ?: boolean ) => this . site + '/' + path ;
215+ // resolveUrl = (path: string, isNovel?: boolean) => this.site + '/' + path;
210216}
211217
212218export default new ReadFromPlugin ( ) ;
0 commit comments