11# /// script
2+ # requires-python = ">=3.13"
23# dependencies = [
3- # "requests<3",
4+ # "httpx~=0.28.1",
5+ # "colorama",
46# ]
57# ///
68from pathlib import Path
7- from argparse import ArgumentParser
8- import requests
9+ from colorama import init , Fore
10+
11+ import argparse
12+ import textwrap
13+ import httpx
914import json
1015import re
1116
17+ # Initialize colorama
18+ init (autoreset = True )
19+
20+
21+ DROPBOX_PERMISSION_MESSAGE = f"""
22+ { Fore .YELLOW }
23+ ----------------------------------------------------
24+ Make sure the dropbox App includes these permissions
25+ - files.metadata.read
26+ - files.content.write
27+ - files.content.read
28+ - sharing.write
29+ - sharing.read
30+ """
31+
32+
33+ def dropbox_request_error_handler (res : httpx .Response ):
34+ try :
35+ res .raise_for_status ()
36+ except httpx .HTTPStatusError as http_err :
37+ print (f"{ Fore .RED } HTTP error occurred while requesting { res .url } : { http_err } " )
38+ print (f"{ Fore .RED } Response content: { res .text } " )
39+ raise
40+ except httpx .RequestError as req_err :
41+ print (
42+ f"{ Fore .RED } An error occurred while making the request to { res .url } : { req_err } "
43+ )
44+ raise
45+ except Exception as err :
46+ print (f"{ Fore .RED } An unexpected error occurred: { err } " )
47+ raise
48+ finally :
49+ print (DROPBOX_PERMISSION_MESSAGE )
50+
51+
1252def dropbox_request (endpoint : str , data : object , * , access_token : str ):
1353 url = f"https://api.dropboxapi.com/2/{ endpoint } "
1454 headers = {
1555 "Authorization" : f"Bearer { access_token } " ,
1656 "Content-Type" : "application/json" ,
1757 }
18- res = requests .post (
58+ res = httpx .post (
1959 url ,
2060 headers = headers ,
2161 data = json .dumps (data ),
2262 )
23- res . raise_for_status ( )
63+ dropbox_request_error_handler ( res )
2464 return res .json ()
2565
26- def dropbox_content_request (endpoint : str , path : str , data : object , * , access_token : str ):
66+
67+ def dropbox_content_request (
68+ endpoint : str , path : str , data : object , * , access_token : str
69+ ):
2770 url = f"https://content.dropboxapi.com/2/{ endpoint } "
2871 headers = {
2972 "Authorization" : f"Bearer { access_token } " ,
3073 "Content-Type" : "application/octet-stream" ,
31- "Dropbox-API-Arg" : json .dumps ({
32- "path" : path ,
33- "mode" : "overwrite" , # overwrite if exists
34- "autorename" : False ,
35- "mute" : False
36- })
74+ "Dropbox-API-Arg" : json .dumps (
75+ {
76+ "path" : path ,
77+ "mode" : "overwrite" , # overwrite if exists
78+ "autorename" : False ,
79+ "mute" : False ,
80+ }
81+ ),
3782 }
38- res = requests .post (
83+ res = httpx .post (
3984 url ,
4085 headers = headers ,
4186 data = json .dumps (data ).encode ("utf-8" ),
4287 )
43- res . raise_for_status ( )
88+ dropbox_request_error_handler ( res )
4489 return res .json ()
4590
91+
4692def list_all_files (folder_path : str , * , access_token : str ):
4793 ALLOWED_EXTENSIONS = {".jpg" , ".jpeg" , ".png" , ".webp" }
4894 files = []
@@ -65,11 +111,14 @@ def list_all_files(folder_path: str, *, access_token: str):
65111 files = sorted (files , key = lambda file : file ["name" ].lower ())
66112 # Filter out only files (not folders) that are supported
67113 files = [
68- file for file in files
69- if file [".tag" ] == "file" and Path (file ["name" ]).suffix .lower () in ALLOWED_EXTENSIONS
114+ file
115+ for file in files
116+ if file [".tag" ] == "file"
117+ and Path (file ["name" ]).suffix .lower () in ALLOWED_EXTENSIONS
70118 ]
71119 return files
72120
121+
73122def share_file_and_get_links (files , * , access_token : str ):
74123 total = len (files )
75124 images = []
@@ -88,34 +137,40 @@ def share_file_and_get_links(files, *, access_token: str):
88137 if res .get ("links" ):
89138 link = res ["links" ][0 ]["url" ]
90139 else :
91- data = {
92- "path" : path ,
93- "settings" : {
94- "requested_visibility" : "public"
95- }
96- }
140+ data = {"path" : path , "settings" : {"requested_visibility" : "public" }}
97141 res_create = dropbox_request (
98142 "sharing/create_shared_link_with_settings" ,
99143 data ,
100144 access_token = access_token ,
101145 )
102146 link = res_create ["url" ]
103147
104- raw_url = re .sub (r' &dl=0\b' , '' , link ) + ' &raw=1'
148+ raw_url = re .sub (r" &dl=0\b" , "" , link ) + " &raw=1"
105149
106- images .append ({
107- "id" : i + 1 ,
108- "file_name" : actual_path ,
109- "coco_url" : raw_url ,
110- })
150+ images .append (
151+ {
152+ "id" : i + 1 ,
153+ "file_name" : actual_path ,
154+ "coco_url" : raw_url ,
155+ }
156+ )
111157 return images
112158
113159
114160def main ():
115- parser = ArgumentParser (description = "Generate COCO file from images folder." )
161+ parser = argparse .ArgumentParser (
162+ description = "Generate COCO file from images folder." ,
163+ formatter_class = argparse .RawDescriptionHelpFormatter ,
164+ epilog = textwrap .dedent (DROPBOX_PERMISSION_MESSAGE ),
165+ )
116166 parser .add_argument ("access_token" , help = "Access token for authentication" )
117- parser .add_argument ("images_folder" , help = "Path to the images folder" )
118- parser .add_argument ("export_file_name" , help = "Name of the export COCO file" )
167+ parser .add_argument (
168+ "images_folder" , help = 'Path to the images folder in dropbox. eg: "/COCO TEST"'
169+ )
170+ parser .add_argument (
171+ "export_file_name" ,
172+ help = "Name of the export COCO file to be created in dropbox under provided images_folder" ,
173+ )
119174
120175 args = parser .parse_args ()
121176
@@ -141,17 +196,18 @@ def main():
141196 dropbox_content_request (
142197 "files/upload" ,
143198 absolute_export_file_name ,
144- { "images" : public_images },
199+ {"images" : public_images },
145200 access_token = access_token ,
146201 )
147202
148203 # Get temporary link
149204 res = dropbox_request (
150205 "files/get_temporary_link" ,
151- { "path" : absolute_export_file_name },
206+ {"path" : absolute_export_file_name },
152207 access_token = access_token ,
153208 )
154- print (f"COCO file available at { res ["link" ]} " )
209+ print (f"COCO file available at { res ['link' ]} " )
210+
155211
156212if __name__ == "__main__" :
157213 main ()
0 commit comments