cloudinary 1.17.0 → 1.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +61 -0
- data/cloudinary.gemspec +8 -3
- data/lib/active_storage/service/cloudinary_service.rb +32 -6
- data/lib/cloudinary.rb +45 -64
- data/lib/cloudinary/account_api.rb +231 -0
- data/lib/cloudinary/account_config.rb +30 -0
- data/lib/cloudinary/api.rb +182 -66
- data/lib/cloudinary/auth_token.rb +4 -0
- data/lib/cloudinary/base_api.rb +79 -0
- data/lib/cloudinary/base_config.rb +70 -0
- data/lib/cloudinary/config.rb +43 -0
- data/lib/cloudinary/helper.rb +1 -1
- data/lib/cloudinary/uploader.rb +33 -6
- data/lib/cloudinary/utils.rb +109 -27
- data/lib/cloudinary/version.rb +1 -1
- data/lib/cloudinary/video_helper.rb +96 -22
- data/vendor/assets/javascripts/cloudinary/jquery.cloudinary.js +6 -2
- metadata +17 -12
@@ -0,0 +1,43 @@
|
|
1
|
+
module Cloudinary
|
2
|
+
module Config
|
3
|
+
include BaseConfig
|
4
|
+
|
5
|
+
ENV_URL = "CLOUDINARY_URL"
|
6
|
+
SCHEME = "cloudinary"
|
7
|
+
|
8
|
+
def load_config_from_env
|
9
|
+
if ENV["CLOUDINARY_CLOUD_NAME"]
|
10
|
+
config_keys = ENV.keys.select! { |key| key.start_with? "CLOUDINARY_" }
|
11
|
+
config_keys -= ["CLOUDINARY_URL"] # ignore it when explicit options are passed
|
12
|
+
config_keys.each do |full_key|
|
13
|
+
conf_key = full_key["CLOUDINARY_".length..-1].downcase # convert "CLOUDINARY_CONFIG_NAME" to "config_name"
|
14
|
+
conf_val = ENV[full_key]
|
15
|
+
conf_val = conf_val == 'true' if %w[true false].include?(conf_val) # cast relevant boolean values
|
16
|
+
update(conf_key => conf_val)
|
17
|
+
end
|
18
|
+
elsif ENV[ENV_URL]
|
19
|
+
load_from_url(ENV[ENV_URL])
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def env_url
|
26
|
+
ENV_URL
|
27
|
+
end
|
28
|
+
|
29
|
+
def expected_scheme
|
30
|
+
SCHEME
|
31
|
+
end
|
32
|
+
|
33
|
+
def config_from_parsed_url(parsed_url)
|
34
|
+
{
|
35
|
+
"cloud_name" => parsed_url.host,
|
36
|
+
"api_key" => parsed_url.user,
|
37
|
+
"api_secret" => parsed_url.password,
|
38
|
+
"private_cdn" => !parsed_url.path.blank?,
|
39
|
+
"secure_distribution" => parsed_url.path[1..-1]
|
40
|
+
}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/cloudinary/helper.rb
CHANGED
@@ -438,7 +438,7 @@ begin
|
|
438
438
|
# @return [::SassC::Script::Value::String]
|
439
439
|
def cloudinary_url(public_id, sass_options = {})
|
440
440
|
options = {}
|
441
|
-
sass_options.to_h.each { |k, v| options[k.value] = v.value }
|
441
|
+
sass_options.to_h.each { |k, v| options[k.value.to_sym] = v.value }
|
442
442
|
url = Cloudinary::Utils.cloudinary_url(public_id.value, {:type => :asset}.merge(options))
|
443
443
|
::SassC::Script::Value::String.new("url(#{url})")
|
444
444
|
end
|
data/lib/cloudinary/uploader.rb
CHANGED
@@ -42,6 +42,7 @@ class Cloudinary::Uploader
|
|
42
42
|
:faces => Cloudinary::Utils.as_safe_bool(options[:faces]),
|
43
43
|
:folder => options[:folder],
|
44
44
|
:format => options[:format],
|
45
|
+
:filename_override => options[:filename_override],
|
45
46
|
:headers => build_custom_headers(options[:headers]),
|
46
47
|
:image_metadata => Cloudinary::Utils.as_safe_bool(options[:image_metadata]),
|
47
48
|
:invalidate => Cloudinary::Utils.as_safe_bool(options[:invalidate]),
|
@@ -65,7 +66,8 @@ class Cloudinary::Uploader
|
|
65
66
|
:unique_filename => Cloudinary::Utils.as_safe_bool(options[:unique_filename]),
|
66
67
|
:upload_preset => options[:upload_preset],
|
67
68
|
:use_filename => Cloudinary::Utils.as_safe_bool(options[:use_filename]),
|
68
|
-
:accessibility_analysis => Cloudinary::Utils.as_safe_bool(options[:accessibility_analysis])
|
69
|
+
:accessibility_analysis => Cloudinary::Utils.as_safe_bool(options[:accessibility_analysis]),
|
70
|
+
:metadata => Cloudinary::Utils.encode_context(options[:metadata])
|
69
71
|
}
|
70
72
|
params
|
71
73
|
end
|
@@ -272,6 +274,29 @@ class Cloudinary::Uploader
|
|
272
274
|
return self.call_tags_api(nil, "remove_all", public_ids, options)
|
273
275
|
end
|
274
276
|
|
277
|
+
# Populates metadata fields with the given values. Existing values will be overwritten.
|
278
|
+
#
|
279
|
+
# Any metadata-value pairs given are merged with any existing metadata-value pairs
|
280
|
+
# (an empty value for an existing metadata field clears the value).
|
281
|
+
#
|
282
|
+
# @param [Hash] metadata A list of custom metadata fields (by external_id) and the values to assign to each of them.
|
283
|
+
# @param [Array] public_ids An array of Public IDs of assets uploaded to Cloudinary.
|
284
|
+
# @param [Hash] options
|
285
|
+
# @option options [String] :resource_type The type of file. Default: image. Valid values: image, raw, video.
|
286
|
+
# @option options [String] :type The storage type. Default: upload. Valid values: upload, private, authenticated
|
287
|
+
# @return mixed a list of public IDs that were updated
|
288
|
+
# @raise [Cloudinary::Api:Error]
|
289
|
+
def self.update_metadata(metadata, public_ids, options = {})
|
290
|
+
self.call_api("metadata", options) do
|
291
|
+
{
|
292
|
+
timestamp: (options[:timestamp] || Time.now.to_i),
|
293
|
+
type: options[:type],
|
294
|
+
public_ids: Cloudinary::Utils.build_array(public_ids),
|
295
|
+
metadata: Cloudinary::Utils.encode_context(metadata)
|
296
|
+
}
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
275
300
|
private
|
276
301
|
|
277
302
|
def self.call_tags_api(tag, command, public_ids = [], options = {})
|
@@ -317,11 +342,13 @@ class Cloudinary::Uploader
|
|
317
342
|
non_signable ||= []
|
318
343
|
|
319
344
|
unless options[:unsigned]
|
320
|
-
api_key
|
321
|
-
api_secret
|
322
|
-
|
323
|
-
params[:
|
345
|
+
api_key = options[:api_key] || Cloudinary.config.api_key || raise(CloudinaryException, "Must supply api_key")
|
346
|
+
api_secret = options[:api_secret] || Cloudinary.config.api_secret || raise(CloudinaryException, "Must supply api_secret")
|
347
|
+
signature_algorithm = options[:signature_algorithm]
|
348
|
+
params[:signature] = Cloudinary::Utils.api_sign_request(params.reject { |k, v| non_signable.include?(k) }, api_secret, signature_algorithm)
|
349
|
+
params[:api_key] = api_key
|
324
350
|
end
|
351
|
+
proxy = options[:api_proxy] || Cloudinary.config.api_proxy
|
325
352
|
timeout = options.fetch(:timeout) { Cloudinary.config.to_h.fetch(:timeout, 60) }
|
326
353
|
|
327
354
|
result = nil
|
@@ -331,7 +358,7 @@ class Cloudinary::Uploader
|
|
331
358
|
headers['Content-Range'] = options[:content_range] if options[:content_range]
|
332
359
|
headers['X-Unique-Upload-Id'] = options[:unique_upload_id] if options[:unique_upload_id]
|
333
360
|
headers.merge!(options[:extra_headers]) if options[:extra_headers]
|
334
|
-
RestClient::Request.execute(:method => :post, :url => api_url, :payload => params.reject { |k, v| v.nil? || v=="" }, :timeout => timeout, :headers => headers) do
|
361
|
+
RestClient::Request.execute(:method => :post, :url => api_url, :payload => params.reject { |k, v| v.nil? || v=="" }, :timeout => timeout, :headers => headers, :proxy => proxy) do
|
335
362
|
|response, request, tmpresult|
|
336
363
|
raise CloudinaryException, "Server returned unexpected status code - #{response.code} - #{response.body}" unless [200, 400, 401, 403, 404, 500].include?(response.code)
|
337
364
|
begin
|
data/lib/cloudinary/utils.rb
CHANGED
@@ -32,19 +32,34 @@ class Cloudinary::Utils
|
|
32
32
|
|
33
33
|
PREDEFINED_VARS = {
|
34
34
|
"aspect_ratio" => "ar",
|
35
|
+
"aspectRatio" => "ar",
|
35
36
|
"current_page" => "cp",
|
37
|
+
"currentPage" => "cp",
|
36
38
|
"face_count" => "fc",
|
39
|
+
"faceCount" => "fc",
|
37
40
|
"height" => "h",
|
38
41
|
"initial_aspect_ratio" => "iar",
|
42
|
+
"initialAspectRatio" => "iar",
|
43
|
+
"trimmed_aspect_ratio" => "tar",
|
44
|
+
"trimmedAspectRatio" => "tar",
|
39
45
|
"initial_height" => "ih",
|
46
|
+
"initialHeight" => "ih",
|
40
47
|
"initial_width" => "iw",
|
48
|
+
"initialWidth" => "iw",
|
41
49
|
"page_count" => "pc",
|
50
|
+
"pageCount" => "pc",
|
42
51
|
"page_x" => "px",
|
52
|
+
"pageX" => "px",
|
43
53
|
"page_y" => "py",
|
54
|
+
"pageY" => "py",
|
44
55
|
"tags" => "tags",
|
45
56
|
"initial_duration" => "idu",
|
57
|
+
"initialDuration" => "idu",
|
46
58
|
"duration" => "du",
|
47
|
-
"width" => "w"
|
59
|
+
"width" => "w",
|
60
|
+
"illustration_score" => "ils",
|
61
|
+
"illustrationScore" => "ils",
|
62
|
+
"context" => "ctx"
|
48
63
|
}
|
49
64
|
|
50
65
|
SIMPLE_TRANSFORMATION_PARAMS = {
|
@@ -144,6 +159,16 @@ class Cloudinary::Utils
|
|
144
159
|
LONG_URL_SIGNATURE_LENGTH = 32
|
145
160
|
SHORT_URL_SIGNATURE_LENGTH = 8
|
146
161
|
|
162
|
+
UPLOAD_PREFIX = 'https://api.cloudinary.com'
|
163
|
+
|
164
|
+
ALGO_SHA1 = :sha1
|
165
|
+
ALGO_SHA256 = :sha256
|
166
|
+
|
167
|
+
ALGORITHM_SIGNATURE = {
|
168
|
+
ALGO_SHA1 => Digest::SHA1,
|
169
|
+
ALGO_SHA256 => Digest::SHA256,
|
170
|
+
}
|
171
|
+
|
147
172
|
def self.extract_config_params(options)
|
148
173
|
options.select{|k,v| URL_KEYS.include?(k)}
|
149
174
|
end
|
@@ -309,16 +334,16 @@ class Cloudinary::Utils
|
|
309
334
|
"if_" + normalize_expression(if_value) unless if_value.to_s.empty?
|
310
335
|
end
|
311
336
|
|
312
|
-
EXP_REGEXP = Regexp.new('(?<!\$)('+PREDEFINED_VARS.keys.join("|")+')'+'|('+CONDITIONAL_OPERATORS.keys.reverse.map { |k| Regexp.escape(k) }.join('|')+')(?=[ _])')
|
337
|
+
EXP_REGEXP = Regexp.new('(\$_*[^_ ]+)|(?<!\$)('+PREDEFINED_VARS.keys.join("|")+')'+'|('+CONDITIONAL_OPERATORS.keys.reverse.map { |k| Regexp.escape(k) }.join('|')+')(?=[ _])')
|
313
338
|
EXP_REPLACEMENT = PREDEFINED_VARS.merge(CONDITIONAL_OPERATORS)
|
314
339
|
|
315
340
|
def self.normalize_expression(expression)
|
316
341
|
if expression.nil?
|
317
|
-
|
342
|
+
nil
|
318
343
|
elsif expression.is_a?( String) && expression =~ /^!.+!$/ # quoted string
|
319
344
|
expression
|
320
345
|
else
|
321
|
-
expression.to_s.gsub(EXP_REGEXP
|
346
|
+
expression.to_s.gsub(EXP_REGEXP) { |match| EXP_REPLACEMENT[match] || match }.gsub(/[ _]+/, "_")
|
322
347
|
end
|
323
348
|
end
|
324
349
|
|
@@ -433,9 +458,9 @@ class Cloudinary::Utils
|
|
433
458
|
params_to_sign.map{|k,v| [k.to_s, v.is_a?(Array) ? v.join(",") : v]}.reject{|k,v| v.nil? || v == ""}.sort_by(&:first).map{|k,v| "#{k}=#{v}"}.join("&")
|
434
459
|
end
|
435
460
|
|
436
|
-
def self.api_sign_request(params_to_sign, api_secret)
|
461
|
+
def self.api_sign_request(params_to_sign, api_secret, signature_algorithm = nil)
|
437
462
|
to_sign = api_string_to_sign(params_to_sign)
|
438
|
-
|
463
|
+
hash("#{to_sign}#{api_secret}", signature_algorithm, :hexdigest)
|
439
464
|
end
|
440
465
|
|
441
466
|
# Returns a JSON array as String.
|
@@ -501,6 +526,7 @@ class Cloudinary::Utils
|
|
501
526
|
use_root_path = config_option_consume(options, :use_root_path)
|
502
527
|
auth_token = config_option_consume(options, :auth_token)
|
503
528
|
long_url_signature = config_option_consume(options, :long_url_signature)
|
529
|
+
signature_algorithm = config_option_consume(options, :signature_algorithm)
|
504
530
|
unless auth_token == false
|
505
531
|
auth_token = Cloudinary::AuthToken.merge_auth_token(Cloudinary.config.auth_token, auth_token)
|
506
532
|
end
|
@@ -545,7 +571,10 @@ class Cloudinary::Utils
|
|
545
571
|
raise(CloudinaryException, "Must supply api_secret") if (secret.nil? || secret.empty?)
|
546
572
|
to_sign = [transformation, sign_version && version, source_to_sign].reject(&:blank?).join("/")
|
547
573
|
to_sign = fully_unescape(to_sign)
|
548
|
-
|
574
|
+
signature_algorithm = long_url_signature ? ALGO_SHA256 : signature_algorithm
|
575
|
+
hash = hash("#{to_sign}#{secret}", signature_algorithm)
|
576
|
+
signature = Base64.urlsafe_encode64(hash)
|
577
|
+
signature = "s--#{signature[0, long_url_signature ? LONG_URL_SIGNATURE_LENGTH : SHORT_URL_SIGNATURE_LENGTH ]}--"
|
549
578
|
end
|
550
579
|
|
551
580
|
prefix = unsigned_download_url_prefix(source, cloud_name, private_cdn, cdn_subdomain, secure_cdn_subdomain, cname, secure, secure_distribution)
|
@@ -661,18 +690,31 @@ class Cloudinary::Utils
|
|
661
690
|
prefix
|
662
691
|
end
|
663
692
|
|
693
|
+
# Creates a base URL for the cloudinary api
|
694
|
+
#
|
695
|
+
# @param [Object] path Resource name
|
696
|
+
# @param [Hash] options Additional options
|
697
|
+
#
|
698
|
+
# @return [String]
|
699
|
+
def self.base_api_url(path, options = {})
|
700
|
+
cloudinary = options[:upload_prefix] || Cloudinary.config.upload_prefix || UPLOAD_PREFIX
|
701
|
+
cloud_name = options[:cloud_name] || Cloudinary.config.cloud_name || raise(CloudinaryException, 'Must supply cloud_name')
|
702
|
+
|
703
|
+
[cloudinary, 'v1_1', cloud_name, path].join('/')
|
704
|
+
end
|
705
|
+
|
664
706
|
def self.cloudinary_api_url(action = 'upload', options = {})
|
665
|
-
|
666
|
-
|
667
|
-
resource_type
|
668
|
-
return [cloudinary, "v1_1", cloud_name, resource_type, action].join("/")
|
707
|
+
resource_type = options[:resource_type] || 'image'
|
708
|
+
|
709
|
+
base_api_url([resource_type, action], options)
|
669
710
|
end
|
670
711
|
|
671
712
|
def self.sign_request(params, options={})
|
672
713
|
api_key = options[:api_key] || Cloudinary.config.api_key || raise(CloudinaryException, "Must supply api_key")
|
673
714
|
api_secret = options[:api_secret] || Cloudinary.config.api_secret || raise(CloudinaryException, "Must supply api_secret")
|
715
|
+
signature_algorithm = options[:signature_algorithm]
|
674
716
|
params = params.reject{|k, v| self.safe_blank?(v)}
|
675
|
-
params[:signature] =
|
717
|
+
params[:signature] = api_sign_request(params, api_secret, signature_algorithm)
|
676
718
|
params[:api_key] = api_key
|
677
719
|
params
|
678
720
|
end
|
@@ -736,6 +778,18 @@ class Cloudinary::Utils
|
|
736
778
|
download_archive_url(options.merge(:target_format => "zip"))
|
737
779
|
end
|
738
780
|
|
781
|
+
# Creates and returns a URL that when invoked creates an archive of a folder.
|
782
|
+
#
|
783
|
+
# @param [Object] folder_path Full path (from the root) of the folder to download.
|
784
|
+
# @param [Hash] options Additional options.
|
785
|
+
#
|
786
|
+
# @return [String]
|
787
|
+
def self.download_folder(folder_path, options = {})
|
788
|
+
resource_type = options[:resource_type] || "all"
|
789
|
+
|
790
|
+
download_archive_url(options.merge(:resource_type => resource_type, :prefixes => folder_path))
|
791
|
+
end
|
792
|
+
|
739
793
|
def self.signed_download_url(public_id, options = {})
|
740
794
|
aws_private_key_path = options[:aws_private_key_path] || Cloudinary.config.aws_private_key_path
|
741
795
|
if aws_private_key_path
|
@@ -1137,23 +1191,51 @@ class Cloudinary::Utils
|
|
1137
1191
|
REMOTE_URL_REGEX === url
|
1138
1192
|
end
|
1139
1193
|
|
1140
|
-
#
|
1141
|
-
#
|
1142
|
-
#
|
1143
|
-
#
|
1144
|
-
# @
|
1145
|
-
|
1146
|
-
|
1194
|
+
# The returned url should allow downloading the backedup asset based on the version and asset id
|
1195
|
+
#
|
1196
|
+
# asset and version id are returned with resource(<PUBLIC_ID1>, { versions: true })
|
1197
|
+
#
|
1198
|
+
# @param [String] asset_id Asset identifier
|
1199
|
+
# @param [String] version_id Specific version of asset to download
|
1200
|
+
# @param [Hash] options Additional options
|
1201
|
+
#
|
1202
|
+
# @return [String] An url for downloading a file
|
1203
|
+
def self.download_backedup_asset(asset_id, version_id, options = {})
|
1204
|
+
params = Cloudinary::Utils.sign_request({
|
1205
|
+
:timestamp => (options[:timestamp] || Time.now.to_i),
|
1206
|
+
:asset_id => asset_id,
|
1207
|
+
:version_id => version_id
|
1208
|
+
}, options)
|
1209
|
+
|
1210
|
+
"#{Cloudinary::Utils.base_api_url("download_backup", options)}?#{Cloudinary::Utils.hash_query_params((params))}"
|
1211
|
+
end
|
1147
1212
|
|
1148
|
-
|
1149
|
-
|
1150
|
-
|
1151
|
-
|
1152
|
-
|
1153
|
-
|
1213
|
+
# Format date in a format accepted by the usage API (e.g., 31-12-2020) if
|
1214
|
+
# passed value is of type Date, otherwise return the string representation of
|
1215
|
+
# the input.
|
1216
|
+
#
|
1217
|
+
# @param [Date|Object] date
|
1218
|
+
# @return [String]
|
1219
|
+
def self.to_usage_api_date_format(date)
|
1220
|
+
if date.is_a?(Date)
|
1221
|
+
date.strftime('%d-%m-%Y')
|
1222
|
+
else
|
1223
|
+
date.to_s
|
1224
|
+
end
|
1225
|
+
end
|
1154
1226
|
|
1155
|
-
|
1227
|
+
# Computes hash from input string using specified algorithm.
|
1228
|
+
#
|
1229
|
+
# @param [String] input String which to compute hash from
|
1230
|
+
# @param [String|nil] signature_algorithm Algorithm to use for computing hash
|
1231
|
+
# @param [Symbol] hash_method Hash method applied to a signature algorithm (:digest or :hexdigest)
|
1232
|
+
#
|
1233
|
+
# @return [String] Computed hash value
|
1234
|
+
def self.hash(input, signature_algorithm = nil, hash_method = :digest)
|
1235
|
+
signature_algorithm ||= Cloudinary.config.signature_algorithm || ALGO_SHA1
|
1236
|
+
algorithm = ALGORITHM_SIGNATURE[signature_algorithm] || raise("Unsupported algorithm '#{signature_algorithm}'")
|
1237
|
+
algorithm.public_send(hash_method, input)
|
1156
1238
|
end
|
1157
1239
|
|
1158
|
-
private_class_method :
|
1240
|
+
private_class_method :hash
|
1159
1241
|
end
|
data/lib/cloudinary/version.rb
CHANGED
@@ -3,12 +3,33 @@ module CloudinaryHelper
|
|
3
3
|
DEFAULT_POSTER_OPTIONS = { :format => 'jpg', :resource_type => 'video' }
|
4
4
|
DEFAULT_SOURCE_TYPES = %w(webm mp4 ogv)
|
5
5
|
DEFAULT_VIDEO_OPTIONS = { :resource_type => 'video' }
|
6
|
+
DEFAULT_SOURCES = [
|
7
|
+
{
|
8
|
+
:type => "mp4",
|
9
|
+
:codecs => "hev1",
|
10
|
+
:transformations => { :video_codec => "h265" }
|
11
|
+
},
|
12
|
+
{
|
13
|
+
:type => "webm",
|
14
|
+
:codecs => "vp9",
|
15
|
+
:transformations => { :video_codec => "vp9" }
|
16
|
+
},
|
17
|
+
{
|
18
|
+
:type => "mp4",
|
19
|
+
:transformations => { :video_codec => "auto" }
|
20
|
+
},
|
21
|
+
{
|
22
|
+
:type => "webm",
|
23
|
+
:transformations => { :video_codec => "auto" }
|
24
|
+
}
|
25
|
+
]
|
6
26
|
|
7
27
|
# Creates an HTML video tag for the provided +source+
|
8
28
|
#
|
9
29
|
# ==== Options
|
10
30
|
# * <tt>:source_types</tt> - Specify which source type the tag should include. defaults to webm, mp4 and ogv.
|
11
31
|
# * <tt>:source_transformation</tt> - specific transformations to use for a specific source type.
|
32
|
+
# * <tt>:sources</tt> - list of sources (overrides :source_types when present)
|
12
33
|
# * <tt>:poster</tt> - override default thumbnail:
|
13
34
|
# * url: provide an ad hoc url
|
14
35
|
# * options: with specific poster transformations and/or Cloudinary +:public_id+
|
@@ -20,6 +41,17 @@ module CloudinaryHelper
|
|
20
41
|
# cl_video_tag("mymovie.webm", :source_types => [:webm, :mp4], :poster => {:effect => 'sepia'}) do
|
21
42
|
# content_tag( :span, "Cannot present video!")
|
22
43
|
# end
|
44
|
+
# cl_video_tag("mymovie", :sources => [
|
45
|
+
# {
|
46
|
+
# :type => "mp4",
|
47
|
+
# :codecs => "hev1",
|
48
|
+
# :transformations => { :video_codec => "h265" }
|
49
|
+
# },
|
50
|
+
# {
|
51
|
+
# :type => "webm",
|
52
|
+
# :transformations => { :video_codec => "auto" }
|
53
|
+
# }
|
54
|
+
# ])
|
23
55
|
def cl_video_tag(source, options = {}, &block)
|
24
56
|
source = strip_known_ext(source)
|
25
57
|
video_attributes = [:autoplay,:controls,:loop,:muted,:poster, :preload]
|
@@ -48,30 +80,12 @@ module CloudinaryHelper
|
|
48
80
|
video_options[:poster] = cl_video_thumbnail_path(source, options)
|
49
81
|
end
|
50
82
|
|
51
|
-
|
52
|
-
source_types = Array(options.delete(:source_types))
|
53
|
-
fallback = (capture(&block) if block_given?) || options.delete(:fallback_content)
|
83
|
+
fallback = (capture(&block) if block_given?) || options.delete(:fallback_content)
|
54
84
|
|
55
|
-
if
|
56
|
-
|
57
|
-
content_tag('video', tag_options.merge(video_options)) do
|
58
|
-
source_tags = source_types.map do |type|
|
59
|
-
transformation = source_transformation[type.to_sym] || {}
|
60
|
-
cloudinary_tag("#{source}.#{type}", options.merge(transformation)) do |url, _tag_options|
|
61
|
-
mime_type = "video/#{(type == 'ogv' ? 'ogg' : type)}"
|
62
|
-
tag("source", :src => url, :type => mime_type)
|
63
|
-
end
|
64
|
-
end
|
65
|
-
source_tags.push(fallback.html_safe) unless fallback.blank?
|
66
|
-
safe_join(source_tags)
|
67
|
-
end
|
68
|
-
end
|
85
|
+
if options[:sources]
|
86
|
+
video_tag_from_sources(source, options, video_options, fallback)
|
69
87
|
else
|
70
|
-
|
71
|
-
video_options[:src] = cl_video_path("#{source}.#{source_types.first.to_sym}", transformation.merge(options))
|
72
|
-
cloudinary_tag(source, options) do |_source, tag_options|
|
73
|
-
content_tag('video', fallback, tag_options.merge(video_options))
|
74
|
-
end
|
88
|
+
video_tag_from_source_types(source, options, video_options, fallback)
|
75
89
|
end
|
76
90
|
end
|
77
91
|
|
@@ -96,6 +110,66 @@ module CloudinaryHelper
|
|
96
110
|
name.sub(/\.(#{DEFAULT_SOURCE_TYPES.join("|")})$/, '')
|
97
111
|
end
|
98
112
|
|
113
|
+
private
|
114
|
+
|
115
|
+
def video_tag_from_source_types(source_name, options, video_options, fallback)
|
116
|
+
source_transformation = options.delete(:source_transformation) || {}
|
117
|
+
source_types = Array(options.delete(:source_types))
|
118
|
+
|
119
|
+
if source_types.size > 1
|
120
|
+
sources = source_types.map do |type|
|
121
|
+
{
|
122
|
+
:type => type,
|
123
|
+
:transformations => source_transformation[type.to_sym] || {}
|
124
|
+
}
|
125
|
+
end
|
126
|
+
|
127
|
+
generate_tag_from_sources(:source_name => source_name,
|
128
|
+
:sources => sources,
|
129
|
+
:options => options,
|
130
|
+
:video_options => video_options,
|
131
|
+
:fallback => fallback)
|
132
|
+
else
|
133
|
+
transformation = source_transformation[source_types.first.to_sym] || {}
|
134
|
+
video_options[:src] = cl_video_path("#{source_name}.#{source_types.first.to_sym}", transformation.merge(options))
|
135
|
+
cloudinary_tag(source_name, options) do |_source, tag_options|
|
136
|
+
content_tag('video', fallback, tag_options.merge(video_options))
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def video_tag_from_sources(source_name, options, video_options, fallback)
|
142
|
+
sources = options.delete(:sources)
|
143
|
+
|
144
|
+
generate_tag_from_sources(:source_name => source_name,
|
145
|
+
:sources => sources,
|
146
|
+
:options => options,
|
147
|
+
:video_options => video_options,
|
148
|
+
:fallback => fallback)
|
149
|
+
end
|
150
|
+
|
151
|
+
def generate_tag_from_sources(params)
|
152
|
+
source_name, sources, options, video_options, fallback = params.values_at(:source_name, :sources, :options, :video_options, :fallback)
|
153
|
+
|
154
|
+
cloudinary_tag(source_name, options) do |_source, tag_options|
|
155
|
+
content_tag('video', tag_options.merge(video_options)) do
|
156
|
+
source_tags = sources.map do |source|
|
157
|
+
type = source[:type]
|
158
|
+
transformation = source[:transformations] || {}
|
159
|
+
cloudinary_tag("#{source_name}.#{type}", options.merge(transformation)) do |url, _tag_options|
|
160
|
+
mime_type = "video/#{(type == 'ogv' ? 'ogg' : type)}"
|
161
|
+
if source[:codecs]
|
162
|
+
codecs = source[:codecs].is_a?(Array) ? source[:codecs].join(", ") : source[:codecs]
|
163
|
+
mime_type = "#{mime_type}; codecs=#{codecs}"
|
164
|
+
end
|
165
|
+
tag("source", :src => url, :type => mime_type)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
source_tags.push(fallback.html_safe) unless fallback.blank?
|
169
|
+
safe_join(source_tags)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
99
173
|
end
|
100
174
|
|
101
175
|
|