Skip to content

Commit bf6d9e5

Browse files
committed
add support for video tag. refactor Url based tags
1 parent d6882f0 commit bf6d9e5

File tree

13 files changed

+687
-170
lines changed

13 files changed

+687
-170
lines changed

cloudinary-core/src/main/java/com/cloudinary/StoredFile.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ public class StoredFile {
2020

2121
private static final String IMAGE_RESOURCE_TYPE = "image";
2222

23+
private static final String VIDEO_RESOURCE_TYPE = "video";
24+
2325
private static final String AUTO_RESOURCE_TYPE = "auto";
2426

2527
private static final Pattern PRELOADED_PATTERN = Pattern.compile("^([^\\/]+)\\/([^\\/]+)\\/v(\\d+)\\/([^#]+)#?([^\\/]+)?$");
@@ -119,4 +121,8 @@ public String getComputedSignature(Cloudinary cloudinary) {
119121
public boolean getIsImage() {
120122
return IMAGE_RESOURCE_TYPE.equals(resourceType) || AUTO_RESOURCE_TYPE.equals(resourceType);
121123
}
124+
125+
public boolean getIsVideo() {
126+
return VIDEO_RESOURCE_TYPE.equals(resourceType);
127+
}
122128
}

cloudinary-core/src/main/java/com/cloudinary/Transformation.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,12 @@ public Transformation params(Map transformation) {
349349
public Transformation chain() {
350350
return params(new HashMap());
351351
}
352+
353+
public Transformation chainWith(Transformation transformation) {
354+
List<Map> transformations = dup(this.transformations);
355+
transformations.addAll(dup(transformation.transformations));
356+
return new Transformation(transformations);
357+
}
352358

353359
public Transformation param(String key, Object value) {
354360
transformation.put(key, value);

cloudinary-core/src/main/java/com/cloudinary/Uploader.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,11 @@ public String imageUploadTag(String field, Map options, Map<String, Object> html
351351

352352
StringBuilder builder = new StringBuilder();
353353
builder.append("<input type='file' name='file' data-url='").append(cloudinaryUploadUrl).append("' data-form-data='").append(tagParams)
354-
.append("' data-cloudinary-field='").append(field).append("' class='cloudinary-fileupload");
354+
.append("' data-cloudinary-field='").append(field).append("'");
355+
if (options.containsKey("chunk_size"))
356+
builder.append(" data-max-chunk-size='").append(options.get("chunk_size")).append("'");
357+
builder.append(" class='cloudinary-fileupload");
358+
355359
if (htmlOptions.containsKey("class")) {
356360
builder.append(" ").append(htmlOptions.get("class"));
357361
}

cloudinary-core/src/main/java/com/cloudinary/Url.java

Lines changed: 234 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
import java.net.URLDecoder;
55
import java.security.MessageDigest;
66
import java.security.NoSuchAlgorithmException;
7+
import java.util.ArrayList;
8+
import java.util.HashMap;
9+
import java.util.List;
710
import java.util.Locale;
811
import java.util.Map;
912
import java.util.TreeMap;
@@ -16,22 +19,59 @@
1619
import com.cloudinary.utils.StringUtils;
1720

1821
public class Url {
22+
private final Cloudinary cloudinary;
1923
private final Configuration config;
2024
String publicId = null;
2125
String type = null;
22-
String resourceType = "image";
26+
String resourceType = null;
2327
String format = null;
2428
String version = null;
2529
Transformation transformation = null;
2630
boolean signUrl;
2731
String source = null;
2832
private String urlSuffix;
2933
private Boolean useRootPath;
34+
Map<String, Transformation> sourceTransformation = null;
35+
String[] sourceTypes = null;
36+
String fallbackContent = null;
37+
Transformation posterTransformation = null;
38+
String posterSource = null;
39+
Url posterUrl = null;
40+
3041
private static final String CL_BLANK = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
42+
public static final String[] DEFAULT_VIDEO_SOURCE_TYPES = {"webm", "mp4", "ogv"};
43+
private static final Pattern VIDEO_EXTENSION_RE = Pattern.compile("\\.(" + StringUtils.join(DEFAULT_VIDEO_SOURCE_TYPES, "|") + ")$");
3144

3245
public Url(Cloudinary cloudinary) {
46+
this.cloudinary = cloudinary;
3347
this.config = new Configuration(cloudinary.config);
3448
}
49+
50+
public Url clone() {
51+
Url cloned = cloudinary.url();
52+
53+
cloned.fallbackContent = this.fallbackContent;
54+
cloned.format = this.format;
55+
cloned.posterSource = this.posterSource;
56+
if (this.posterTransformation != null) cloned.posterTransformation = new Transformation(this.posterTransformation);
57+
if (this.posterUrl != null) cloned.posterUrl = this.posterUrl.clone();
58+
cloned.publicId = this.publicId;
59+
cloned.resourceType = this.resourceType;
60+
cloned.signUrl = this.signUrl;
61+
cloned.source = this.source;
62+
if (this.transformation != null) cloned.transformation = new Transformation(this.transformation);
63+
if (this.sourceTransformation != null) {
64+
cloned.sourceTransformation = new HashMap<String, Transformation>();
65+
for (Map.Entry<String, Transformation> keyValuePair : this.sourceTransformation.entrySet()) {
66+
cloned.sourceTransformation.put(keyValuePair.getKey(), keyValuePair.getValue());
67+
};
68+
}
69+
cloned.sourceTypes = this.sourceTypes;
70+
cloned.urlSuffix = this.urlSuffix;
71+
cloned.useRootPath = this.useRootPath;
72+
73+
return cloned;
74+
}
3575

3676
private static Pattern identifierPattern = Pattern.compile("^(?:([^/]+)/)??(?:([^/]+)/)??(?:v(\\d+)/)?" + "(?:([^#/]+?)(?:\\.([^.#/]+))?)(?:#([^/]+))?$");
3777

@@ -170,6 +210,78 @@ public Url signed(boolean signUrl) {
170210
this.signUrl = signUrl;
171211
return this;
172212
}
213+
214+
public Url sourceTransformation(Map<String, Transformation> sourceTransformation) {
215+
this.sourceTransformation = sourceTransformation;
216+
return this;
217+
}
218+
219+
public Url sourceTransformationFor(String source, Transformation transformation) {
220+
if (this.sourceTransformation == null) {
221+
this.sourceTransformation = new HashMap<String, Transformation>();
222+
}
223+
this.sourceTransformation.put(source, transformation);
224+
return this;
225+
}
226+
227+
public Url sourceTypes(String[] sourceTypes) {
228+
this.sourceTypes = sourceTypes;
229+
return this;
230+
}
231+
232+
public Url fallbackContent(String fallbackContent) {
233+
this.fallbackContent = fallbackContent;
234+
return this;
235+
}
236+
237+
public Url posterTransformation(Transformation posterTransformation) {
238+
this.posterTransformation = posterTransformation;
239+
return this;
240+
}
241+
242+
@SuppressWarnings("rawtypes")
243+
public Url posterTransformation(List<Map> posterTransformations) {
244+
this.posterTransformation = new Transformation(posterTransformations);
245+
return this;
246+
}
247+
248+
@SuppressWarnings({ "rawtypes", "unchecked" })
249+
public Url posterTransformation(Map posterTransformations) {
250+
List<Map> transformations = new ArrayList<Map>();
251+
Map copy = new HashMap();
252+
copy.putAll(posterTransformations);
253+
transformations.add(copy);
254+
this.posterTransformation = new Transformation(transformations);
255+
return this;
256+
}
257+
258+
public Url posterSource(String posterSource) {
259+
this.posterSource = posterSource;
260+
return this;
261+
}
262+
263+
public Url posterUrl(Url posterUrl) {
264+
this.posterUrl = posterUrl;
265+
return this;
266+
}
267+
268+
public Url poster(Object poster) {
269+
if (poster instanceof Transformation) {
270+
return posterTransformation((Transformation) poster);
271+
} else if (poster instanceof List) {
272+
return posterTransformation((List) poster);
273+
} else if (poster instanceof Map) {
274+
return posterTransformation((Map) poster);
275+
} else if (poster instanceof Url) {
276+
return posterUrl((Url) poster);
277+
} else if (poster instanceof String) {
278+
return posterSource((String) poster);
279+
} else if (poster == null || poster.equals(Boolean.FALSE)){
280+
return posterSource("");
281+
} else {
282+
throw new IllegalArgumentException("Illegal value type supplied to poster. must be one of: <Transformation>, <List<Map>>, <Map>, <Url>, <String>");
283+
}
284+
}
173285

174286
public String generate() {
175287
return generate(null);
@@ -195,16 +307,17 @@ public String generate(String source) {
195307
}
196308
}
197309

198-
199-
200310
if (source == null) {
201311
if (publicId == null) {
202-
return null;
312+
if (this.source == null) {
313+
return null;
314+
}
315+
source = this.source;
316+
} else {
317+
source = publicId;
203318
}
204-
source = publicId;
205319
}
206320

207-
208321
if (source.toLowerCase(Locale.US).matches("^https?:/.*")) {
209322
if (StringUtils.isEmpty(type) || "asset".equals(type) ) {
210323
return source;
@@ -252,6 +365,8 @@ public String generate(String source) {
252365
signature = "s--" + signature.substring(0, 8) + "--" ;
253366
}
254367

368+
String resourceType = this.resourceType;
369+
if (resourceType == null) resourceType = "image";
255370
String finalResourceType = finalizeResourceType(resourceType,type,urlSuffix,useRootPath,config.shorten);
256371
String prefix = unsignedDownloadUrlPrefix(source,config.cloudName,config.privateCdn,config.cdnSubdomain,config.secureCdnSubdomain,config.cname,config.secure,config.secureDistribution);
257372

@@ -379,7 +494,7 @@ public String imageTag(String source) {
379494
}
380495

381496
public String imageTag(Map<String, String> attributes) {
382-
return imageTag("", attributes);
497+
return imageTag(null, attributes);
383498
}
384499

385500
public String imageTag(String source, Map<String, String> attributes) {
@@ -416,6 +531,118 @@ public String imageTag(String source, Map<String, String> attributes) {
416531
builder.append("/>");
417532
return builder.toString();
418533
}
534+
535+
public String videoTag() {
536+
return videoTag("", new HashMap<String, String>());
537+
}
538+
539+
public String videoTag(Map<String, String> attributes) {
540+
return videoTag("", attributes);
541+
}
542+
543+
private String finalizePosterUrl(String source) {
544+
String posterUrl = null;
545+
if (this.posterUrl != null) {
546+
posterUrl = this.posterUrl.generate();
547+
} else if (this.posterTransformation != null) {
548+
posterUrl = this.clone().format("jpg").transformation(new Transformation(this.posterTransformation))
549+
.generate(source);
550+
} else if (this.posterSource != null) {
551+
if (!StringUtils.isEmpty(this.posterSource))
552+
posterUrl = this.clone().format("jpg").generate(this.posterSource);
553+
} else {
554+
posterUrl = this.clone().format("jpg").generate(source);
555+
}
556+
return posterUrl;
557+
}
558+
559+
private void appendVideoSources(StringBuilder html, String source, String sourceType ) {
560+
Url sourceUrl = this.clone();
561+
if (this.sourceTransformation != null) {
562+
Transformation transformation = this.transformation;
563+
Transformation sourceTransformation = null;
564+
if (this.sourceTransformation.get(sourceType) != null)
565+
sourceTransformation = new Transformation(this.sourceTransformation.get(sourceType));
566+
if (transformation == null) {
567+
transformation = sourceTransformation;
568+
} else if (sourceTransformation != null) {
569+
transformation = transformation.chainWith(sourceTransformation);
570+
}
571+
sourceUrl.transformation(transformation);
572+
}
573+
String src = sourceUrl.format(sourceType).generate(source);
574+
String videoType = sourceType;
575+
if (sourceType.equals("ogv"))
576+
videoType = "ogg";
577+
String mimeType = "video/" + videoType;
578+
html.append("<source src='").append(src).append("' type='").append(mimeType).append("'>");
579+
}
580+
581+
public String videoTag(String source, Map<String, String> attributes) {
582+
if (StringUtils.isEmpty(source))
583+
source = this.source;
584+
if (StringUtils.isEmpty(source))
585+
source = publicId;
586+
if (StringUtils.isEmpty(source))
587+
throw new IllegalArgumentException("must supply source or public id");
588+
source = VIDEO_EXTENSION_RE.matcher(source).replaceFirst("");
589+
590+
if (this.resourceType == null) this.resourceType = "video";
591+
attributes = new TreeMap<String, String>(attributes); // Make sure they are ordered.
592+
593+
String[] sourceTypes = this.sourceTypes;
594+
595+
if (sourceTypes == null) {
596+
sourceTypes = DEFAULT_VIDEO_SOURCE_TYPES;
597+
}
598+
599+
String posterUrl = this.finalizePosterUrl(source);
600+
601+
if (!StringUtils.isEmpty(posterUrl))
602+
attributes.put("poster", posterUrl);
603+
604+
StringBuilder html = new StringBuilder().append("<video");
605+
606+
String url = null;
607+
608+
boolean multiSource = sourceTypes.length > 1;
609+
if (!multiSource) {
610+
url = generate(source + "." + sourceTypes[0]);
611+
attributes.put("src", url);
612+
} else {
613+
generate(source);
614+
}
615+
616+
if (this.transformation.getHtmlHeight() != null)
617+
attributes.put("height", this.transformation.getHtmlHeight());
618+
if (attributes.containsKey("html_height"))
619+
attributes.put("height", attributes.remove("html_height"));
620+
if (this.transformation.getHtmlWidth() != null)
621+
attributes.put("width", this.transformation.getHtmlWidth());
622+
if (attributes.containsKey("html_width"))
623+
attributes.put("width", attributes.remove("html_width"));
624+
625+
for (Map.Entry<String, String> attr : attributes.entrySet()) {
626+
html.append(" ").append(attr.getKey());
627+
if (attr.getValue() != null) {
628+
String value = ObjectUtils.asString(attr.getValue());
629+
html.append("='").append(value).append("'");
630+
}
631+
}
632+
633+
html.append(">");
634+
635+
if (multiSource) {
636+
for (String sourceType : sourceTypes) {
637+
this.appendVideoSources(html, source, sourceType);
638+
}
639+
}
640+
641+
if (this.fallbackContent != null)
642+
html.append(this.fallbackContent);
643+
html.append("</video>");
644+
return html.toString();
645+
}
419646

420647
public String generateSpriteCss(String source) {
421648
this.type = "sprite";

0 commit comments

Comments
 (0)