1717import uuid
1818from pathlib import Path
1919
20- VERSION = "1.1 .0"
20+ VERSION = "1.2 .0"
2121CONFIG_DIR = Path .home () / ".config" / "image-upload-transit"
2222
2323IMAGE_EXTENSIONS = {".jpg" , ".jpeg" , ".png" , ".gif" , ".webp" , ".avif" , ".heic" , ".bmp" , ".tiff" , ".tif" , ".svg" }
@@ -234,22 +234,58 @@ def main() -> int:
234234 parser = argparse .ArgumentParser (
235235 description = "Upload images/videos to GCS with short transitapp.com URLs" ,
236236 prog = "image-upload-transit" ,
237+ epilog = """
238+ Examples:
239+ %(prog)s image.png Upload a single image
240+ %(prog)s *.jpg Upload multiple images
241+ %(prog)s -s screenshot.png Upload to staging environment
242+ %(prog)s --json photo.jpg Output result as JSON (for scripting/agents)
243+
244+ JSON Output Format (--json):
245+ Success: {"file": "image.png", "url": "https://img.transitapp.com/abc123.png", "success": true}
246+ Error: {"file": "bad.txt", "error": "Unsupported file type", "success": false}
247+ Multiple files produce one JSON object per line (JSONL format).
248+
249+ Supported formats: images (jpg, png, gif, webp, avif, heic, bmp, tiff, svg) and videos (mp4, mov, webm).
250+ Size limits: 25MB for images, 100MB for videos.
251+ """ ,
252+ formatter_class = argparse .RawDescriptionHelpFormatter ,
237253 )
238254 parser .add_argument ("files" , nargs = "*" , help = "Files to upload" )
239255 parser .add_argument ("-s" , "--staging" , action = "store_true" , help = "Use staging environment" )
240256 parser .add_argument ("-v" , "--version" , action = "version" , version = f"%(prog)s { VERSION } " )
257+ parser .add_argument ("--json" , action = "store_true" , help = "Output results as JSON (one object per line, for scripting/agents)" )
241258 parser .add_argument ("--refresh-credentials" , action = "store_true" , help = "Force re-fetch credentials from 1Password" )
242259
243260 args = parser .parse_args ()
244261
262+ def output_result (file : str , url : str = None , err : str = None ) -> None :
263+ if args .json :
264+ result = {"file" : file , "success" : err is None }
265+ if url :
266+ result ["url" ] = url
267+ if err :
268+ result ["error" ] = err
269+ print (json .dumps (result ))
270+ elif err :
271+ error (err )
272+ else :
273+ success (f"{ file } -> { url } " )
274+
245275 if args .refresh_credentials and not args .files :
246276 try :
247277 get_credentials (is_staging = args .staging , force_refresh = True )
248278 env_name = "staging" if args .staging else "production"
249- success (f"Credentials refreshed for { env_name } " )
279+ if args .json :
280+ print (json .dumps ({"action" : "refresh_credentials" , "environment" : env_name , "success" : True }))
281+ else :
282+ success (f"Credentials refreshed for { env_name } " )
250283 return 0
251284 except CredentialsError as e :
252- error (str (e ))
285+ if args .json :
286+ print (json .dumps ({"action" : "refresh_credentials" , "error" : str (e ), "success" : False }))
287+ else :
288+ error (str (e ))
253289 return 2
254290
255291 if not args .files :
@@ -262,19 +298,22 @@ def main() -> int:
262298 try :
263299 credentials = get_credentials (is_staging = args .staging , force_refresh = args .refresh_credentials )
264300 except CredentialsError as e :
265- error (str (e ))
301+ if args .json :
302+ print (json .dumps ({"error" : str (e ), "success" : False }))
303+ else :
304+ error (str (e ))
266305 return 2
267306
268307 exit_code = 0
269308 for filepath in args .files :
270309 try :
271310 url = upload_file (filepath , bucket , base_url , credentials )
272- success ( f" { filepath } -> { url } " )
311+ output_result ( filepath , url = url )
273312 except ValueError as e :
274- error ( str (e ))
313+ output_result ( filepath , err = str (e ))
275314 exit_code = 1
276315 except CredentialsError as e :
277- error ( str (e ))
316+ output_result ( filepath , err = str (e ))
278317 return 2
279318
280319 return exit_code
0 commit comments