1+ import {
2+ Chapter ,
3+ ChapterDetails ,
4+ HomeSection ,
5+ Manga ,
6+ MangaUpdates ,
7+ PagedResults ,
8+ Request , RequestHeaders , RequestManager ,
9+ SearchRequest ,
10+ Source ,
11+ SourceInfo ,
12+ TagSection ,
13+ } from "paperback-extensions-common"
14+ import { SenMangaParser } from "./SenMangaParser" ;
15+
16+ const BASE = "https://raw.senmanga.com"
17+
18+ export const SenMangaInfo :SourceInfo = {
19+ icon :"icon.png" ,
20+ version :"1.0.0" ,
21+ name :"SenManga" ,
22+ author :"PythonCoderAS" ,
23+ authorWebsite :"https://github.com/PythonCoderAS" ,
24+ description :"Extension that pulls manga from SenManga" ,
25+ language :"jp" ,
26+ hentaiSource :false ,
27+ websiteBaseURL :BASE
28+ }
29+
30+ export class SenManga extends Source {
31+
32+ private readonly parser :SenMangaParser = new SenMangaParser ( ) ;
33+
34+ private readonly header :RequestHeaders = {
35+ "user-agent" :"Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36"
36+ }
37+
38+ readonly requestManager :RequestManager = createRequestManager ( {
39+ requestTimeout :15000 ,
40+ requestsPerSecond :4
41+ } ) ;
42+
43+ globalRequestHeaders ( ) :RequestHeaders {
44+ return this . header ;
45+ }
46+
47+ getMangaShareUrl ( mangaId :string ) :string {
48+ return `${ BASE } /${ mangaId } ` ;
49+ }
50+
51+ async getTags ( ) :Promise < TagSection [ ] > {
52+ const options :Request = createRequestObject ( {
53+ url :`${ BASE } ` ,
54+ method :'GET' ,
55+ headers :this . header
56+ } ) ;
57+ let response = await this . requestManager . schedule ( options , 1 ) ;
58+ let $ = this . cheerio . load ( response . data ) ;
59+ return this . parser . parseGlobalTagList ( $ ) ;
60+ }
61+
62+ async getHomePageSections ( sectionCallback :( section :HomeSection ) => void ) :Promise < void > {
63+ const options :Request = createRequestObject ( {
64+ url :`${ BASE } ` ,
65+ method :'GET' ,
66+ headers :this . header
67+ } ) ;
68+ let response = await this . requestManager . schedule ( options , 1 ) ;
69+ let $ = this . cheerio . load ( response . data ) ;
70+ sectionCallback ( createHomeSection ( {
71+ id :"last_update" ,
72+ items :this . parser . parseLatestUpdates ( $ , BASE ) ,
73+ title :"Latest Updates" ,
74+ view_more :true
75+ } ) ) ;
76+ sectionCallback ( createHomeSection ( {
77+ id :"popular" ,
78+ items :this . parser . parseMostPopular ( $ , BASE ) ,
79+ title :"Popular Manga" ,
80+ view_more :true
81+ } ) ) ;
82+ sectionCallback ( createHomeSection ( {
83+ id :"new_series" ,
84+ items :this . parser . parseNewSeries ( $ , BASE ) ,
85+ title :"New Series" ,
86+ view_more :true
87+ } ) ) ;
88+ }
89+
90+ private async getPagedResults ( url :string , metadata :{ page ?:number | null , [ key :string ] :any } ) {
91+ if ( typeof metadata !== "object" ) {
92+ metadata = { page :metadata }
93+ }
94+ let page = 1 ;
95+ if ( metadata . page ) {
96+ page = metadata . page ;
97+ }
98+ if ( page === null ) {
99+ return createPagedResults ( { results :[ ] } ) ;
100+ }
101+ let pageAdder = `?page=${ page } ` ;
102+ if ( url . includes ( "?" ) ) {
103+ pageAdder = `&page=${ page } ` ;
104+ }
105+ const options :Request = createRequestObject ( {
106+ url :url + pageAdder ,
107+ method :'GET' ,
108+ headers :this . header
109+ } ) ;
110+ let response = await this . requestManager . schedule ( options , 1 ) ;
111+ let $ = this . cheerio . load ( response . data ) ;
112+ const data = this . parser . parseDirectory ( $ , BASE ) ;
113+ let newPage :number | null = page + 1 ;
114+ if ( data . max >= newPage ) {
115+ newPage = null ;
116+ }
117+ metadata . page = newPage ;
118+ return createPagedResults ( {
119+ results :data . data
120+ } ) ;
121+ }
122+
123+ async getWebsiteMangaDirectory ( metadata :{ page ?:number | null , [ key :string ] :any } ) :Promise < PagedResults > {
124+ return await this . getPagedResults ( `${ BASE } /directory` , metadata )
125+ }
126+
127+ async getChapterDetails ( mangaId :string , chapterId :string ) :Promise < ChapterDetails > {
128+ const options :Request = createRequestObject ( {
129+ url :this . getMangaShareUrl ( mangaId ) + "/" + chapterId ,
130+ method :'GET' ,
131+ headers :this . header ,
132+ cookies :[
133+ createCookie ( {
134+ name :"viewer" ,
135+ value :"1" ,
136+ domain :BASE ,
137+ path :"/" ,
138+ created :new Date ( ) ,
139+ expires :new Date ( 2147483647 )
140+ } )
141+ ]
142+ } ) ;
143+ let response = await this . requestManager . schedule ( options , 1 ) ;
144+ return createChapterDetails ( {
145+ id :chapterId ,
146+ mangaId :mangaId ,
147+ longStrip :false ,
148+ pages :await this . parser . parsePages ( response . data )
149+ } ) ;
150+ }
151+
152+ async getChapters ( mangaId :string ) :Promise < Chapter [ ] > {
153+ const options :Request = createRequestObject ( {
154+ url :this . getMangaShareUrl ( mangaId ) ,
155+ method :'GET' ,
156+ headers :this . header
157+ } ) ;
158+ let response = await this . requestManager . schedule ( options , 1 ) ;
159+ let $ = this . cheerio . load ( response . data ) ;
160+ return this . parser . parseChapterList ( $ , mangaId , BASE ) ;
161+ }
162+
163+ async getMangaDetails ( mangaId :string ) :Promise < Manga > {
164+ const options :Request = createRequestObject ( {
165+ url :this . getMangaShareUrl ( mangaId ) ,
166+ method :'GET' ,
167+ headers :this . header
168+ } ) ;
169+ let response = await this . requestManager . schedule ( options , 1 ) ;
170+ let $ = this . cheerio . load ( response . data ) ;
171+ return this . parser . parseManga ( $ , mangaId ) ;
172+ }
173+
174+ async searchRequest ( query :SearchRequest , metadata :any ) :Promise < PagedResults > {
175+ return await this . getPagedResults ( `${ BASE } /search?s=${ query . title } ` , metadata ) ;
176+ }
177+
178+ async getViewMoreItems ( homepageSectionId :string , metadata :any ) :Promise < PagedResults > {
179+ return await this . getPagedResults ( `${ BASE } /directory/${ homepageSectionId } ` , metadata ) ;
180+ }
181+
182+ async filterUpdatedManga ( mangaUpdatesFoundCallback :( updates :MangaUpdates ) => void , time :Date , ids :string [ ] ) :Promise < void > {
183+ const options :Request = createRequestObject ( {
184+ url :`${ BASE } ` ,
185+ method :'GET' ,
186+ headers :this . header
187+ } ) ;
188+ let response = await this . requestManager . schedule ( options , 1 ) ;
189+ let $ = this . cheerio . load ( response . data ) ;
190+ const idList :string [ ] = [ ] ;
191+ let tiles = this . parser . parseLatestUpdates ( $ , BASE , true ) ;
192+ for ( let i = 0 ; i < tiles . length ; i ++ ) {
193+ const tile = tiles [ i ] ;
194+ const parsedTime = this . parser . parseDateTime ( tile . primaryText ?. text || "" )
195+ if ( parsedTime && parsedTime > time && ids . includes ( tile . id ) ) {
196+ idList . push ( tile . id ) ;
197+ }
198+ }
199+ mangaUpdatesFoundCallback ( createMangaUpdates ( {
200+ ids :idList
201+ } ) ) ;
202+ }
203+ }