@@ -6,6 +6,46 @@ use std::io::Cursor;
66pub async fn migrate_covers ( ) {
77 println ! ( "Checking if any covers need migration..." ) ;
88
9+ let mut root_covers = std:: collections:: HashMap :: new ( ) ;
10+ let mut root_smalls = std:: collections:: HashMap :: new ( ) ;
11+ let mut root_icons = std:: collections:: HashMap :: new ( ) ;
12+
13+ let bucket = & * crate :: S3 ;
14+
15+ for base in [ "covers" , "covers_small" , "icons" ] {
16+ for prefix in [ format ! ( "{}/" , base) , format ! ( "/{}/" , base) ] {
17+ if let Ok ( results) = bucket. list ( prefix. clone ( ) , Some ( "/" . to_string ( ) ) ) . await {
18+ let mut count = 0 ;
19+
20+ for res in results {
21+ for obj in res. contents {
22+ if obj. key . ends_with ( '/' ) {
23+ continue ;
24+ }
25+
26+ let path = std:: path:: Path :: new ( & obj. key ) ;
27+ if let Some ( stem) = path. file_stem ( ) . and_then ( |s| s. to_str ( ) ) {
28+ let stem = stem. to_string ( ) ;
29+ if base == "covers" {
30+ root_covers. insert ( stem, obj. key ) ;
31+ } else if base == "covers_small" {
32+ root_smalls. insert ( stem, obj. key ) ;
33+ } else if base == "icons" {
34+ root_icons. insert ( stem, obj. key ) ;
35+ }
36+ count += 1 ;
37+ }
38+ }
39+ }
40+ }
41+ }
42+ }
43+
44+ println ! (
45+ "Total images sitting in root folders: {}" ,
46+ root_covers. len( ) + root_smalls. len( ) + root_icons. len( )
47+ ) ;
48+
949 let roms = match sqlx:: query!( "SELECT id, console, image_hash FROM roms" )
1050 . fetch_all ( & * SQL )
1151 . await
@@ -23,20 +63,26 @@ pub async fn migrate_covers() {
2363 let hash = & rom. image_hash ;
2464
2565 let new_cover = format ! ( "covers/{}/{}/{}.webp" , rom. console, rom. id, hash) ;
26- let old_cover = format ! ( "covers/{}.webp" , hash) ;
27-
2866 let new_small = format ! ( "covers_small/{}/{}/{}.webp" , rom. console, rom. id, hash) ;
29- let old_small = format ! ( "covers_small/{}.webp" , hash) ;
30-
3167 let new_icon = format ! ( "icons/{}/{}/{}.webp" , rom. console, rom. id, hash) ;
32- let old_icon = format ! ( "icons/{}.webp" , hash) ;
68+
69+ let old_cover_key = root_covers. get ( hash) ;
70+ if old_cover_key. is_none ( ) {
71+ if crate :: utils:: s3:: download_object ( & new_cover) . await . is_ok ( ) {
72+ continue ;
73+ }
74+ }
3375
3476 let mut moved_any = false ;
3577 let mut source_bytes = None ;
3678
37- if let Ok ( bytes) = crate :: utils:: s3:: download_object ( & old_cover) . await {
38- source_bytes = Some ( bytes) ;
39- } else {
79+ if let Some ( key) = old_cover_key {
80+ if let Ok ( bytes) = crate :: utils:: s3:: download_object ( key) . await {
81+ source_bytes = Some ( bytes) ;
82+ }
83+ }
84+
85+ if source_bytes. is_none ( ) {
4086 let shared_roms = sqlx:: query!(
4187 "SELECT id, console FROM roms WHERE image_hash = $1 AND id != $2" ,
4288 rom. image_hash,
@@ -52,45 +98,67 @@ pub async fn migrate_covers() {
5298 if let Ok ( bytes) = crate :: utils:: s3:: download_object ( & other_new_path) . await {
5399 source_bytes = Some ( bytes) ;
54100
55- // Also try to recover small and icon from the same source ROM if needed
56- if crate :: utils:: s3:: download_object ( & old_small) . await . is_err ( ) {
101+ if crate :: utils:: s3:: download_object ( & new_small) . await . is_err ( ) {
57102 let other_new_small = format ! (
58103 "covers_small/{}/{}/{}.webp" ,
59104 s. console, s. id, rom. image_hash
60105 ) ;
61- if let Ok ( s_bytes ) =
62- crate :: utils:: s3:: download_object ( & other_new_small ) . await
63- {
64- let _ = crate :: utils :: s3 :: upload_object ( & new_small , & s_bytes ) . await ;
106+ if let Some ( key ) = root_smalls . get ( hash ) {
107+ if let Ok ( s_bytes ) = crate :: utils:: s3:: download_object ( key ) . await {
108+ let _ = crate :: utils :: s3 :: upload_object ( & new_small , & s_bytes ) . await ;
109+ }
65110 }
66111 }
67- if crate :: utils:: s3:: download_object ( & old_icon ) . await . is_err ( ) {
112+ if crate :: utils:: s3:: download_object ( & new_icon ) . await . is_err ( ) {
68113 let other_new_icon =
69114 format ! ( "icons/{}/{}/{}.webp" , s. console, s. id, rom. image_hash) ;
70- if let Ok ( i_bytes ) =
71- crate :: utils:: s3:: download_object ( & other_new_icon ) . await
72- {
73- let _ = crate :: utils :: s3 :: upload_object ( & new_icon , & i_bytes ) . await ;
115+ if let Some ( key ) = root_icons . get ( hash ) {
116+ if let Ok ( i_bytes ) = crate :: utils:: s3:: download_object ( key ) . await {
117+ let _ = crate :: utils :: s3 :: upload_object ( & new_icon , & i_bytes ) . await ;
118+ }
74119 }
75120 }
76121 break ;
77122 }
78123 }
79124 }
80125
81- if let Some ( bytes) = source_bytes {
126+ if let Some ( mut bytes) = source_bytes {
127+ if let Ok ( img) = image:: load_from_memory ( & bytes) {
128+ let mut buf = std:: io:: Cursor :: new ( Vec :: new ( ) ) ;
129+ if img. write_to ( & mut buf, image:: ImageFormat :: WebP ) . is_ok ( ) {
130+ bytes = buf. into_inner ( ) ;
131+ }
132+ }
133+
82134 if crate :: utils:: s3:: upload_object ( & new_cover, & bytes)
83135 . await
84136 . is_ok ( )
85137 {
86138 moved_any = true ;
87139 }
88140
89- if let Ok ( s_bytes) = crate :: utils:: s3:: download_object ( & old_small) . await {
90- let _ = crate :: utils:: s3:: upload_object ( & new_small, & s_bytes) . await ;
141+ if let Some ( key) = root_smalls. get ( hash) {
142+ if let Ok ( mut s_bytes) = crate :: utils:: s3:: download_object ( key) . await {
143+ if let Ok ( img) = image:: load_from_memory ( & s_bytes) {
144+ let mut buf = std:: io:: Cursor :: new ( Vec :: new ( ) ) ;
145+ if img. write_to ( & mut buf, image:: ImageFormat :: WebP ) . is_ok ( ) {
146+ s_bytes = buf. into_inner ( ) ;
147+ }
148+ }
149+ let _ = crate :: utils:: s3:: upload_object ( & new_small, & s_bytes) . await ;
150+ }
91151 }
92- if let Ok ( i_bytes) = crate :: utils:: s3:: download_object ( & old_icon) . await {
93- let _ = crate :: utils:: s3:: upload_object ( & new_icon, & i_bytes) . await ;
152+ if let Some ( key) = root_icons. get ( hash) {
153+ if let Ok ( mut i_bytes) = crate :: utils:: s3:: download_object ( key) . await {
154+ if let Ok ( img) = image:: load_from_memory ( & i_bytes) {
155+ let mut buf = std:: io:: Cursor :: new ( Vec :: new ( ) ) ;
156+ if img. write_to ( & mut buf, image:: ImageFormat :: WebP ) . is_ok ( ) {
157+ i_bytes = buf. into_inner ( ) ;
158+ }
159+ }
160+ let _ = crate :: utils:: s3:: upload_object ( & new_icon, & i_bytes) . await ;
161+ }
94162 }
95163 }
96164
@@ -107,12 +175,19 @@ pub async fn migrate_covers() {
107175 }
108176
109177 println ! ( "Cleaning up unused covers in root directories..." ) ;
110- for prefix in [ "covers/" , "covers_small/" , "icons/" ] {
111- if let Ok ( results) = crate :: utils:: s3:: list_objects_shallow ( prefix) . await {
112- for key in results {
113- if key. ends_with ( ".webp" ) {
114- println ! ( "Deleting unused file: {}" , key) ;
115- let _ = crate :: utils:: s3:: delete_object ( & key) . await ;
178+
179+ for base in [ "covers" , "covers_small" , "icons" ] {
180+ for prefix in [ format ! ( "{}/" , base) , format ! ( "/{}/" , base) ] {
181+ let bucket = & * crate :: S3 ;
182+
183+ if let Ok ( results) = bucket. list ( prefix, Some ( "/" . to_string ( ) ) ) . await {
184+ for res in results {
185+ for obj in res. contents {
186+ if !obj. key . ends_with ( '/' ) {
187+ println ! ( "Deleting unused file: {}" , obj. key) ;
188+ let _ = bucket. delete_object ( & obj. key ) . await ;
189+ }
190+ }
116191 }
117192 }
118193 }
0 commit comments