11use std:: time:: Duration ;
22
33use again:: RetryPolicy ;
4+ use ipnet:: Ipv6Net ;
45use regex:: Regex ;
5- use reqwest:: header;
6+ use reqwest:: header:: { self , HeaderMap } ;
67use serde:: Deserialize ;
78
89use crate :: {
910 config,
10- http_proxy :: ProxiedClient ,
11+ http_client :: { GhostClient , GhostClientBuilder } ,
1112 stream:: AsyncStream ,
12- util:: match_first_group,
13- util:: { get_bytes, get_string} ,
13+ util:: { get_bytes, get_string, match_first_group} ,
1414} ;
1515
1616use super :: {
@@ -32,8 +32,8 @@ const TIMEOUT: Duration = Duration::from_secs(30);
3232
3333#[ derive( Debug , Clone ) ]
3434pub struct EXCollector {
35- proxy_client : ProxiedClient ,
36- client : reqwest:: Client ,
35+ ghost_client : GhostClient ,
36+ raw_client : reqwest:: Client ,
3737}
3838
3939#[ derive( Debug , Deserialize ) ]
@@ -43,40 +43,49 @@ pub struct ExConfig {
4343 pub igneous : String ,
4444}
4545
46- impl EXCollector {
47- pub fn new ( config : & ExConfig , proxy_client : ProxiedClient ) -> anyhow :: Result < Self > {
46+ impl ExConfig {
47+ fn build_header ( & self ) -> HeaderMap {
4848 let cookie_value = format ! (
4949 "ipb_pass_hash={};ipb_member_id={};igneous={};nw=1" ,
50- config . ipb_pass_hash, config . ipb_member_id, config . igneous
50+ self . ipb_pass_hash, self . ipb_member_id, self . igneous
5151 ) ;
5252
5353 // set headers with exhentai cookies
5454 let mut request_headers = header:: HeaderMap :: new ( ) ;
5555 request_headers. insert (
5656 header:: COOKIE ,
57- header:: HeaderValue :: from_str ( & cookie_value) ?,
57+ header:: HeaderValue :: from_str ( & cookie_value)
58+ . expect ( "invalid ExConfig settings, unable to build header map" ) ,
5859 ) ;
60+ request_headers
61+ }
62+ }
63+
64+ impl EXCollector {
65+ pub fn new ( config : & ExConfig , prefix : Option < Ipv6Net > ) -> anyhow:: Result < Self > {
5966 Ok ( Self {
60- client : {
61- reqwest:: Client :: builder ( )
62- . default_headers ( request_headers. clone ( ) )
63- . timeout ( TIMEOUT )
64- . build ( )
65- . expect ( "build reqwest client failed" )
66- } ,
67- proxy_client : proxy_client. with_default_headers ( request_headers) ,
67+ ghost_client : GhostClientBuilder :: default ( )
68+ . with_default_headers ( config. build_header ( ) )
69+ . with_cf_resolve ( & [ "exhentai.org" ] )
70+ . build ( prefix) ,
71+ raw_client : reqwest:: Client :: builder ( ) . timeout ( TIMEOUT ) . build ( ) . unwrap ( ) ,
6872 } )
6973 }
7074
7175 pub fn new_from_config ( ) -> anyhow:: Result < Self > {
7276 let config: ExConfig = config:: parse ( CONFIG_KEY ) ?
7377 . ok_or_else ( || anyhow:: anyhow!( "exhentai config(key: exhentai) not found" ) ) ?;
74- let proxy_client = ProxiedClient :: new_from_config ( ) ;
75- Self :: new ( & config, proxy_client)
78+ Ok ( Self {
79+ ghost_client : GhostClientBuilder :: default ( )
80+ . with_default_headers ( config. build_header ( ) )
81+ . with_cf_resolve ( & [ "exhentai.org" ] )
82+ . build_from_config ( ) ?,
83+ raw_client : reqwest:: Client :: builder ( ) . timeout ( TIMEOUT ) . build ( ) . unwrap ( ) ,
84+ } )
7685 }
7786
7887 pub fn get_client ( & self ) -> reqwest:: Client {
79- self . client . clone ( )
88+ self . raw_client . clone ( )
8089 }
8190}
8291
@@ -87,7 +96,7 @@ impl Collector for EXCollector {
8796
8897 #[ inline]
8998 fn name ( ) -> & ' static str {
90- "exhentai "
99+ "e-hentai "
91100 }
92101
93102 async fn fetch (
@@ -109,16 +118,16 @@ impl Collector for EXCollector {
109118 tracing:: info!( "[exhentai] process {url}" ) ;
110119
111120 let mut paged = Paged :: new ( 0 , EXPageIndicator { base : url. clone ( ) } ) ;
112- let gallery_pages = paged. pages ( & self . proxy_client ) . await . map_err ( |e| {
121+ let gallery_pages = paged. pages ( & self . ghost_client ) . await . map_err ( |e| {
113122 tracing:: error!( "[exhentai] load page failed: {e:?}" ) ;
114123 e
115124 } ) ?;
116125 tracing:: info!( "[exhentai] pages loaded for {album_id}/{album_token}" ) ;
117126
118127 // Since paged returns at least one page, we can safely get it.
119128 let title = match_first_group ( & TITLE_RE , & gallery_pages[ 0 ] )
120- . unwrap_or ( "No Title" )
121- . to_string ( ) ;
129+ . map ( |s| s . to_string ( ) )
130+ . unwrap_or_else ( || format ! ( "exhentai-{album_id}" ) ) ;
122131
123132 let mut image_page_links = Vec :: new ( ) ;
124133 for gallery_page in gallery_pages. iter ( ) {
@@ -144,8 +153,8 @@ impl Collector for EXCollector {
144153 tags : None ,
145154 } ,
146155 EXImageStream {
147- client : self . client . clone ( ) ,
148- proxy_client : self . proxy_client . clone ( ) ,
156+ raw_client : self . raw_client . clone ( ) ,
157+ ghost_client : self . ghost_client . clone ( ) ,
149158 image_page_links : image_page_links. into_iter ( ) ,
150159 } ,
151160 ) )
@@ -154,24 +163,24 @@ impl Collector for EXCollector {
154163
155164#[ derive( Debug ) ]
156165pub struct EXImageStream {
157- client : reqwest:: Client ,
158- proxy_client : ProxiedClient ,
166+ raw_client : reqwest:: Client ,
167+ ghost_client : GhostClient ,
159168 image_page_links : std:: vec:: IntoIter < String > ,
160169}
161170
162171impl EXImageStream {
163172 async fn load_image (
164- proxy_client : ProxiedClient ,
165- client : reqwest:: Client ,
173+ ghost_client : GhostClient ,
174+ raw_client : reqwest:: Client ,
166175 link : String ,
167176 ) -> anyhow:: Result < ( ImageMeta , ImageData ) > {
168177 let content = RETRY_POLICY
169- . retry ( || async { get_string ( & proxy_client , & link) . await } )
178+ . retry ( || async { get_string ( & ghost_client , & link) . await } )
170179 . await ?;
171180 let img_url = match_first_group ( & IMG_RE , & content)
172181 . ok_or_else ( || anyhow:: anyhow!( "unable to find image in page" ) ) ?;
173182 let image_data = RETRY_POLICY
174- . retry ( || async { get_bytes ( & client , img_url) . await } )
183+ . retry ( || async { get_bytes ( & raw_client , img_url) . await } )
175184 . await ?;
176185
177186 tracing:: trace!(
@@ -194,9 +203,9 @@ impl AsyncStream for EXImageStream {
194203
195204 fn next ( & mut self ) -> Option < Self :: Future > {
196205 let link = self . image_page_links . next ( ) ?;
197- let client = self . client . clone ( ) ;
198- let proxy_client = self . proxy_client . clone ( ) ;
199- Some ( async move { Self :: load_image ( proxy_client , client , link) . await } )
206+ let ghost_client = self . ghost_client . clone ( ) ;
207+ let raw_client = self . raw_client . clone ( ) ;
208+ Some ( async move { Self :: load_image ( ghost_client , raw_client , link) . await } )
200209 }
201210
202211 #[ inline]
@@ -238,7 +247,7 @@ mod tests {
238247 igneous : "balabala" . to_string ( ) ,
239248 } ;
240249 println ! ( "config {config:#?}" ) ;
241- let collector = EXCollector :: new ( & config, ProxiedClient :: default ( ) ) . unwrap ( ) ;
250+ let collector = EXCollector :: new ( & config, None ) . unwrap ( ) ;
242251 let ( album, mut image_stream) = collector
243252 . fetch ( "/g/2129939/01a6e086b9" . to_string ( ) )
244253 . await
@@ -261,7 +270,7 @@ mod tests {
261270 igneous : "balabala" . to_string ( ) ,
262271 } ;
263272 println ! ( "config {config:#?}" ) ;
264- let collector = EXCollector :: new ( & config, ProxiedClient :: default ( ) ) . unwrap ( ) ;
273+ let collector = EXCollector :: new ( & config, None ) . unwrap ( ) ;
265274 let output = collector. fetch ( "/g/2129939/00000" . to_string ( ) ) . await ;
266275 assert ! ( output. is_err( ) ) ;
267276 println ! ( "output err {output:?}" ) ;
0 commit comments