Files
parentzone_downloader/__pycache__/image_downloader.cpython-313.pyc

192 lines
23 KiB
Plaintext
Raw Normal View History

2025-10-07 14:52:04 +01:00
<EFBFBD>
ܯ<>h<EFBFBD>M<00><00>&<00>SrSSKrSSKrSSKrSSKrSSKrSSKrSSKrSSKJ r SSK
J r J r SSK JrJrJrJr SSKrSSKJr SSKrSSKJr SSKJr "S S
5rS r\S :Xa\"\"55 gg!\a SrN3f=f!\a SrN:f=f) aG
Image Downloader Script
This script downloads images from a REST API that provides:
1. An endpoint to list all assets
2. An endpoint to download individual assets in full resolution
Usage:
python image_downloader.py --api-url <base_url> --list-endpoint <endpoint> --download-endpoint <endpoint> --output-dir <directory>
<EFBFBD>N)<01>Path)<02>urljoin<69>urlparse)<04>List<73>Dict<63>Any<6E>Optional)<01>tqdm)<01> AuthManager)<01> AssetTrackerc<00>H<00>\rSrSrSS\S\S\S\S\S\S \S
\S \S \4S jjrSrS\ RS\ \ \\ 44SjrS\ \\ 4S\4SjrS\ \\ 4S\S\4SjrS\S\4SjrS\S\4SjrS\ RS\ \\ 4S\R(S\4SjrS S\4SjjrSrg)!<21>ImageDownloader<65>'N<>api_url<72> list_endpoint<6E>download_endpoint<6E>
output_dir<EFBFBD>max_concurrent<6E>timeout<75>api_key<65>email<69>password<72> track_assetsc <00>X<00>URS5UlURS5UlURS5Ul[ U5UlXPlX`lXpl X<>l
X<>l SUl UR RSSS9 [R"[R S[R""UR S- 5[R$"5/S9 [R&"[(5UlSUlU
(aH[.(a=[/[1UR 5S9UlUR*R3S 5 O>U
(aUR*R5S
5 OUR*R3S 5 S S S S S .Ulg)a 
Initialize the image downloader.
Args:
api_url: Base URL of the API
list_endpoint: Endpoint to get the list of assets
download_endpoint: Endpoint to download individual assets
output_dir: Directory to save downloaded images
max_concurrent: Maximum number of concurrent downloads
timeout: Request timeout in seconds
api_key: API key for authentication
email: Email for login authentication
password: Password for login authentication
track_assets: Whether to enable asset tracking to avoid re-downloads
<EFBFBD>/NT)<02>parents<74>exist_okz)%(asctime)s - %(levelname)s - %(message)sz download.log)<03>level<65>format<61>handlers<72><01> storage_dirzAsset tracking enabledz7Asset tracking requested but AssetTracker not availablezAsset tracking disabledr)<04>total<61>
successful<EFBFBD>failed<65>skipped)<1C>rstripr<00>lstriprrrrrrrrr<00> auth_manager<65>mkdir<69>logging<6E> basicConfig<69>INFO<46> FileHandler<65> StreamHandler<65> getLogger<65>__name__<5F>logger<65> asset_trackerr <00>str<74>info<66>warning<6E>stats) <0B>selfrrrrrrrrrrs <20>E/Users/tudorsitaru/projects/parentzone_downloader/image_downloader.py<70>__init__<5F>ImageDownloader.__init__(sQ<00><00>$<1F>~<7E>~<7E>c<EFBFBD>*<2A><04> <0C>*<2A>1<>1<>#<23>6<><04><1A>!2<>!9<>!9<>#<23>!><3E><04><1E><1E>z<EFBFBD>*<2A><04><0F>,<2C><1B><1E> <0C><1E> <0C><1A>
<EFBFBD> <20> <0A> <20><04><19> <0A><0F><0F><1D><1D>d<EFBFBD>T<EFBFBD><1D>:<3A> <10><1B><1B><19>,<2C>,<2C>><3E><17>#<23>#<23>D<EFBFBD>O<EFBFBD>O<EFBFBD>n<EFBFBD>$D<>E<><17>%<25>%<25>'<27><0E>
<EFBFBD><1E>'<27>'<27><08>1<><04> <0B>"<22><04><1A> <17>L<EFBFBD>L<EFBFBD>!-<2D>#<23>d<EFBFBD>o<EFBFBD>o<EFBFBD>:N<>!O<>D<EFBFBD> <1E> <10>K<EFBFBD>K<EFBFBD> <1C> <1C>5<> 6<> <19> <10>K<EFBFBD>K<EFBFBD> <1F> <1F> Y<> Z<> <10>K<EFBFBD>K<EFBFBD> <1C> <1C>6<> 7<><17><1B><17><18> 
<EFBFBD><04>
<EFBFBD>c<00><00>f# <00>UR(a<>UR(a<>[(a<>URR S5 [UR
5UlUR RURUR5IShv<01>N nU(aURR S5 gURRS5 [S5eUR(dUR(a&URRS5 [S5egN<>7f)z9Perform login authentication if credentials are provided.z"Attempting login authentication...NzLogin authentication successfulzLogin authentication failedzABoth email and password must be provided for login authentication) rrr r2r5rr)<00>login<69>error<6F> Exceptionr6)r8<00>successs r9<00> authenticate<74>ImageDownloader.authenticatees<><00><00><00> <0F>:<3A>:<3A>$<24>-<2D>-<2D>K<EFBFBD>K<EFBFBD> <10>K<EFBFBD>K<EFBFBD> <1C> <1C>A<> B<> +<2B>D<EFBFBD>L<EFBFBD>L<EFBFBD> 9<>D<EFBFBD> <1D> <20>-<2D>-<2D>3<>3<>D<EFBFBD>J<EFBFBD>J<EFBFBD><04> <0A> <0A>N<>N<>G<EFBFBD><16><14> <0B> <0B> <20> <20>!B<>C<><14> <0B> <0B>!<21>!<21>"?<3F>@<40><1F> =<3D>><3E>><3E> <11>Z<EFBFBD>Z<EFBFBD>4<EFBFBD>=<3D>=<3D> <10>K<EFBFBD>K<EFBFBD> <1F> <1F> c<> d<><1B>_<>`<60> `<60>)<29>O<01>s<00>BD1<01>D/<04>BD1<01>session<6F>returnc<00><00># <00>[URUR5nURR SU35 0nUR
(aUR
US'OYUR (aHUR R5(a)URUR R55 URX#URS9IShv<01>N nUR5 UR5IShv<01>N n[U[5(aUnOT[U[ 5(a(SU;aUSnO3SU;aUSnO'SU;aUSnOU/nO[#S[%U535eURR S ['U5S
35 UsSSS5IShv<01>N $N<>N<>N !,IShv<01>N (df  g=f![(a$nURR+S U35 eSnAff=f7f) z<>
Fetch the list of assets from the API.
Args:
session: aiohttp session for making requests
Returns:
List of asset dictionaries
zFetching asset list from: z x-api-key)<02>headersrN<>data<74>results<74>itemszUnexpected response format: <20>Found z assetszFailed to fetch asset list: )rrrr2r5rr)<00>is_authenticated<65>update<74>get_auth_headers<72>getr<00>raise_for_status<75>json<6F>
isinstance<EFBFBD>list<73>dict<63>
ValueError<EFBFBD>type<70>lenr@r?)r8rD<00>urlrG<00>responserH<00>assets<74>es r9<00>get_asset_list<73>ImageDownloader.get_asset_listus<><00><00><00><16>d<EFBFBD>l<EFBFBD>l<EFBFBD>D<EFBFBD>$6<>$6<>7<><03> <0C> <0B> <0B><18><18>5<>c<EFBFBD>U<EFBFBD>;<3B><<3C>$ <12><18>G<EFBFBD><14>|<7C>|<7C>'+<2B>|<7C>|<7C><07> <0B>$<24><16>"<22>"<22>t<EFBFBD>'8<>'8<>'I<>'I<>'K<>'K<><17><0E><0E>t<EFBFBD>0<>0<>A<>A<>C<>D<><1E>{<7B>{<7B>3<EFBFBD><14><1C><1C>{<7B>N<>N<>RZ<52><18>)<29>)<29>+<2B>%<25>]<5D>]<5D>_<EFBFBD>,<2C><04><1E>d<EFBFBD>D<EFBFBD>)<29>)<29>!<21>F<EFBFBD><1F><04>d<EFBFBD>+<2B>+<2B><1D><14>~<7E>!%<25>f<EFBFBD><1C><06>"<22>d<EFBFBD>*<2A>!%<25>i<EFBFBD><1F><06> <20>D<EFBFBD><1F>!%<25>g<EFBFBD><1D><06>"&<26><16><06>$<24>'C<>D<EFBFBD><14>J<EFBFBD><<3C>%P<>Q<>Q<><14> <0B> <0B> <20> <20>6<EFBFBD>#<23>f<EFBFBD>+<2B><1D>g<EFBFBD>!><3E>?<3F><1D>+O<01>N<>N<>,<2C>O<01>N<>N<>N<><4E>.<19> <12> <10>K<EFBFBD>K<EFBFBD> <1D> <1D> <<3C>Q<EFBFBD>C<EFBFBD>@<40> A<> <11><> <12>s<EFBFBD><00>?H<01>BG<00>F0<06>G<00> $F6<03>F2<06>BF6<03> G<00>*F4<06>+G<00>/H<01>0G<00>2F6<03>4G<00>6G <07><F? <0C>= G <07> G<00> H<01> G<00>
G><03>G9<03>9G><03>>H<01>assetc <00><><00>Sn/SQnUHnXA;dM
Xn O Uc [U5nSSKJn URUR SS5S.n[ UR SUS U"U535nU$)
z<EFBFBD>
Generate the download URL for an asset.
Args:
asset: Asset dictionary from the API
Returns:
Download URL for the asset
N)<06>id<69>asset_id<69>image_id<69>file_id<69>uuid<69>keyr)<01> urlencode<64>updated<65>)re<00>uz
/v1/media/z/full?)r4<00> urllib.parserfrrOrr)r8r^ra<00> id_fields<64>fieldrf<00>params<6D> download_urls r9<00>get_download_url<72> ImageDownloader.get_download_url<72>s<><00><00><18><08>M<01> <09><1E>E<EFBFBD><14>~<7E> <20><<3C><08><15><1F>
<14> <1B><1A>5<EFBFBD>z<EFBFBD>H<EFBFBD> +<2B><18><<3C><<3C><16><19><19>9<EFBFBD>b<EFBFBD>)<29>
<EFBFBD><06>
<1F>t<EFBFBD>|<7C>|<7C>z<EFBFBD>(<28><1A>6<EFBFBD>)<29>TZ<54>J[<5B>I\<5C>-]<5D>^<5E> <0C><1B>r<rXc<00>f<00>SU;aUSnO<>SU;aUSnO<>SU;aUSnO<>SU;aUSnOv[U5n[RRUR5nSU;a<SU;aUR US5nOSU;aUR US5nOSnX5- nUR U5nS nUnUR U- R5(aS[RRU5up<>US
UU3nUS - nUR U- R5(aMSU$) z<>
Generate a filename for the downloaded asset.
Args:
asset: Asset dictionary from the API
url: Download URL
Returns:
Filename for the asset
<EFBFBD>fileName<6D>filename<6D>name<6D>title<6C>.<2E>mimeType<70> content_type<70>.jpg<70><00>_) r<00>os<6F>path<74>basename<6D>_get_extension_from_mime<6D>_sanitize_filenamer<00>exists<74>splitext) r8r^rXrs<00>
parsed_url<EFBFBD>ext<78>counter<65>original_filenamerts r9<00> get_filename<6D>ImageDownloader.get_filename<6D>sH<00><00> <16><15> <1E><1C>Z<EFBFBD>(<28>H<EFBFBD> <17>5<EFBFBD> <20><1C>Z<EFBFBD>(<28>H<EFBFBD> <13>u<EFBFBD>_<EFBFBD><1C>V<EFBFBD>}<7D>H<EFBFBD> <14><05> <1D><1C>W<EFBFBD>~<7E>H<EFBFBD>"<22>#<23><1D>J<EFBFBD><19>w<EFBFBD>w<EFBFBD>'<27>'<27>
<EFBFBD><0F><0F>8<>H<EFBFBD><13>(<28>"<22><1D><15>&<26><1E>7<>7<><05>j<EFBFBD>8I<38>J<>C<EFBFBD>#<23>u<EFBFBD>,<2C><1E>7<>7<><05>n<EFBFBD>8M<38>N<>C<EFBFBD> <20>C<EFBFBD><18><0F><08><18>*<2A>*<2A>8<EFBFBD>4<><08><14><07>$<24><19><13><EFBFBD><EFBFBD><18>)<29>1<>1<>3<>3<><1A><07><07>(<28>(<28>):<3A>;<3B>I<EFBFBD>D<EFBFBD><1E><16>q<EFBFBD><17> <09>#<23><15>/<2F>H<EFBFBD> <13>q<EFBFBD>L<EFBFBD>G<EFBFBD><14><EFBFBD><EFBFBD><18>)<29>1<>1<>3<>3<>
<18>r<<00> mime_typec <00>X<00>SSSSSSSSS.nURUR5S5$) z"Get file extension from MIME type.ryz.pngz.gifz.webpz.bmpz.tiffz.svg)z
image/jpegz image/jpgz image/pngz image/gifz
image/webpz image/bmpz
image/tiffz image/svg+xml)rO<00>lower)r8r<><00> mime_to_exts r9r<00>(ImageDownloader._get_extension_from_mime<6D>s=<00><00>!<21><1F><1F><1F>!<21><1F>!<21>#<23> 
<EFBFBD> <0B><1B><EFBFBD><EFBFBD>y<EFBFBD><EFBFBD><EFBFBD>0<>&<26>9<>9r<rsc<00>t<00>SnUHnURUS5nM URS5nU(dSnU$)z1Sanitize filename by removing invalid characters.z <>:"/\|?*r{z. <20>image)<02>replace<63>strip)r8rs<00> invalid_chars<72>chars r9r<><00>"ImageDownloader._sanitize_filename sD<00><00>%<25> <0A>!<21>D<EFBFBD><1F>'<27>'<27><04>c<EFBFBD>2<>H<EFBFBD>"<22><1C>><3E>><3E>$<24>'<27><08><18><1E>H<EFBFBD><17>r<<00> semaphorec <00><00>d# <00>UIShv<01>N URU5nURX$5nURU- nUR5(aYUR(dHUR
R SUS35 URS==S- ss'SSS5IShv<01>N gUR
R SUSU35 URX@RS 9IShv<01>N nUR5 URRS
S 5nURS 5(dUR
RS U35 [R"US5IShv<01>N n UR R#S5Shv<01>N n
U R%U
5IShv<01>N M$GN<>GN N<>NKN)N
SSS5IShv<01>N O!,IShv<01>N (df  O=fSU;a<>SSKJn SSKn U R+USR-SS55n U R."XmR15U R1545 UR
R SUS35 O5![2a(nUR
RSU35 SnAOSnAff=fSSS5IShv<01>N O!,IShv<01>N (df  O=fUR(aURR5X&S5 UR
R SU35 URS==S- ss'SSS5IShv<01>N g![2a<>nUR(aMURU5nURX$5nURU- nURR5X&S5 UR
R7SURSS5SU35 URS==S- ss'SnASSS5IShv<01>N gSnAff=f!,IShv<01>N (df  g=f7f)z<>
Download a single asset.
Args:
session: aiohttp session for making requests
asset: Asset dictionary from the API
semaphore: Semaphore to limit concurrent downloads
Returns:
True if download was successful, False otherwise
Nz Skipping z (already exists)r&rzTz Downloading z from )rz content-typerhzimage/zContent type is not an image: <20>wbi rgr)<01>datetime<6D>Zz+00:00zSet file modification time to z&Failed to set file modification time: zSuccessfully downloaded r$FzFailed to download asset r`<00>unknownz: r%)ror<>rr<>r3r2r5r7rOrrPrG<00>
startswithr6<00>aiofiles<65>open<65>content<6E> iter_chunked<65>writer<65>r|<00> fromisoformatr<74><00>utime<6D> timestampr@<00>mark_asset_downloadedr?)r8rDr^r<>rnrs<00>filepathrYrx<00>f<>chunkr<6B>r|<00> updated_timer[s r9<00>download_asset<65>ImageDownloader.download_assetsk<00><00><00><1D>9<EFBFBD>9 <1D>#<23>4<>4<>U<EFBFBD>;<3B> <0C><1F>,<2C>,<2C>U<EFBFBD>A<><08><1F>?<3F>?<3F>X<EFBFBD>5<><08><1C>?<3F>?<3F>$<24>$<24>T<EFBFBD>-?<3F>-?<3F><18>K<EFBFBD>K<EFBFBD>$<24>$<24>y<EFBFBD><18>
<EFBFBD>:K<>%L<>M<><18>J<EFBFBD>J<EFBFBD>y<EFBFBD>)<29>Q<EFBFBD>.<2E>)<29><1F><1D>9<EFBFBD>9<EFBFBD><15> <0B> <0B> <20> <20><<3C><08>z<EFBFBD><16> <0C>~<7E>!N<>O<>"<22>;<3B>;<3B>|<7C>\<5C>\<5C>;<3B>J<>J<>h<EFBFBD><1C>-<2D>-<2D>/<2F>$,<2C>#3<>#3<>#7<>#7<><0E><02>#K<>L<EFBFBD>'<27>2<>2<>8<EFBFBD><<3C><<3C><1C> <0B> <0B>+<2B>+<2B>.L<>\<5C>N<EFBFBD>,[<5B>\<5C> (<28>}<7D>}<7D>X<EFBFBD>t<EFBFBD><<3C><<3C><01>+3<>+;<3B>+;<3B>+H<>+H<><14>+N<>1<>%<25>"#<23>'<27>'<27>%<25>.<2E>0<>0<>3<1D>K<01> =<3D>1<>0<>,O<01> =<3D><<3C><<3C><<3C><<3C><<3C><>
!<21>E<EFBFBD>)<29> ^<01>9<>%<25>+3<>+A<>+A<>%<25> <09>BR<42>BZ<42>BZ<42>[^<5E>`h<>Bi<42>+j<>L<EFBFBD><1E>H<EFBFBD>H<EFBFBD>X<EFBFBD>0F<30>0F<30>0H<30>,<2C>J`<60>J`<60>Jb<4A>/c<>d<> <20>K<EFBFBD>K<EFBFBD>,<2C>,<2C>/M<>e<EFBFBD>T]<5D>N^<5E>M_<4D>-`<60>a<><61>(<28>^<01> <20>K<EFBFBD>K<EFBFBD>/<2F>/<2F>2X<32>YZ<59>X[<5B>0\<5C>]<5D>]<5D><>^<01><>/K<01>J<>J<>J<>J<>J<><4A>6<18>%<25>%<25><18>&<26>&<26><<3C><<3C>U<EFBFBD>d<EFBFBD>S<><14> <0B> <0B> <20> <20>#;<3B>H<EFBFBD>:<3A>!F<>G<><14>
<EFBFBD>
<EFBFBD><<3C>(<28>A<EFBFBD>-<2D>(<28><1B>]<1D>9<EFBFBD>9<EFBFBD><39>`<1D>
<1D><17>%<25>%<25>#'<27>#8<>#8<><15>#?<3F>L<EFBFBD>#<23>0<>0<><15>E<>H<EFBFBD>#<23><EFBFBD><EFBFBD><18>9<>H<EFBFBD><18>&<26>&<26><<3C><<3C>U<EFBFBD>e<EFBFBD>T<><14> <0B> <0B>!<21>!<21>$=<3D>e<EFBFBD>i<EFBFBD>i<EFBFBD><04>i<EFBFBD>>X<>=Y<>Y[<5B>\]<5D>[^<5E>"_<>`<60><14>
<EFBFBD>
<EFBFBD>8<EFBFBD>$<24><01>)<29>$<24><1C>u<1D>9<EFBFBD>9<EFBFBD><39>`
<1D><>a<1D>9<EFBFBD>9<EFBFBD>s<EFBFBD><00>P0<01>F0<06>P0<01>P<03>B M<02> P0<01>(F3<04>)P0<01>.>M<02>,F6<08>-M<02>0A<J9<05>,F8
<EFBFBD>-J9<05>0G<07> F><08>F: <0C>F><08>G<07>)F< <0C>*G<07>0P0<01>3P0<01>6M<02>8J9<05>:F><08><G<07>>G<07>? J9<05>
G <08> J9<05>G( <0B>G<10> G( <0B>$ J9<05>2A?I2<04>1J9<05>2
J$<07><J<07>J9<05>J$<07>$J9<05>' M<02>2J5<06>3M<02>9K <09>?K<0E> K <09> A)M<02>6 P0<01>M<04>P0<01>
P<05>B&P<05>8P<03>< P0<01>P
<04>P0<01>P<05>P<03>P-<07>P <0C> P-<07>)P0<01>force_redownloadc <00><00># <00>[R"5n[R"SSS9n[R"URS9n[R
"X4S9IShv<01>N nUR 5IShv<01>N URU5IShv<01>N nURRS[U5S35 U(d-URRS 5 SSS5IShv<01>N gUR(a<>U(d<>URRU5nURRS
[U5S 35 [U5S :Xa-URRS 5 SSS5IShv<01>N gO$UnU(aURRS5 [U5URS'[R "UR"5nUV s/sHn UR%XYU5PM n
n ['[U
5SS9n [R("U
5HZn U IShv<01>N n U R+S5 U R-URSURSURSS.5 M\ SSS5 SSS5IShv<01>N [R"5U-
nURRSUSS35 URRSUR35 gGN<>GN<>GN~GN&GN<>s sn fN<>!,(df  N<>=f![.a$nURR1SU35 eSnAff=fN<>!,IShv<01>N (df  N<>=f7f)zt
Download all assets from the API.
Args:
force_redownload: If True, download all assets regardless of tracking
<EFBFBD>d<00>)<02>limit<69>limit_per_host)r#)<02> connectorrNz
Retrieved z total assets from APIzNo assets found to downloadrKz new/modified assets to downloadrzAll assets are up to date!z1Force redownload enabled - downloading all assetsr#zDownloading assets)r#<00>descrzr$r%r&)<03>Success<73>Failed<65>SkippedzError during download process: zDownload completed in z.2fz secondsz Statistics: )<19>time<6D>aiohttp<74> TCPConnector<6F> ClientTimeoutr<00> ClientSessionrBr\r2r5rWr6r3<00>get_new_assetsr7<00>asyncio<69> Semaphorerr<>r
<00> as_completedrM<00> set_postfixr@r?)r8r<><00>
start_timer<EFBFBD>rrD<00>
all_assetsrZr<>r^<00>tasks<6B>pbar<61>coro<72>resultr[<00> elapsed_times r9<00>download_all_assets<74>#ImageDownloader.download_all_assetsbs<><00><00><00><1A>Y<EFBFBD>Y<EFBFBD>[<5B>
<EFBFBD><1C>(<28>(<28>s<EFBFBD>2<EFBFBD>F<> <09><19>'<27>'<27>d<EFBFBD>l<EFBFBD>l<EFBFBD>;<3B><07><1A>(<28>(<28>9<EFBFBD>N<>N<>RY<52>0 <16><1A>'<27>'<27>)<29>)<29>)<29>$(<28>#6<>#6<>w<EFBFBD>#?<3F>?<3F>
<EFBFBD><14> <0B> <0B> <20> <20>:<3A>c<EFBFBD>*<2A>o<EFBFBD>-><3E>>T<>!U<>V<>!<21><18>K<EFBFBD>K<EFBFBD>'<27>'<27>(E<>F<><1A>O<01>N<>N<><18>%<25>%<25>.><3E>!<21>/<2F>/<2F>><3E>><3E>z<EFBFBD>J<>F<EFBFBD><18>K<EFBFBD>K<EFBFBD>$<24>$<24>v<EFBFBD>c<EFBFBD>&<26>k<EFBFBD>]<5D>:Z<>%[<5B>\<5C><1A>6<EFBFBD>{<7B>a<EFBFBD>'<27><1C> <0B> <0B>(<28>(<28>)E<>F<><1E>'O<01>N<>N<>"(<28>(<28>F<EFBFBD>'<27><1C> <0B> <0B>(<28>(<28>)\<5C>]<5D>&)<29>&<26>k<EFBFBD><04>
<EFBFBD>
<EFBFBD>7<EFBFBD>#<23>$<24>-<2D>-<2D>d<EFBFBD>.A<>.A<>B<> <09>
"(<28><12>!'<27><05><19>'<27>'<27><07> <09>B<>!'<27><16><12> <1A><03>E<EFBFBD>
<EFBFBD>1E<EFBFBD>F<>$<24> '<27> 4<> 4<>U<EFBFBD> ;<3B><04>'+<2B><1A><06><1C> <0B> <0B>A<EFBFBD><0E><1C>(<28>(<28>'+<2B>z<EFBFBD>z<EFBFBD>,<2C>'?<3F>&*<2A>j<EFBFBD>j<EFBFBD><18>&:<3A>'+<2B>z<EFBFBD>z<EFBFBD>)<29>'<<3C>*<1A><1B>!<<3C>G<01>KO<01>N<>h<1C>y<EFBFBD>y<EFBFBD>{<7B>Z<EFBFBD>/<2F> <0C> <0C> <0B> <0B><18><18>1<>,<2C>s<EFBFBD>1C<31>8<EFBFBD>L<>M<> <0C> <0B> <0B><18><18><<3C><04>
<EFBFBD>
<EFBFBD>|<7C>4<>5<>mO<01>*<2A>@<01> O<01><>><12>",<2C>G<01>F<><46><1D> <16><14> <0B> <0B>!<21>!<21>$C<>A<EFBFBD>3<EFBFBD>"G<>H<><15><> <16><>_O<01>N<>N<>N<>s<00>A!N<01>#L <06>$N<01>'M'<03>)L4<02><L<06>=L4<02>L<06>AL4<02>% N<01>0L<04>1N<01>6BL4<02>< N<01>L<04>N<01> A!L4<02>.L<06>
L4<02>L#<05>>L!
<EFBFBD>?AL#<05>L4<02> N<01>'M%<04>(A&N<01>L4<02>L4<02>N<01>N<01>L4<02>!L#<05>#
L1 <09>-L4<02>0M'<03>1L4<02>4
M"<05>>M<05>M"<05>"M'<03>%N<01>'M><07>-M0 <0C>. M><07>:N) rrr3r)rrrr2rrrr7r)<06>r<>NNNT)F)r1<00>
__module__<EFBFBD> __qualname__<5F>__firstlineno__r4<00>int<6E>boolr:rBr<>r<>rrrr\ror<>rr<>r<>r<>r<>r<><00>__static_attributes__<5F>r<r9rr'sB<00><00>]a<>OS<4F>;
<EFBFBD><03>;
<EFBFBD>C<EFBFBD>;
<EFBFBD>C<EFBFBD>;
<EFBFBD> <20>;
<EFBFBD>25<EFBFBD>;
<EFBFBD>DG<EFBFBD>;
<EFBFBD>WZ<EFBFBD>;
<EFBFBD><1B>;
<EFBFBD>.1<EFBFBD>;
<EFBFBD>HL<EFBFBD>;
<EFBFBD>za<01> 1<12>G<EFBFBD>,A<>,A<>1<12>d<EFBFBD>4<EFBFBD>PS<50>UX<55>PX<50>><3E>FZ<46>1<12>f!<1C>d<EFBFBD>3<EFBFBD><03>8<EFBFBD>n<EFBFBD>!<1C><13>!<1C>F.<18>$<24>s<EFBFBD>C<EFBFBD>x<EFBFBD>.<2E>.<18>s<EFBFBD>.<18>s<EFBFBD>.<18>` :<3A>#<23> :<3A>#<23> :<3A><18>3<EFBFBD><18>3<EFBFBD><18> G<1D>G<EFBFBD>,A<>,A<>G<1D>$<24>s<EFBFBD>TW<54>x<EFBFBD>.<2E>G<1D>&-<2D>&7<>&7<>G<1D><@<40>G<1D>RC6<>$<24>C6<>C6r<rc <00><00>[R"S[RSS9nURSSSS9 URSSS S9 URS
SS S9 URS SS S9 URS[SSS9 URS[SSS9 URSSS9 URSSS9 URSSS9 URSSSS9 URS SS!S9 URS"SS#S9 URS$SS%S9 UR 5nUR (dUR(am[(aV[URS&9nUR(aUR5 UR (aUR5 g([S'5 g([URURUR URUR"UR$UR&UR(UR*UR,(+S)9
n[.R0"UR3UR4S*95 g.![6a [S+5 g.[8an[S,U35 S(nAg-S(nAff=f)/z*Main function to run the image downloader.zDownload images from a REST APIa<49>
Examples:
# Basic usage
python image_downloader.py --api-url "https://api.example.com" \
--list-endpoint "/assets" \
--download-endpoint "/download" \
--output-dir "./images"
# With custom concurrent downloads and timeout
python image_downloader.py --api-url "https://api.example.com" \
--list-endpoint "/assets" \
--download-endpoint "/download" \
--output-dir "./images" \
--max-concurrent 10 \
--timeout 60
)<03> description<6F>formatter_class<73>epilogz --api-urlTz3Base URL of the API (e.g., https://api.example.com))<02>required<65>helpz--list-endpointz=Endpoint to get the list of assets (e.g., /assets or /images)z--download-endpointzCEndpoint to download individual assets (e.g., /download or /assets)z --output-dirz#Directory to save downloaded imagesz--max-concurrentr<74>z3Maximum number of concurrent downloads (default: 5))rV<00>defaultr<74>z --timeoutr<74>z(Request timeout in seconds (default: 30)z --api-keyz-API key for authentication (x-api-key header))r<>z--emailzEmail for login authenticationz
--passwordz!Password for login authenticationz --no-tracking<6E>
store_truez4Disable asset tracking (will re-download all assets))<02>actionr<6E>z--force-redownloadz8Force re-download of all assets, even if already trackedz --show-statsz'Show asset tracking statistics and exitz --cleanupz,Clean up metadata for missing files and exitr!zAsset tracking is not availableN)
rrrrrrrrrr)r<>z
Download interrupted by userzError: rzr)<1D>argparse<73>ArgumentParser<65>RawDescriptionHelpFormatter<65> add_argumentr<74><00>
parse_args<EFBFBD>
show_stats<EFBFBD>cleanupr r<00>cleanup_missing_files<65> print_stats<74>printrrrrrrrrr<00> no_trackingr<67><00>runr<6E>r<><00>KeyboardInterruptr@)<05>parser<65>args<67>tracker<65>
downloaderr[s r9<00>mainr<6E><00>s<><00><00> <15> $<24> $<24>5<> <20><<3C><<3C> <0C><06>F<EFBFBD>* <0B><17><17><13><15> B<><18><06>  <0B><17><17><19><15> L<><18><06>  <0B><17><17><1D><15> R<><18><06>  <0B><17><17><16><15> 2<><18><06>  <0B><17><17><1A> <10><11> B<> <18><06> <0B><17><17><13> <10><12> 7<> <18><06> <0B><17><17><13> <<3C><18><06>
 <0B><17><17><11> -<2D><18><06>
 <0B><17><17><14> 0<><18><06>
 <0B><17><17><17><1B> C<><18><06>  <0B><17><17><1C><1B> G<><18><06>  <0B><17><17><16><1B> 6<><18><06>  <0B><17><17><13><1B> ;<3B><18><06> <12> <1C> <1C> <1E>D<EFBFBD> <0C><EFBFBD><EFBFBD>$<24>,<2C>,<2C> <17><<3C>"<22>t<EFBFBD><EFBFBD><EFBFBD>?<3F>G<EFBFBD><13>|<7C>|<7C><17>-<2D>-<2D>/<2F><13><EFBFBD><EFBFBD><17>#<23>#<23>%<25> <0F> <12>3<> 4<><0E>!<21><14> <0C> <0C><1A>(<28>(<28><1E>0<>0<><17>?<3F>?<3F><1B>*<2A>*<2A><14> <0C> <0C><14> <0C> <0C><12>j<EFBFBD>j<EFBFBD><15><1D><1D><1D>)<29>)<29>)<29> <06>J<EFBFBD><11><0F> <0B> <0B>J<EFBFBD>2<>2<>D<EFBFBD>DY<44>DY<44>2<>Z<>[<5B> <0A><> <1D>0<> <0A>.<2E>/<2F>
<0A> <15><11> <0A><07><01>s<EFBFBD>m<EFBFBD><1C><10><><11>s<00>$-I<00>J
<03>) J
<03>2J<03>J
<03>__main__)<1E>__doc__r<5F>r<>r<>r<>r|rQr+<00>pathlibrrjrr<00>typingrrrr r<>r
<00>hashlibr)r <00> ImportErrorr3r rr<>r1<00>exitr<74>r<r9<00><module>r<>s<><00><01> <04><10><0E><0E><0F> <09> <0B><0E><18>*<2A>,<2C>,<2C> <0B><15><0E><17>(<28><18>*<2A>
~6<>~6<>B H <0A>V <0C>z<EFBFBD><19><08><14><16>L<EFBFBD><1A><>k<13><17><16>K<EFBFBD><17><>
<13><18><17>L<EFBFBD><18>s$<00>A7<00>B<00>7B<03>B<03>B<03>B