Skip to content

Commit

Permalink
Support upload_presets. Support unsigned uploads. Support start_at fo…
Browse files Browse the repository at this point in the history
…r resource listing. Support phash for upload and resource details
  • Loading branch information
TalLevAmi committed Apr 21, 2014
1 parent 057b538 commit 85b9574
Show file tree
Hide file tree
Showing 7 changed files with 213 additions and 122 deletions.
30 changes: 28 additions & 2 deletions cloudinary/api.py
Expand Up @@ -46,7 +46,7 @@ def resources(**options):
type = options.pop("type", None)
uri = ["resources", resource_type]
if type: uri.append(type)
return call_api("get", uri, only(options, "next_cursor", "max_results", "prefix", "tags", "context", "moderations", "direction"), **options)
return call_api("get", uri, only(options, "next_cursor", "max_results", "prefix", "tags", "context", "moderations", "direction", "start_at"), **options)

def resources_by_tag(tag, **options):
resource_type = options.pop("resource_type", "image")
Expand All @@ -70,7 +70,7 @@ def resource(public_id, **options):
resource_type = options.pop("resource_type", "image")
type = options.pop("type", "upload")
uri = ["resources", resource_type, type, public_id]
return call_api("get", uri, only(options, "exif", "faces", "colors", "image_metadata", "pages", "max_results"), **options)
return call_api("get", uri, only(options, "exif", "faces", "colors", "image_metadata", "pages", "phash", "max_results"), **options)

def update(public_id, **options):
resource_type = options.pop("resource_type", "image")
Expand Down Expand Up @@ -146,6 +146,32 @@ def create_transformation(name, definition, **options):
uri = ["transformations", name]
return call_api("post", uri, {"transformation": transformation_string(definition)}, **options)

def upload_presets(**options):
uri = ["upload_presets"]
return call_api("get", uri, only(options, "next_cursor", "max_results"), **options)

def upload_preset(name, **options):
uri = ["upload_presets", name]
return call_api("get", uri, only(options, "max_results"), **options)

def delete_upload_preset(name, **options):
uri = ["upload_presets", name]
return call_api("delete", uri, {}, **options)

def update_upload_preset(name, **options):
uri = ["upload_presets", name]
params = utils.build_upload_params(**options)
params = utils.cleanup_params(params)
params.update(only(options, "unsigned", "disallow_public_id"))
return call_api("put", uri, params, **options)

def create_upload_preset(**options):
uri = ["upload_presets"]
params = utils.build_upload_params(**options)
params = utils.cleanup_params(params)
params.update(only(options, "unsigned", "disallow_public_id", "name"))
return call_api("post", uri, params, **options)

def call_api(method, uri, params, **options):
prefix = options.pop("upload_prefix", cloudinary.config().upload_prefix) or "https://api.cloudinary.com"
cloud_name = options.pop("cloud_name", cloudinary.config().cloud_name)
Expand Down
20 changes: 15 additions & 5 deletions cloudinary/forms.py
Expand Up @@ -15,12 +15,15 @@ class CloudinaryInput(forms.TextInput):
input_type = 'file'

def render(self, name, value, attrs=None):
self.build_attrs(attrs)
attrs = self.attrs.copy()
options = attrs.pop('options', {})
attrs = self.build_attrs(attrs)
options = attrs.get('options', {})
attrs["options"] = ''

params = cloudinary.uploader.build_upload_params(**options)
params = cloudinary.utils.sign_request(params, options)
params = cloudinary.utils.build_upload_params(**options)
if options.get("unsigned"):
params = cloudinary.utils.cleanup_params(params)
else:
params = cloudinary.utils.sign_request(params, options)

if 'resource_type' not in options: options['resource_type'] = 'auto'
cloudinary_upload_url = cloudinary.utils.cloudinary_api_url("upload", **options)
Expand Down Expand Up @@ -82,6 +85,13 @@ def validate(self, value):
if not value.validate():
raise forms.ValidationError("Signature mismatch")

class CloudinaryUnsignedJsFileField(CloudinaryJsFileField):

