@@ -153,31 +153,33 @@ def init_spotify_client(cfg: configparser.ConfigParser) -> spotipy.Spotify:
153153 return spotipy .Spotify (auth_manager = auth , retries = 10 )
154154
155155
156- def sanitize_filename (name : str , ext : str ) -> str :
156+ def sanitize_filename (name : str ) -> str :
157157 """Convert a playlist name into a safe filename."""
158158 safe = "" .join (c if c .isalnum () or c in (" " , "_" , "-" ) else "_" for c in name )
159- return f"{ safe .strip ().replace (' ' , '_' ).lower ()} . { ext } "
159+ return f"{ safe .strip ().replace (' ' , '_' ).lower ()} "
160160
161161
162- def write_file (file_path : Path , data : list [dict ], file_format : str = "csv" ) -> None :
162+ def write_file (file_path : Path , data : list [dict ], file_formats ) -> None :
163163 """Write list of dicts to file."""
164164 if not data :
165165 logger .warning ("No data to write; skipping file." )
166166 return
167167
168- if file_format == "csv" :
168+ if "csv" in file_formats :
169169 headers = list (data [0 ].keys ())
170- with file_path .open ("w" , newline = "" , encoding = "utf-8" ) as csvfile :
170+ with file_path .with_suffix (".csv" ).open (
171+ "w" , newline = "" , encoding = "utf-8"
172+ ) as csvfile :
171173 writer = csv .DictWriter (csvfile , fieldnames = headers )
172174 writer .writeheader ()
173175 for row in data :
174176 writer .writerow (row )
177+ logger .info (f"Exported to { file_path } .csv" )
175178
176- elif file_format == "json" :
177- with file_path .open ("w" , encoding = "utf-8" ) as jsonfile :
179+ if "json" in file_formats :
180+ with file_path .with_suffix ( ".json" ). open ("w" , encoding = "utf-8" ) as jsonfile :
178181 json .dump (data , jsonfile , ensure_ascii = False , indent = 4 )
179-
180- logger .info (f"Exported to { file_path } " )
182+ logger .info (f"Exported to { file_path } .json" )
181183
182184
183185class SpotifyExporter :
@@ -186,7 +188,7 @@ class SpotifyExporter:
186188 def __init__ (
187189 self ,
188190 spotify_client : spotipy .Spotify ,
189- file_format : str ,
191+ file_formats : list [ str ] ,
190192 include_uris : bool ,
191193 external_ids : bool ,
192194 with_bar : bool ,
@@ -195,7 +197,7 @@ def __init__(
195197 ) -> None :
196198 """Initialize the exporter with a Spotify client."""
197199 self .spotify = spotify_client
198- self .file_format = file_format
200+ self .file_formats = file_formats
199201 self .include_uris = include_uris
200202 self .external_ids = external_ids
201203 self .with_bar = with_bar
@@ -345,7 +347,7 @@ def export_playlist(self, playlist: dict, output_dir: Path) -> None:
345347 """Export a single playlist to CSV file."""
346348 name , pid = playlist ["name" ], playlist ["id" ]
347349 output_dir .mkdir (parents = True , exist_ok = True )
348- filepath = output_dir / sanitize_filename (name , self . file_format )
350+ filepath = output_dir / sanitize_filename (name )
349351
350352 # Format description for progress bar
351353 desc = (
@@ -463,12 +465,13 @@ def export_playlist(self, playlist: dict, output_dir: Path) -> None:
463465 record .pop ("Track ISRC" , None )
464466 record .pop ("Album UPC" , None )
465467
466- write_file (filepath , export_data , self .file_format )
468+ write_file (filepath , export_data , self .file_formats )
467469 self .exported_playlists += 1
468470 self .exported_tracks += len (export_data )
469- click .echo (
470- f"Exported { len (export_data )} tracks from '{ name } ' to { filepath } " ,
471- )
471+ for ext in self .file_formats :
472+ click .echo (
473+ f"Exported { len (export_data )} tracks from '{ name } ' to { filepath } .{ ext } " ,
474+ )
472475
473476
474477# Custom command class to override usage line
@@ -518,9 +521,10 @@ def format_usage(self, ctx, formatter) -> None:
518521 "-f" ,
519522 "--format" ,
520523 "format_param" ,
521- type = click . Choice ([ "csv" , "json" ]) ,
524+ multiple = True ,
522525 default = None ,
523- help = "Output file format (defaults to 'csv')." ,
526+ type = click .Choice (["csv" , "json" ]),
527+ help = "Output file format (defaults to 'csv'); repeatable." ,
524528)
525529@optgroup .option (
526530 "--uris" ,
@@ -592,7 +596,15 @@ def main(
592596 cfg = load_config (cfg_path )
593597
594598 # Resolve config vs CLI
595- file_format = format_param if format_param else cfg .get ("exportify-cli" , "format" )
599+ file_formats = format_param if format_param else None
600+ if file_formats is None :
601+ file_formats = []
602+ if "csv" in cfg .get ("exportify-cli" , "format" ):
603+ file_formats .append ("csv" )
604+ if "json" in cfg .get ("exportify-cli" , "format" ):
605+ file_formats .append ("json" )
606+ file_formats = list (set (file_formats ))
607+
596608 output = output_param if output_param else cfg .get ("exportify-cli" , "output" )
597609 include_uris = (
598610 uris_flag if uris_flag is not None else cfg .getboolean ("exportify-cli" , "uris" )
@@ -657,7 +669,7 @@ def main(
657669
658670 exporter = SpotifyExporter (
659671 spotify_client = client ,
660- file_format = file_format ,
672+ file_formats = file_formats ,
661673 include_uris = include_uris ,
662674 external_ids = external_ids ,
663675 with_bar = with_bar ,
0 commit comments