66from functools import wraps
77from typing import Dict , Any , Callable , Optional , Tuple , Iterable , List , Union
88
9- import bcrypt
109from flask import Blueprint , url_for , current_app , request , abort
1110from flask_login import login_user , current_user
1211from sqlalchemy import desc , asc
2221from ..objects import GameVersion , Game , Publisher , Mod , Featured , User , ModVersion , SharedAuthor , \
2322 ModList
2423from ..search import search_mods , search_users , typeahead_mods , get_mod_score
24+ from ..thumbnail import thumb_path_from_background_path
2525
2626api = Blueprint ('api' , __name__ )
2727
@@ -60,7 +60,7 @@ def mod_info(mod: Mod) -> Dict[str, Any]:
6060 "author" : mod .user .username ,
6161 "default_version_id" : mod .default_version .id ,
6262 "shared_authors" : list (),
63- "background" : mod .background ,
63+ "background" : mod .background_url ( _cfg ( 'protocol' ), _cfg ( 'cdn-domain' )) ,
6464 "bg_offset_y" : mod .bgOffsetY ,
6565 "license" : mod .license ,
6666 "website" : mod .external_link ,
@@ -174,28 +174,41 @@ def _update_image(old_path: str, base_name: str, base_path: str) -> Optional[str
174174 full_path = os .path .join (storage , base_path )
175175 if not os .path .exists (full_path ):
176176 os .makedirs (full_path )
177- try :
178- os .remove (os .path .join (storage , old_path ))
179- except :
180- pass # who cares
177+
178+ if old_path :
179+ try_remove_file_and_folder (os .path .join (storage , old_path ))
181180 f .save (os .path .join (full_path , filename ))
182181 return os .path .join (base_path , filename )
183182
184183
184+ def try_remove_file_and_folder (path : str ) -> None :
185+ """Tries to remove a file and the containing folder if empty
186+
187+ :param path: An absolute path to the file
188+ """
189+ try :
190+ os .remove (path )
191+ # Remove the containing folder if empty
192+ folder = os .path .dirname (path )
193+ if not os .listdir (folder ):
194+ os .rmdir (folder )
195+ except :
196+ pass
197+
198+
185199def _get_modversion_paths (mod_name : str , friendly_version : str ) -> Tuple [str , str ]:
186200 mod_name_sec = secure_filename (mod_name )
187- storage_base = os .path .join (f'{ secure_filename (current_user .username )} _{ current_user .id !s} ' ,
188- mod_name_sec )
201+ base_path = os .path .join (current_user .base_path (), mod_name_sec )
189202 storage = _cfg ('storage' )
190203 if not storage :
191- return ( '' , '' )
192- storage_path = os .path .join (storage , storage_base )
204+ return '' , ''
205+ storage_path = os .path .join (storage , base_path )
193206 filename = f'{ mod_name_sec } -{ friendly_version } .zip'
194207 if not os .path .exists (storage_path ):
195208 os .makedirs (storage_path )
196209 full_path = os .path .join (storage_path , filename )
197210 # Return tuple of (full path, relative path)
198- return ( full_path , os .path .join (storage_base , filename ) )
211+ return full_path , os .path .join (base_path , filename )
199212
200213
201214def serialize_mod_list (mods : Iterable [Mod ]) -> Iterable [Dict [str , Any ]]:
@@ -458,13 +471,23 @@ def update_mod_background(mod_id: int) -> Dict[str, Any]:
458471 mod = _get_mod (mod_id )
459472 _check_mod_editable (mod )
460473 seq_mod_name = secure_filename (mod .name )
461- base_name = f'{ seq_mod_name } -{ time .time ()!s } '
462- base_path = os . path . join ( f' { secure_filename ( mod .user . username ) } _ { mod . user . id !s } ' , seq_mod_name )
463- new_path = _update_image (mod . background , base_name , base_path )
474+ base_name = f'{ seq_mod_name } -{ int ( time .time ()) } '
475+ old_path = mod .background
476+ new_path = _update_image (old_path , base_name , mod . base_path () )
464477 if new_path :
465478 mod .background = new_path
479+ # Remove the old thumbnail
480+ storage = _cfg ('storage' )
481+ if storage :
482+ if mod .thumbnail :
483+ try_remove_file_and_folder (os .path .join (storage , mod .thumbnail ))
484+ if old_path and (calc_path := thumb_path_from_background_path (old_path )) != mod .thumbnail :
485+ try_remove_file_and_folder (os .path .join (storage , calc_path ))
486+ mod .thumbnail = None
487+ # Generate the new thumbnail
488+ mod .background_thumb ()
466489 notify_ckan (mod , 'update-background' )
467- return {'path' : '/content/' + new_path }
490+ return {'path' : mod . background_url ( _cfg ( 'protocol' ), _cfg ( 'cdn-domain' )) }
468491 return {'path' : None }
469492
470493
@@ -476,12 +499,13 @@ def update_user_background(username: str) -> Union[Dict[str, Any], Tuple[Dict[st
476499 if not current_user .admin and current_user .username != username :
477500 return {'error' : True , 'reason' : 'You are not authorized to edit this user\' s background' }, 403
478501 user = User .query .filter (User .username == username ).first ()
479- base_name = secure_filename (user .username )
480- base_path = f'{ base_name } - { time .time ()!s } _ { user . id !s } '
481- new_path = _update_image (user .backgroundMedia , base_name , base_path )
502+ seq_username = secure_filename (user .username )
503+ base_name = f'{ seq_username } -header- { int ( time .time ()) } '
504+ new_path = _update_image (user .backgroundMedia , base_name , user . base_path () )
482505 if new_path :
483506 user .backgroundMedia = new_path
484- return {'path' : '/content/' + new_path }
507+ # The frontend needs the new path so it can show the updated image
508+ return {'path' : user .background_url (_cfg ('protocol' ), _cfg ('cdn-domain' ))}
485509 return {'path' : None }
486510
487511
@@ -605,6 +629,7 @@ def create_mod() -> Tuple[Dict[str, Any], int]:
605629 return {'error' : True , 'reason' : 'Only users with public profiles may create mods.' }, 403
606630 mod_name = request .form .get ('name' )
607631 short_description = request .form .get ('short-description' )
632+ description = request .form .get ('description' , default_description )
608633 mod_friendly_version = secure_filename (request .form .get ('version' , '' ))
609634 # 'game' is deprecated, but kept for compatibility
610635 game_id = request .form .get ('game-id' ) or request .form .get ('game' )
@@ -662,7 +687,7 @@ def create_mod() -> Tuple[Dict[str, Any], int]:
662687 mod = Mod (user = current_user ,
663688 name = mod_name ,
664689 short_description = short_description ,
665- description = default_description ,
690+ description = description ,
666691 license = mod_licence ,
667692 ckan = (request .form .get ('ckan' , '' ).lower () in TRUE_STR ),
668693 game = game ,
@@ -735,6 +760,7 @@ def update_mod(mod_id: int) -> Tuple[Dict[str, Any], int]:
735760 if mod .versions :
736761 version .sort_index = max (v .sort_index for v in mod .versions ) + 1
737762 version .mod = mod
763+ version .download_size = os .path .getsize (full_path )
738764 mod .default_version = version
739765 mod .updated = datetime .now ()
740766 db .commit ()
0 commit comments