11use std:: io:: Cursor ;
22
33use async_recursion:: async_recursion;
4- use futures:: TryStreamExt ;
54use nanoid:: nanoid;
65use rs_plugin_common_interfaces:: {
7- ExternalImage , ImageType , domain:: { ItemWithRelations , other_ids:: OtherIds , rs_ids:: { ApplyRsIds , RsIds } } , lookup:: { RsLookupBook , RsLookupMetadataResult , RsLookupQuery } , request :: RsRequest
6+ ExternalImage , ImageType , domain:: { ItemWithRelations , other_ids:: OtherIds , rs_ids:: { ApplyRsIds , RsIds } } , lookup:: { RsLookupBook , RsLookupMetadataResult , RsLookupQuery } ,
87} ;
98use serde:: { Deserialize , Serialize } ;
109use strum_macros:: EnumString ;
11- use tokio:: io:: AsyncWriteExt ;
1210
1311use crate :: {
1412 domain:: {
@@ -23,13 +21,14 @@ use crate::{
2321 tags:: TagForAdd ,
2422 } ,
2523 plugins:: sources:: {
26- error:: SourcesError , AsyncReadPinBox , FileStreamResult , Source , SourceRead ,
24+ error:: SourcesError , AsyncReadPinBox , FileStreamResult ,
2725 } ,
2826 routes:: sse:: SseEvent ,
29- tools:: image_tools:: { convert_image_reader, resize_image_reader , ImageSize } ,
27+ tools:: image_tools:: { convert_image_reader, ImageSize } ,
3028} ;
3129
3230use super :: {
31+ entity_images:: EntityImageConfig ,
3332 error:: { Error , Result } ,
3433 store:: sql:: SqlOrder ,
3534 users:: ConnectedUser ,
@@ -69,19 +68,6 @@ pub struct BookQuery {
6968}
7069
7170impl ModelController {
72- fn select_book_image_url ( images : Vec < ExternalImage > , kind : & ImageType ) -> Option < RsRequest > {
73- let exact_images: Vec < _ > = images. into_iter ( ) . filter ( |image| image. match_type . is_some ( ) ) . collect ( ) ;
74- let first_kind_match = exact_images
75- . iter ( )
76- . find ( |image| image. kind . as_ref ( ) == Some ( kind) )
77- . map ( |image| image. url . clone ( ) ) ;
78- if first_kind_match. is_some ( ) {
79- first_kind_match
80- } else {
81- exact_images. into_iter ( ) . next ( ) . map ( |image| image. url )
82- }
83- }
84-
8571 pub async fn get_books (
8672 & self ,
8773 library_id : & str ,
@@ -217,11 +203,11 @@ impl ModelController {
217203 new_book. apply_rs_ids ( & ids) ;
218204 println ! ( "Adding book with ids: {:?}" , ids) ;
219205 println ! ( "Adding book {:?}" , new_book) ;
220- if ids. isbn13 . is_some ( )
221- || ids. openlibrary_edition_id . is_some ( )
222- || ids. openlibrary_work_id . is_some ( )
223- || ids. google_books_volume_id . is_some ( )
224- || ids. asin . is_some ( )
206+ if ids. isbn13 ( ) . is_some ( )
207+ || ids. openlibrary_edition_id ( ) . is_some ( )
208+ || ids. openlibrary_work_id ( ) . is_some ( )
209+ || ids. google_books_volume_id ( ) . is_some ( )
210+ || ids. asin ( ) . is_some ( )
225211 {
226212 if let Some ( existing) = self
227213 . get_book_by_external_id ( library_id, ids, requesting_user)
@@ -449,99 +435,25 @@ impl ModelController {
449435 requesting_user : & ConnectedUser ,
450436 ) -> RsResult < FileStreamResult < AsyncReadPinBox > > {
451437 let target_kind = kind. unwrap_or ( ImageType :: Poster ) ;
452-
438+ let config = EntityImageConfig { folder : ".books" , cache_prefix : "book" } ;
453439 if RsIds :: is_id ( book_id) {
454440 let book_ids: RsIds = book_id. to_string ( ) . try_into ( ) ?;
455441 let store = self . store . get_library_store ( library_id) ?;
456442 let existing_book = store. get_book_by_external_id ( book_ids. clone ( ) ) . await ?;
457-
458443 if let Some ( existing_book) = existing_book {
459- self . book_image (
460- library_id,
461- & existing_book. item . id ,
462- Some ( target_kind) ,
463- size,
464- requesting_user,
465- )
466- . await
467- } else {
468- // Book not in DB — fetch image from plugin lookup and cache it
469- let local_provider = self . library_source_for_library ( library_id) . await ?;
470- let image_path = format ! (
471- "cache/book-{}-{}.avif" ,
472- book_id. replace( ':' , "-" ) ,
473- target_kind
474- ) ;
475-
476- if !local_provider. exists ( & image_path) . await {
477- let lookup_query = RsLookupBook {
478- name : None ,
479- ids : Some ( book_ids) ,
480- page_key : None ,
481- } ;
482- let image_request = self
483- . get_book_image_url (
484- lookup_query,
485- Some ( library_id. to_string ( ) ) ,
486- & target_kind,
487- requesting_user,
488- )
489- . await ?
490- . ok_or ( crate :: Error :: NotFound ( format ! (
491- "Unable to get book image url: {} kind {:?}" ,
492- book_id, target_kind
493- ) ) ) ?;
494- let ( _, mut writer) = local_provider. get_file_write_stream ( & image_path) . await ?;
495- let image_reader = SourceRead :: Request ( image_request)
496- . into_reader (
497- Some ( library_id) ,
498- None ,
499- None ,
500- Some ( ( self . clone ( ) , requesting_user) ) ,
501- None ,
502- )
503- . await ?;
504- let resized = resize_image_reader (
505- image_reader. stream ,
506- ImageSize :: Large . to_size ( ) ,
507- image:: ImageFormat :: Avif ,
508- Some ( 70 ) ,
509- false ,
510- )
511- . await ?;
512- writer. write_all ( & resized) . await ?;
513- }
514-
515- let source = local_provider. get_file ( & image_path, None ) . await ?;
516- match source {
517- SourceRead :: Stream ( s) => Ok ( s) ,
518- SourceRead :: Request ( _) => Err ( crate :: Error :: GenericRedseatError ) ,
519- }
444+ return self . book_image ( library_id, & existing_book. item . id , Some ( target_kind) , size, requesting_user) . await ;
520445 }
446+ let lookup_query = RsLookupQuery :: Book ( RsLookupBook {
447+ name : None ,
448+ ids : Some ( book_ids) ,
449+ page_key : None ,
450+ } ) ;
451+ self . serve_cached_entity_image ( library_id, book_id, lookup_query, & target_kind, & config, requesting_user) . await
521452 } else {
522- if !self
523- . has_library_image (
524- library_id,
525- ".books" ,
526- book_id,
527- Some ( target_kind. clone ( ) ) ,
528- requesting_user,
529- )
530- . await ?
531- {
532- self . refresh_book_image ( library_id, book_id, & target_kind, requesting_user)
533- . await ?;
534- }
535-
536- self . library_image (
537- library_id,
538- ".books" ,
539- book_id,
540- Some ( target_kind) ,
541- size,
542- requesting_user,
543- )
544- . await
453+ self . serve_local_entity_image (
454+ library_id, book_id, & target_kind, size, & config, requesting_user,
455+ async { self . refresh_book_image ( library_id, book_id, & target_kind, requesting_user) . await . map ( |_| ( ) ) } ,
456+ ) . await
545457 }
546458 }
547459
@@ -551,24 +463,7 @@ impl ModelController {
551463 library_id : Option < String > ,
552464 requesting_user : & ConnectedUser ,
553465 ) -> RsResult < Vec < ExternalImage > > {
554- let lookup_query = RsLookupQuery :: Book ( query) ;
555- println ! ( "Executing book image lookup with query: {:?}" , lookup_query) ;
556- let images = match self
557- . exec_lookup_images ( lookup_query, library_id, requesting_user, None )
558- . await
559- {
560- Ok ( images) => images,
561- Err ( error) => {
562- crate :: tools:: log:: log_error (
563- crate :: tools:: log:: LogServiceType :: Plugin ,
564- format ! ( "book image lookup failed: {:#}" , error) ,
565- ) ;
566- Vec :: new ( )
567- }
568- } ;
569-
570- //println!("result: {:?}", images);
571- Ok ( images)
466+ self . get_entity_images ( RsLookupQuery :: Book ( query) , library_id, requesting_user) . await
572467 }
573468
574469 pub async fn get_book_image_url (
@@ -577,11 +472,8 @@ impl ModelController {
577472 library_id : Option < String > ,
578473 kind : & ImageType ,
579474 requesting_user : & ConnectedUser ,
580- ) -> RsResult < Option < RsRequest > > {
581- let images = self
582- . get_book_images ( query, library_id, requesting_user)
583- . await ?;
584- Ok ( Self :: select_book_image_url ( images, kind) )
475+ ) -> RsResult < Option < rs_plugin_common_interfaces:: RsRequest > > {
476+ self . get_entity_image_url ( RsLookupQuery :: Book ( query) , library_id, kind, requesting_user) . await
585477 }
586478
587479 pub async fn download_book_image (
@@ -591,23 +483,7 @@ impl ModelController {
591483 kind : & ImageType ,
592484 requesting_user : & ConnectedUser ,
593485 ) -> RsResult < AsyncReadPinBox > {
594- let request = self
595- . get_book_image_url ( query, library_id. clone ( ) , kind, requesting_user)
596- . await ?
597- . ok_or ( crate :: Error :: NotFound ( format ! (
598- "Unable to get book image url for kind: {:?}" ,
599- kind
600- ) ) ) ?;
601- let reader = SourceRead :: Request ( request)
602- . into_reader (
603- library_id. as_deref ( ) ,
604- None ,
605- None ,
606- Some ( ( self . clone ( ) , requesting_user) ) ,
607- None ,
608- )
609- . await ?;
610- Ok ( reader. stream )
486+ self . download_entity_image ( RsLookupQuery :: Book ( query) , library_id, kind, requesting_user) . await
611487 }
612488
613489 pub async fn refresh_book_image (
@@ -622,31 +498,16 @@ impl ModelController {
622498 . await ?
623499 . item ;
624500 let ids: RsIds = book. clone ( ) . into ( ) ;
625- let lookup_query = RsLookupBook {
501+ let lookup_query = RsLookupQuery :: Book ( RsLookupBook {
626502 name : Some ( book. name . clone ( ) ) ,
627503 ids : Some ( ids) ,
628504 page_key : None ,
629- } ;
505+ } ) ;
630506 let reader = self
631- . download_book_image (
632- lookup_query,
633- Some ( library_id. to_string ( ) ) ,
634- kind,
635- requesting_user,
636- )
507+ . download_entity_image ( lookup_query, Some ( library_id. to_string ( ) ) , kind, requesting_user)
637508 . await ?;
638- self . update_book_image (
639- library_id,
640- & book. id ,
641- kind,
642- reader,
643- & ConnectedUser :: ServerAdmin ,
644- )
645- . await ?;
646- Ok ( self
647- . get_book ( library_id, book. id , requesting_user)
648- . await ?
649- . item )
509+ self . update_book_image ( library_id, & book. id , kind, reader, & ConnectedUser :: ServerAdmin ) . await ?;
510+ Ok ( self . get_book ( library_id, book. id , requesting_user) . await ?. item )
650511 }
651512
652513 pub async fn update_book_image (
0 commit comments