def __init__(self, upload_preset, attrs={}, options={}, autosave=True, *args, **kwargs):
options = options.copy()
options.update({"unsigned": True, "upload_preset": upload_preset})
super(CloudinaryUnsignedJsFileField, self).__init__(attrs, options, autosave, *args, **kwargs)

class CloudinaryFileField(forms.FileField):
my_default_error_messages = {
'required': _(u"No image selected!")
Expand Down
2 changes: 1 addition & 1 deletion cloudinary/templatetags/cloudinary.py
Expand Up @@ -35,7 +35,7 @@ def cloudinary_direct_upload_field(field_name="image", request=None):
"""Deprecated - please use cloudinary_direct_upload_field, or a proper form"""
@register.inclusion_tag('cloudinary_direct_upload.html')
def cloudinary_direct_upload(callback_url, **options):
params = uploader.build_upload_params(callback=callback_url, **options)
params = utils.build_upload_params(callback=callback_url, **options)
params = utils.sign_request(params, options)

api_url = utils.cloudinary_api_url("upload", resource_type=options.get("resource_type", "image"), upload_prefix=options.get("upload_prefix"))
Expand Down
80 changes: 10 additions & 70 deletions cloudinary/uploader.py
Expand Up @@ -10,76 +10,13 @@
from cloudinary.compat import (urllib2, BytesIO, string_types, urlencode, to_bytes, to_string)
_initialized = False

def build_eager(transformations):
eager = []
for tr in utils.build_array(transformations):
format = tr.get("format")
single_eager = "/".join([x for x in [utils.generate_transformation_string(**tr)[0], format] if x])
eager.append(single_eager)
return "|".join(eager)

def build_custom_headers(headers):
if headers == None:
return None
elif isinstance(headers, list):
pass
elif isinstance(headers, dict):
headers = [k + ": " + v for k, v in headers.items()]
else:
return headers
return "\n".join(headers)

def build_upload_params(**options):
params = {"timestamp": utils.now(),
"transformation": utils.generate_transformation_string(**options)[0],
"public_id": options.get("public_id"),
"callback": options.get("callback"),
"format": options.get("format"),
"type": options.get("type"),
"backup": options.get("backup"),
"faces": options.get("faces"),
"image_metadata": options.get("image_metadata"),
"exif": options.get("exif"),
"colors": options.get("colors"),
"headers": build_custom_headers(options.get("headers")),
"eager": build_eager(options.get("eager")),
"use_filename": options.get("use_filename"),
"unique_filename": options.get("unique_filename"),
"discard_original_filename": options.get("discard_original_filename"),
"invalidate": options.get("invalidate"),
"notification_url": options.get("notification_url"),
"eager_notification_url": options.get("eager_notification_url"),
"eager_async": options.get("eager_async"),
"proxy": options.get("proxy"),
"folder": options.get("folder"),
"overwrite": options.get("overwrite"),
"tags": options.get("tags") and ",".join(utils.build_array(options["tags"])),
"allowed_formats": options.get("allowed_formats") and ",".join(utils.build_array(options["allowed_formats"])),
"face_coordinates": utils.encode_double_array(options.get("face_coordinates")),
"context": utils.encode_dict(options.get("context")),
"moderation": options.get("moderation"),
"raw_convert": options.get("raw_convert"),
"ocr": options.get("ocr"),
"categorization": options.get("categorization"),
"detection": options.get("detection"),
"similarity_search": options.get("similarity_search"),
"auto_tagging": options.get("auto_tagging") and float(options.get("auto_tagging"))}
params = dict( [ (k, __safe_value(v)) for (k,v) in params.items()] )
return params

def __safe_value(v):
if isinstance(v, (bool)):
if v:
return "1"
else:
return "0"
else:
return v

def upload(file, **options):
params = build_upload_params(**options)
params = utils.build_upload_params(**options)
return call_api("upload", params, file = file, **options)

def unsigned_upload(file, upload_preset, **options):
return upload(file, upload_preset=upload_preset, unsigned=True, **options)

def upload_image(file, **options):
result = upload(file, **options)
return cloudinary.CloudinaryImage(result["public_id"], version=str(result["version"]),
Expand Down Expand Up @@ -142,8 +79,8 @@ def explicit(public_id, **options):
"type": options.get("type"),
"public_id": public_id,
"callback": options.get("callback"),
"headers": build_custom_headers(options.get("headers")),
"eager": build_eager(options.get("eager")),
"headers": utils.build_custom_headers(options.get("headers")),
"eager": utils.build_eager(options.get("eager")),
"tags": options.get("tags") and ",".join(utils.build_array(options["tags"])),
"face_coordinates": utils.encode_double_array(options.get("face_coordinates"))}
return call_api("explicit", params, **options)
Expand Down Expand Up @@ -212,7 +149,10 @@ def call_api(action, params, **options):
try:
file_io = None
return_error = options.get("return_error")
params = utils.sign_request(params, options)
if options.get("unsigned"):
params = utils.cleanup_params(params)
else:
params = utils.sign_request(params, options)

param_list = []
for k, v in params.items():
Expand Down
72 changes: 71 additions & 1 deletion cloudinary/utils.py
Expand Up @@ -86,13 +86,16 @@ def generate_transformation_string(**options):
url = "/".join([trans for trans in base_transformations + [transformation] if trans])
return (url, options)

def cleanup_params(params):
return dict( [ (k, __safe_value(v)) for (k,v) in params.items() if not v is None and not v == ""] )

def sign_request(params, options):
api_key = options.get("api_key", cloudinary.config().api_key)
if not api_key: raise Exception("Must supply api_key")
api_secret = options.get("api_secret", cloudinary.config().api_secret)
if not api_secret: raise Exception("Must supply api_secret")

params = dict( [ (k,v) for (k,v) in params.items() if v] )
params = cleanup_params(params)
params["signature"] = api_sign_request(params, api_secret)
params["api_key"] = api_key

Expand Down Expand Up @@ -217,3 +220,70 @@ def zip_download_url(tag, **options):
}, options)

