1
+ use std:: io:: { self , ErrorKind } ;
1
2
use std:: path;
2
3
3
4
use crate :: core:: api:: { self , deserialize_json} ;
@@ -7,20 +8,27 @@ use directories::ProjectDirs;
7
8
use lazy_static:: lazy_static;
8
9
use tokio:: fs;
9
10
use tokio:: io:: AsyncWriteExt ;
10
- use tracing:: info;
11
+ use tracing:: { error , info} ;
11
12
12
13
const SERIES_CACHE_DIRECTORY : & str = "series-troxide-series-data" ;
13
14
const IMAGES_CACHE_DIRECTORY : & str = "series-troxide-images-data" ;
15
+ const EPISODE_LIST_FILENAME : & str = "episode-list" ;
16
+ const SERIES_MAIN_INFORMATION_FILENAME : & str = "main-info" ;
14
17
15
18
lazy_static ! {
16
19
pub static ref CACHER : Cacher = Cacher :: init( ) ;
17
20
}
18
21
19
- pub enum CacheType {
22
+ pub enum CacheFolderType {
20
23
Series ,
21
24
Images ,
22
25
}
23
26
27
+ pub enum CacheFilePath {
28
+ SeriesMainInformation ( u32 ) ,
29
+ SeriesEpisodeList ( u32 ) ,
30
+ }
31
+
24
32
pub struct Cacher {
25
33
cache_path : path:: PathBuf ,
26
34
}
@@ -37,19 +45,43 @@ impl Cacher {
37
45
}
38
46
}
39
47
40
- pub fn get_cache_path ( & self , cache_type : CacheType ) -> path:: PathBuf {
48
+ pub fn get_cache_folder_path ( & self , cache_type : CacheFolderType ) -> path:: PathBuf {
41
49
let mut cache_path = self . cache_path . clone ( ) ;
42
50
match cache_type {
43
- CacheType :: Series => cache_path. push ( SERIES_CACHE_DIRECTORY ) ,
44
- CacheType :: Images => cache_path. push ( IMAGES_CACHE_DIRECTORY ) ,
51
+ CacheFolderType :: Series => cache_path. push ( SERIES_CACHE_DIRECTORY ) ,
52
+ CacheFolderType :: Images => cache_path. push ( IMAGES_CACHE_DIRECTORY ) ,
45
53
}
46
54
cache_path
47
55
}
56
+
57
+ /// This method is used to retrieve cache files for individual files in the series cache directory
58
+ /// i.e episode-list, main-info
59
+ pub fn get_cache_file_path ( & self , cache_file_type : CacheFilePath ) -> path:: PathBuf {
60
+ match cache_file_type {
61
+ CacheFilePath :: SeriesMainInformation ( series_id) => {
62
+ let mut cache_folder = self . get_series_cache_folder_path ( series_id) ;
63
+ cache_folder. push ( SERIES_MAIN_INFORMATION_FILENAME ) ;
64
+ cache_folder
65
+ }
66
+ CacheFilePath :: SeriesEpisodeList ( series_id) => {
67
+ let mut cache_folder = self . get_series_cache_folder_path ( series_id) ;
68
+ cache_folder. push ( EPISODE_LIST_FILENAME ) ;
69
+ cache_folder
70
+ }
71
+ }
72
+ }
73
+
74
+ /// This method is used to retrieve the series folder path that is a parent to individual cache files
75
+ pub fn get_series_cache_folder_path ( & self , series_id : u32 ) -> path:: PathBuf {
76
+ let mut cache_folder = self . get_cache_folder_path ( CacheFolderType :: Series ) ;
77
+ cache_folder. push ( format ! ( "{series_id}" ) ) ;
78
+ cache_folder
79
+ }
48
80
}
49
81
50
82
/// Loads the image from the provided url
51
83
pub async fn load_image ( image_url : String ) -> Option < Vec < u8 > > {
52
- let mut image_path = CACHER . get_cache_path ( CacheType :: Images ) ;
84
+ let mut image_path = CACHER . get_cache_folder_path ( CacheFolderType :: Images ) ;
53
85
54
86
// Hashing the image url as a file name as the forward slashes in web urls
55
87
// mimic paths
@@ -64,7 +96,7 @@ pub async fn load_image(image_url: String) -> Option<Vec<u8>> {
64
96
match fs:: read ( & image_path) . await {
65
97
Ok ( image_bytes) => return Some ( image_bytes) ,
66
98
Err ( err) => {
67
- let images_directory = CACHER . get_cache_path ( CacheType :: Images ) ;
99
+ let images_directory = CACHER . get_cache_folder_path ( CacheFolderType :: Images ) ;
68
100
if !images_directory. exists ( ) {
69
101
info ! ( "creating images cache directory as none exists" ) ;
70
102
fs:: DirBuilder :: new ( )
@@ -93,10 +125,46 @@ pub async fn load_image(image_url: String) -> Option<Vec<u8>> {
93
125
} ;
94
126
}
95
127
128
+ pub async fn read_cache ( cache_filepath : impl AsRef < path:: Path > ) -> io:: Result < String > {
129
+ fs:: read_to_string ( cache_filepath) . await
130
+ }
131
+
132
+ pub async fn write_cache ( cache_data : & str , cache_filepath : & path:: Path ) {
133
+ loop {
134
+ if let Err ( err) = fs:: write ( cache_filepath, cache_data) . await {
135
+ if err. kind ( ) == ErrorKind :: NotFound {
136
+ let mut cache_folder = path:: PathBuf :: from ( cache_filepath) ;
137
+ cache_folder. pop ( ) ;
138
+ match fs:: create_dir_all ( & cache_folder) . await {
139
+ Err ( err) => {
140
+ error ! (
141
+ "failed to create cache directory '{}': {}" ,
142
+ cache_folder. display( ) ,
143
+ err
144
+ ) ;
145
+ break ;
146
+ }
147
+ Ok ( _) => continue ,
148
+ } ;
149
+ } else {
150
+ error ! (
151
+ "failed to write cache '{}': {}" ,
152
+ cache_filepath. display( ) ,
153
+ err
154
+ ) ;
155
+ break ;
156
+ }
157
+ }
158
+ break ;
159
+ }
160
+ }
161
+
96
162
pub mod series_information {
97
163
use super :: api:: series_information;
98
164
use super :: * ;
99
165
166
+ use std:: io:: ErrorKind ;
167
+
100
168
pub async fn get_series_main_info_with_url (
101
169
url : String ,
102
170
) -> Result < SeriesMainInformation , ApiError > {
@@ -113,33 +181,22 @@ pub mod series_information {
113
181
pub async fn get_series_main_info_with_id (
114
182
series_id : u32 ,
115
183
) -> Result < SeriesMainInformation , ApiError > {
116
- let name = format ! ( "{}" , series_id) ;
117
- let mut series_main_info_path = CACHER . get_cache_path ( CacheType :: Series ) ;
118
- series_main_info_path. push ( & name) ; // creating the series folder path
119
- let series_directory = series_main_info_path. clone ( ) ; // creating a copy before we make it path to file
120
- series_main_info_path. push ( & name) ; // creating the series information json filename path
184
+ let series_information_path =
185
+ CACHER . get_cache_file_path ( CacheFilePath :: SeriesMainInformation ( series_id) ) ;
121
186
122
- let series_information_json = match fs :: read_to_string ( & series_main_info_path ) . await {
123
- Ok ( info ) => info ,
187
+ let series_information_json = match read_cache ( & series_information_path ) . await {
188
+ Ok ( json_string ) => json_string ,
124
189
Err ( err) => {
125
- info ! (
126
- "falling back online for 'series information' with id {}: {}" ,
127
- series_id, err
128
- ) ;
129
- fs:: DirBuilder :: new ( )
130
- . recursive ( true )
131
- . create ( series_directory)
132
- . await
133
- . unwrap ( ) ;
134
- let ( series_information, json_string) =
135
- series_information:: get_series_main_info_with_id ( series_id)
136
- . await ?
137
- . get_data ( ) ;
138
- fs:: write ( series_main_info_path, json_string) . await . unwrap ( ) ;
139
- return Ok ( series_information) ;
190
+ info ! ( "falling back online for 'series information' for series id: {series_id}" ) ;
191
+ let json_string =
192
+ series_information:: get_series_main_info_with_id ( series_id) . await ?;
193
+
194
+ if err. kind ( ) == ErrorKind :: NotFound {
195
+ write_cache ( & json_string, & series_information_path) . await ;
196
+ }
197
+ json_string
140
198
}
141
199
} ;
142
-
143
200
deserialize_json ( & series_information_json)
144
201
}
145
202
@@ -167,14 +224,14 @@ pub mod show_images {
167
224
show_images:: { get_show_images as get_show_images_api, Image , ImageType } ,
168
225
ApiError ,
169
226
} ,
170
- caching:: { load_image, CacheType , CACHER } ,
227
+ caching:: { load_image, CacheFolderType , CACHER } ,
171
228
} ;
172
229
use tokio:: fs;
173
230
use tracing:: info;
174
231
175
232
pub async fn get_show_images ( series_id : u32 ) -> Result < Vec < Image > , ApiError > {
176
233
let name = format ! ( "{}" , series_id) ;
177
- let mut series_images_list_path = CACHER . get_cache_path ( CacheType :: Series ) ;
234
+ let mut series_images_list_path = CACHER . get_cache_folder_path ( CacheFolderType :: Series ) ;
178
235
series_images_list_path. push ( & name) ; // creating the series folder path
179
236
let series_directory = series_images_list_path. clone ( ) ; // creating a copy before we make it path to file
180
237
series_images_list_path. push ( "series-images-list" ) ; // creating the episode list json filename path
@@ -228,52 +285,45 @@ pub mod show_images {
228
285
}
229
286
230
287
pub mod episode_list {
231
- use std:: collections:: HashSet ;
288
+ use std:: { collections:: HashSet , io :: ErrorKind } ;
232
289
233
290
use crate :: core:: {
234
291
api:: {
235
292
deserialize_json,
236
293
episodes_information:: { get_episode_list, Episode } ,
237
294
ApiError ,
238
295
} ,
239
- caching:: { CacheType , CACHER } ,
296
+ caching:: CACHER ,
240
297
} ;
241
298
use chrono:: { DateTime , Datelike , Local , Timelike , Utc } ;
242
- use tokio:: fs;
243
299
use tracing:: info;
244
300
301
+ use super :: { read_cache, write_cache, CacheFilePath } ;
302
+
245
303
#[ derive( Clone , Debug ) ]
246
304
pub struct EpisodeList {
247
305
episodes : Vec < Episode > ,
248
306
}
249
307
250
308
impl EpisodeList {
251
309
pub async fn new ( series_id : u32 ) -> Result < Self , ApiError > {
252
- let name = format ! ( "{}" , series_id) ;
253
- let mut episode_list_path = CACHER . get_cache_path ( CacheType :: Series ) ;
254
- episode_list_path. push ( & name) ; // creating the series folder path
255
- let series_directory = episode_list_path. clone ( ) ; // creating a copy before we make it path to file
256
- episode_list_path. push ( "episode-list" ) ; // creating the episode list json filename path
257
-
258
- let series_information_json = match fs:: read_to_string ( & episode_list_path) . await {
259
- Ok ( info) => info,
310
+ let episodes_list_path =
311
+ CACHER . get_cache_file_path ( CacheFilePath :: SeriesEpisodeList ( series_id) ) ;
312
+
313
+ let json_string = match read_cache ( & episodes_list_path) . await {
314
+ Ok ( json_string) => json_string,
260
315
Err ( err) => {
261
- info ! (
262
- "falling back online for 'episode list' for series id {}: {}" ,
263
- series_id, err
264
- ) ;
265
- fs:: DirBuilder :: new ( )
266
- . recursive ( true )
267
- . create ( series_directory)
268
- . await
269
- . unwrap ( ) ;
316
+ info ! ( "falling back online for 'episode_list' for series id: {series_id}" ) ;
270
317
let ( episodes, json_string) = get_episode_list ( series_id) . await ?;
271
- fs:: write ( episode_list_path, json_string) . await . unwrap ( ) ;
318
+
319
+ if err. kind ( ) == ErrorKind :: NotFound {
320
+ write_cache ( & json_string, & episodes_list_path) . await ;
321
+ }
272
322
return Ok ( Self { episodes } ) ;
273
323
}
274
324
} ;
275
325
276
- let episodes = deserialize_json :: < Vec < Episode > > ( & series_information_json ) ?;
326
+ let episodes = deserialize_json :: < Vec < Episode > > ( & json_string ) ?;
277
327
Ok ( Self { episodes } )
278
328
}
279
329
0 commit comments