Skip to content

Commit 0cd6c35

Browse files
Fix sync pull for files with extension in public id
1 parent ff73c4a commit 0cd6c35

File tree

3 files changed

+38
-14
lines changed

3 files changed

+38
-14
lines changed

cloudinary_cli/modules/sync.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@
22
from functools import reduce
33
from itertools import product
44
from os import remove
5-
from os.path import split, join as path_join, abspath
5+
from os.path import join as path_join, abspath
66

77
from click import command, argument, option, style
88
from cloudinary import api
99

1010
from cloudinary_cli.utils.api_utils import query_cld_folder, upload_file, download_file
1111
from cloudinary_cli.utils.file_utils import walk_dir, delete_empty_dirs, get_destination_folder
1212
from cloudinary_cli.utils.json_utils import print_json
13-
from cloudinary_cli.utils.utils import logger, run_tasks_concurrently, confirm_action, get_user_action
13+
from cloudinary_cli.utils.utils import logger, run_tasks_concurrently, get_user_action
1414

1515
DELETE_ASSETS_BATCH_SIZE = 100
1616

@@ -88,8 +88,10 @@ def push(self):
8888
return False
8989

9090
files_to_push = self.unique_local_file_names | self.out_of_sync_file_names
91-
to_upload = list(filter(lambda x: split(x)[1][0] != ".", files_to_push))
92-
logger.info(f"Uploading {len(to_upload)} items to Cloudinary folder '{self.remote_dir}'")
91+
if not files_to_push:
92+
return True
93+
94+
logger.info(f"Uploading {len(files_to_push)} items to Cloudinary folder '{self.remote_dir}'")
9395

9496
options = {
9597
'use_filename': True,
@@ -98,7 +100,7 @@ def push(self):
98100
'resource_type': 'auto'
99101
}
100102
uploads = []
101-
for file in to_upload:
103+
for file in files_to_push:
102104
folder = get_destination_folder(self.remote_dir, file)
103105

104106
uploads.append((self.local_files[file]['path'], {**options, 'folder': folder}))
@@ -145,12 +147,14 @@ def pull(self):
145147

146148
files_to_pull = self.unique_remote_file_names | self.out_of_sync_file_names
147149

150+
if not files_to_pull:
151+
return True
152+
148153
logger.info(f"Downloading {len(files_to_pull)} files from Cloudinary")
149154
downloads = []
150155
for file in files_to_pull:
151156
remote_file = self.remote_files[file]
152-
file_name = file + "." + remote_file['format'] if remote_file['resource_type'] != 'raw' else file
153-
local_path = abspath(path_join(self.local_dir, file_name))
157+
local_path = abspath(path_join(self.local_dir, file))
154158

155159
downloads.append((remote_file, local_path))
156160

cloudinary_cli/utils/api_utils.py

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ def query_cld_folder(folder):
2525
res = expression.execute()
2626

2727
for asset in res['resources']:
28-
rel_path = path.relpath(asset['public_id'], folder)
28+
rel_path = path.relpath(asset_source(asset), folder)
2929
files[rel_path] = {
3030
"type": asset['type'],
3131
"resource_type": asset['resource_type'],
@@ -65,18 +65,35 @@ def upload_file(file_path, options, uploaded=None, skipped=None):
6565
def download_file(remote_file, local_path):
6666
makedirs(path.dirname(local_path), exist_ok=True)
6767
with open(local_path, "wb") as f:
68-
f.write(requests.get(cloudinary_url(remote_file['public_id'], resource_type=remote_file['resource_type'],
69-
type=remote_file['type'])[0]).content)
68+
download_url = cloudinary_url(asset_source(remote_file), resource_type=remote_file['resource_type'],
69+
type=remote_file['type'])[0]
70+
f.write(requests.get(download_url).content)
7071
logger.info(style("Downloaded '{}' to '{}'".format(remote_file['relative_path'], local_path), fg="green"))
7172

7273

74+
def asset_source(asset_details):
75+
"""
76+
Public ID of the transformable file (image/video) does not include file extension.
77+
78+
It needs to be added in order to download file properly (without creating derived asset).
79+
80+
Raw files are accessed using only public_id.
81+
82+
:param asset_details: The details of the asset.
83+
:rtype asset_details: dict
84+
85+
:return:
86+
"""
87+
base_name = asset_details['public_id']
88+
return base_name + "." + asset_details['format'] if asset_details['resource_type'] != 'raw' else base_name
89+
90+
7391
def handle_command(
7492
params,
7593
optional_parameter,
7694
optional_parameter_parsed,
7795
module,
7896
module_name):
79-
8097
try:
8198
func = module.__dict__[params[0]]
8299
except KeyError:

cloudinary_cli/utils/file_utils.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,13 @@
88
def walk_dir(root_dir):
99
all_files = {}
1010
for root, _, files in walk(root_dir):
11+
relative_path = relpath(root, root_dir) if root_dir != root else ""
1112
for file in files:
12-
all_files[path.splitext(path.join(root, file)[len(root_dir) + 1:])[0]] = {
13-
"path": path.join(root, file),
14-
"etag": etag(path.join(root, file))
13+
full_path = path.join(root, file)
14+
relative_file_path = "/".join(p for p in [relative_path, file] if p)
15+
all_files[relative_file_path] = {
16+
"path": full_path,
17+
"etag": etag(full_path)
1518
}
1619
return all_files
1720

0 commit comments

Comments
 (0)