return cloudinary_api_url("download_tag.zip", **options) + "?" + urlencode(cloudinary_params)

def build_eager(transformations):
eager = []
for tr in build_array(transformations):
format = tr.get("format")
single_eager = "/".join([x for x in [generate_transformation_string(**tr)[0], format] if x])
eager.append(single_eager)
return "|".join(eager)

def build_custom_headers(headers):
if headers == None:
return None
elif isinstance(headers, list):
pass
elif isinstance(headers, dict):
headers = [k + ": " + v for k, v in headers.items()]
else:
return headers
return "\n".join(headers)

def build_upload_params(**options):
params = {"timestamp": now(),
"transformation": generate_transformation_string(**options)[0],
"public_id": options.get("public_id"),
"callback": options.get("callback"),
"format": options.get("format"),
"type": options.get("type"),
"backup": options.get("backup"),
"faces": options.get("faces"),
"image_metadata": options.get("image_metadata"),
"exif": options.get("exif"),
"colors": options.get("colors"),
"headers": build_custom_headers(options.get("headers")),
"eager": build_eager(options.get("eager")),
"use_filename": options.get("use_filename"),
"unique_filename": options.get("unique_filename"),
"discard_original_filename": options.get("discard_original_filename"),
"invalidate": options.get("invalidate"),
"notification_url": options.get("notification_url"),
"eager_notification_url": options.get("eager_notification_url"),
"eager_async": options.get("eager_async"),
"proxy": options.get("proxy"),
"folder": options.get("folder"),
"overwrite": options.get("overwrite"),
"tags": options.get("tags") and ",".join(build_array(options["tags"])),
"allowed_formats": options.get("allowed_formats") and ",".join(build_array(options["allowed_formats"])),
"face_coordinates": encode_double_array(options.get("face_coordinates")),
"context": encode_dict(options.get("context")),
"moderation": options.get("moderation"),
"raw_convert": options.get("raw_convert"),
"ocr": options.get("ocr"),
"categorization": options.get("categorization"),
"detection": options.get("detection"),
"similarity_search": options.get("similarity_search"),
"upload_preset": options.get("upload_preset"),
"phash": options.get("phash"),
"auto_tagging": options.get("auto_tagging") and float(options.get("auto_tagging"))}
return params

def __safe_value(v):
if isinstance(v, (bool)):
if v:
return "1"
else:
return "0"
else:
return v

0 comments on commit 85b9574

Please sign in to comment.