Update cloudinary==1.28.0

This commit is contained in:
JonnyWong16 2021-11-28 14:12:44 -08:00
parent 0325e9327f
commit dcfd8abddd
No known key found for this signature in database
GPG key ID: B1F1F9807184697A
7 changed files with 196 additions and 21 deletions

View file

@ -38,7 +38,7 @@ CL_BLANK = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAA
URI_SCHEME = "cloudinary" URI_SCHEME = "cloudinary"
API_VERSION = "v1_1" API_VERSION = "v1_1"
VERSION = "1.26.0" VERSION = "1.28.0"
USER_AGENT = "CloudinaryPython/{} (Python {})".format(VERSION, python_version()) USER_AGENT = "CloudinaryPython/{} (Python {})".format(VERSION, python_version())
""" :const: USER_AGENT """ """ :const: USER_AGENT """

View file

@ -93,6 +93,25 @@ def resources_by_ids(public_ids, **options):
return call_api("get", uri, params, **options) return call_api("get", uri, params, **options)
def resources_by_asset_ids(asset_ids, **options):
"""Retrieves the resources (assets) indicated in the asset IDs.
This method does not return deleted assets even if they have been backed up.
See: `Get resources by context API reference
<https://cloudinary.com/documentation/admin_api#get_resources>`_
:param asset_ids: The requested asset IDs.
:type asset_ids: list[str]
:param options: Additional options
:type options: dict, optional
:return: Resources (assets) as indicated in the asset IDs
:rtype: Response
"""
uri = ["resources", 'by_asset_ids']
params = dict(only(options, "tags", "moderations", "context"), asset_ids=asset_ids)
return call_api("get", uri, params, **options)
def resources_by_context(key, value=None, **options): def resources_by_context(key, value=None, **options):
"""Retrieves resources (assets) with a specified context key. """Retrieves resources (assets) with a specified context key.
This method does not return deleted assets even if they have been backed up. This method does not return deleted assets even if they have been backed up.
@ -123,10 +142,38 @@ def resource(public_id, **options):
resource_type = options.pop("resource_type", "image") resource_type = options.pop("resource_type", "image")
upload_type = options.pop("type", "upload") upload_type = options.pop("type", "upload")
uri = ["resources", resource_type, upload_type, public_id] uri = ["resources", resource_type, upload_type, public_id]
params = only(options, "exif", "faces", "colors", "image_metadata", "cinemagraph_analysis", params = _prepare_asset_details_params(**options)
return call_api("get", uri, params, **options)
def resource_by_asset_id(asset_id, **options):
"""
Returns the details of the specified asset and all its derived assets by asset id.
:param asset_id: The Asset ID of the asset
:type asset_id: string
:param options: Additional options
:type options: dict, optional
:return: Resource (asset) of a specific asset_id
:rtype: Response
"""
uri = ["resources", asset_id]
params = _prepare_asset_details_params(**options)
return call_api("get", uri, params, **options)
def _prepare_asset_details_params(**options):
"""
Prepares optional parameters for resource_by_asset_id API calls.
:param options: Additional options
:return: Optional parameters
:internal
"""
return only(options, "exif", "faces", "colors", "image_metadata", "cinemagraph_analysis",
"pages", "phash", "coordinates", "max_results", "quality_analysis", "derived_next_cursor", "pages", "phash", "coordinates", "max_results", "quality_analysis", "derived_next_cursor",
"accessibility_analysis", "versions") "accessibility_analysis", "versions")
return call_api("get", uri, params, **options)
def update(public_id, **options): def update(public_id, **options):
@ -595,3 +642,32 @@ def restore_metadata_field_datasource(field_external_id, entries_external_ids, *
uri = [field_external_id, 'datasource_restore'] uri = [field_external_id, 'datasource_restore']
params = {"external_ids": entries_external_ids} params = {"external_ids": entries_external_ids}
return call_metadata_api("post", uri, params, **options) return call_metadata_api("post", uri, params, **options)
def reorder_metadata_field_datasource(field_external_id, order_by, direction=None, **options):
"""Reorders metadata field datasource. Currently, supports only value.
:param field_external_id: The ID of the metadata field.
:param order_by: Criteria for the order. Currently, supports only value.
:param direction: Optional (gets either asc or desc).
:param options: Additional options.
:rtype: Response
"""
uri = [field_external_id, 'datasource', 'order']
params = {'order_by': order_by, 'direction': direction}
return call_metadata_api('post', uri, params, **options)
def reorder_metadata_fields(order_by, direction=None, **options):
"""Reorders metadata fields.
:param order_by: Criteria for the order (one of the fields 'label', 'external_id', 'created_at').
:param direction: Optional (gets either asc or desc).
:param options: Additional options.
:rtype: Response
"""
uri = ['order']
params = {'order_by': order_by, 'direction': direction}
return call_metadata_api('put', uri, params, **options)

View file

@ -97,9 +97,7 @@ def sub_account(sub_account_id, **options):
return _call_account_api("get", uri, {}, **options) return _call_account_api("get", uri, {}, **options)
def update_sub_account(sub_account_id, name=None, cloud_name=None, custom_attributes=None, def update_sub_account(sub_account_id, name=None, cloud_name=None, custom_attributes=None, enabled=None, **options):
enabled=None, base_account=None,
**options):
""" """
Update a sub account Update a sub account
:param sub_account_id: The id of the sub account :param sub_account_id: The id of the sub account
@ -112,8 +110,6 @@ def update_sub_account(sub_account_id, name=None, cloud_name=None, custom_attrib
:type custom_attributes: dict, optional :type custom_attributes: dict, optional
:param enabled: Whether to create the account as enabled (default is enabled). :param enabled: Whether to create the account as enabled (default is enabled).
:type enabled: bool, optional :type enabled: bool, optional
:param base_account: ID of sub-account from which to copy settings
:type base_account: str, optional
:param options: Generic advanced options dict, see online documentation :param options: Generic advanced options dict, see online documentation
:type options: dict, optional :type options: dict, optional
:return: Updated sub account :return: Updated sub account
@ -123,8 +119,7 @@ def update_sub_account(sub_account_id, name=None, cloud_name=None, custom_attrib
params = {"name": name, params = {"name": name,
"cloud_name": cloud_name, "cloud_name": cloud_name,
"custom_attributes": custom_attributes, "custom_attributes": custom_attributes,
"enabled": enabled, "enabled": enabled}
"base_account": base_account}
return _call_account_api("put", uri, params=params, **options) return _call_account_api("put", uri, params=params, **options)

View file

@ -1,10 +1,16 @@
import json import json
from copy import deepcopy
from cloudinary.api_client.call_api import call_json_api from cloudinary.api_client.call_api import call_json_api
from cloudinary.utils import unique
class Search: class Search:
_KEYS_WITH_UNIQUE_VALUES = {
'sort_by': lambda x: next(iter(x)),
'aggregate': None,
'with_field': None,
}
"""Build and execute a search query.""" """Build and execute a search query."""
def __init__(self): def __init__(self):
self.query = {} self.query = {}
@ -42,7 +48,7 @@ class Search:
return self return self
def to_json(self): def to_json(self):
return json.dumps(self.query) return json.dumps(self.as_dict())
def execute(self, **options): def execute(self, **options):
"""Execute the search and return results.""" """Execute the search and return results."""
@ -57,4 +63,12 @@ class Search:
return self return self
def as_dict(self): def as_dict(self):
return deepcopy(self.query) to_return = {}
for key, value in self.query.items():
if key in self._KEYS_WITH_UNIQUE_VALUES:
value = unique(value, self._KEYS_WITH_UNIQUE_VALUES[key])
to_return[key] = value
return to_return

View file

@ -802,7 +802,7 @@ var slice = [].slice,
function TextLayer(options) { function TextLayer(options) {
var keys; var keys;
TextLayer.__super__.constructor.call(this, options); TextLayer.__super__.constructor.call(this, options);
keys = ["resourceType", "resourceType", "fontFamily", "fontSize", "fontWeight", "fontStyle", "textDecoration", "textAlign", "stroke", "letterSpacing", "lineSpacing", "fontHinting", "fontAntialiasing", "text"]; keys = ["resourceType", "resourceType", "fontFamily", "fontSize", "fontWeight", "fontStyle", "textDecoration", "textAlign", "stroke", "letterSpacing", "lineSpacing", "fontHinting", "fontAntialiasing", "text", "textStyle"];
if (options != null) { if (options != null) {
keys.forEach((function(_this) { keys.forEach((function(_this) {
return function(key) { return function(key) {
@ -886,6 +886,11 @@ var slice = [].slice,
return this; return this;
}; };
TextLayer.prototype.textStyle = function(textStyle) {
this.options.textStyle = textStyle;
return this;
};
/** /**
* generate the string representation of the layer * generate the string representation of the layer
@ -921,6 +926,10 @@ var slice = [].slice,
}; };
TextLayer.prototype.textStyleIdentifier = function() { TextLayer.prototype.textStyleIdentifier = function() {
// Note: if a text-style argument is provided as a whole, it overrides everything else, no mix and match.
if (!Util.isEmpty(this.options.textStyle)) {
return this.options.textStyle;
}
var components; var components;
components = []; components = [];
if (this.options.fontWeight !== "normal") { if (this.options.fontWeight !== "normal") {

View file

@ -3,15 +3,14 @@ import json
import os import os
import socket import socket
import certifi
from six import string_types from six import string_types
from urllib3 import PoolManager, ProxyManager
from urllib3.exceptions import HTTPError from urllib3.exceptions import HTTPError
import cloudinary import cloudinary
from cloudinary import utils from cloudinary import utils
from cloudinary.exceptions import Error
from cloudinary.cache.responsive_breakpoints_cache import instance as responsive_breakpoints_cache_instance from cloudinary.cache.responsive_breakpoints_cache import instance as responsive_breakpoints_cache_instance
from cloudinary.exceptions import Error
from cloudinary.utils import build_eager
try: try:
from urllib3.contrib.appengine import AppEngineManager, is_appengine_sandbox from urllib3.contrib.appengine import AppEngineManager, is_appengine_sandbox
@ -24,6 +23,11 @@ try: # Python 2.7+
except ImportError: except ImportError:
from urllib3.packages.ordered_dict import OrderedDict from urllib3.packages.ordered_dict import OrderedDict
try: # Python 3.4+
from pathlib import Path as PathLibPathType
except ImportError:
PathLibPathType = None
if is_appengine_sandbox(): if is_appengine_sandbox():
# AppEngineManager uses AppEngine's URLFetch API behind the scenes # AppEngineManager uses AppEngine's URLFetch API behind the scenes
_http = AppEngineManager() _http = AppEngineManager()
@ -58,7 +62,12 @@ def upload_image(file, **options):
def upload_resource(file, **options): def upload_resource(file, **options):
result = upload_large(file, **options) upload_func = upload
if hasattr(file, 'size') and file.size > UPLOAD_LARGE_CHUNK_SIZE:
upload_func = upload_large
result = upload_func(file, **options)
return cloudinary.CloudinaryResource( return cloudinary.CloudinaryResource(
result["public_id"], version=str(result["version"]), result["public_id"], version=str(result["version"]),
format=result.get("format"), type=result["type"], format=result.get("format"), type=result["type"],
@ -363,6 +372,39 @@ def text(text, **options):
return call_api("text", params, **options) return call_api("text", params, **options)
_SLIDESHOW_PARAMS = [
"notification_url",
"public_id",
"overwrite",
"upload_preset",
]
def create_slideshow(**options):
"""
Creates auto-generated video slideshows.
:param options: The optional parameters. See the upload API documentation.
:return: a dictionary with details about created slideshow
"""
options["resource_type"] = options.get("resource_type", "video")
params = {param_name: options.get(param_name) for param_name in _SLIDESHOW_PARAMS}
serialized_params = {
"timestamp": utils.now(),
"transformation": build_eager(options.get("transformation")),
"manifest_transformation": build_eager(options.get("manifest_transformation")),
"manifest_json": options.get("manifest_json") and utils.json_encode(options.get("manifest_json")),
"tags": options.get("tags") and utils.encode_list(utils.build_array(options["tags"])),
}
params.update(serialized_params)
return call_api("create_slideshow", params, **options)
def _save_responsive_breakpoints_to_cache(result): def _save_responsive_breakpoints_to_cache(result):
""" """
Saves responsive breakpoints parsed from upload result to cache Saves responsive breakpoints parsed from upload result to cache
@ -427,7 +469,10 @@ def call_api(action, params, http_headers=None, return_error=False, unsigned=Fal
if file: if file:
filename = options.get("filename") # Custom filename provided by user (relevant only for streams and files) filename = options.get("filename") # Custom filename provided by user (relevant only for streams and files)
if isinstance(file, string_types): if PathLibPathType and isinstance(file, PathLibPathType):
name = filename or file.name
data = file.read_bytes()
elif isinstance(file, string_types):
if utils.is_remote_url(file): if utils.is_remote_url(file):
# URL # URL
name = None name = None

View file

@ -70,6 +70,7 @@ __URL_KEYS = [
__SIMPLE_UPLOAD_PARAMS = [ __SIMPLE_UPLOAD_PARAMS = [
"public_id", "public_id",
"public_id_prefix",
"callback", "callback",
"format", "format",
"type", "type",
@ -80,6 +81,8 @@ __SIMPLE_UPLOAD_PARAMS = [
"colors", "colors",
"use_filename", "use_filename",
"unique_filename", "unique_filename",
"display_name",
"use_filename_as_display_name",
"discard_original_filename", "discard_original_filename",
"filename_override", "filename_override",
"invalidate", "invalidate",
@ -89,6 +92,7 @@ __SIMPLE_UPLOAD_PARAMS = [
"eval", "eval",
"proxy", "proxy",
"folder", "folder",
"asset_folder",
"overwrite", "overwrite",
"moderation", "moderation",
"raw_convert", "raw_convert",
@ -526,6 +530,9 @@ def process_video_codec_param(param):
out_param = out_param + ':' + param['profile'] out_param = out_param + ':' + param['profile']
if 'level' in param: if 'level' in param:
out_param = out_param + ':' + param['level'] out_param = out_param + ':' + param['level']
if param.get('b_frames') is False:
out_param = out_param + ':' + 'bframes_no'
return out_param return out_param
@ -1089,6 +1096,10 @@ def build_multi_and_sprite_params(**options):
def __process_text_options(layer, layer_parameter): def __process_text_options(layer, layer_parameter):
text_style = str(layer.get("text_style", ""))
if text_style and not text_style.isspace():
return text_style
font_family = layer.get("font_family") font_family = layer.get("font_family")
font_size = layer.get("font_size") font_size = layer.get("font_size")
keywords = [] keywords = []
@ -1247,7 +1258,7 @@ PREDEFINED_VARS = {
"context": "ctx" "context": "ctx"
} }
replaceRE = "((\\|\\||>=|<=|&&|!=|>|=|<|/|-|\\+|\\*|\\^)(?=[ _])|(\\$_*[^_ ]+)|(?<!\\$)(" + \ replaceRE = "((\\|\\||>=|<=|&&|!=|>|=|<|/|-|\\+|\\*|\\^)(?=[ _])|(\\$_*[^_ ]+)|(?<![\\$:])(" + \
'|'.join(PREDEFINED_VARS.keys()) + "))" '|'.join(PREDEFINED_VARS.keys()) + "))"
@ -1531,3 +1542,28 @@ def safe_cast(val, casting_fn, default=None):
return casting_fn(val) return casting_fn(val)
except (ValueError, TypeError): except (ValueError, TypeError):
return default return default
def __id(x):
"""
Identity function. Returns the passed in values.
"""
return x
def unique(collection, key=None):
"""
Removes duplicates from collection using key function
:param collection: The collection to remove duplicates from
:param key: The function to generate key from each element. If not passed, identity function is used
"""
if key is None:
key = __id
to_return = OrderedDict()
for element in collection:
to_return[key(element)] = element
return list(to_return.values())