001/** 002 * 003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. 004 * 005 * This library is free software; you can redistribute it and/or 006 * modify it under the terms of the GNU Lesser General Public 007 * License as published by the Free Software Foundation; either 008 * version 2.1 of the License, or (at your option) any later version. 009 * 010 * This library is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 * Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public 016 * License along with this library. If not, see <http://www.gnu.org/licenses/>. 017 * 018 **/ 019package lucee.runtime.tag; 020 021 022import java.awt.Color; 023import java.io.IOException; 024import java.util.ArrayList; 025import java.util.Iterator; 026import java.util.List; 027import java.util.Map; 028import java.util.Map.Entry; 029 030import lucee.commons.color.ColorCaster; 031import lucee.commons.io.res.Resource; 032import lucee.commons.io.res.type.file.FileResource; 033import lucee.commons.lang.StringUtil; 034import lucee.runtime.PageContext; 035import lucee.runtime.PageSource; 036import lucee.runtime.exp.ApplicationException; 037import lucee.runtime.exp.ExpressionException; 038import lucee.runtime.exp.PageException; 039import lucee.runtime.ext.tag.BodyTagSupport; 040import lucee.runtime.functions.string.JSStringFormat; 041import lucee.runtime.op.Caster; 042import lucee.runtime.op.Decision; 043import lucee.runtime.type.Collection.Key; 044import lucee.runtime.type.Struct; 045import lucee.runtime.video.VideoInput; 046import lucee.runtime.video.VideoInputImpl; 047import lucee.runtime.video.VideoUtilImpl; 048 049import org.apache.commons.collections.map.ReferenceMap; 050 051/** 052 * implementation of the tag Compiler 053 */ 054public class VideoPlayerJW extends BodyTagSupport { 055 056 private static final int TYPE_NONE = 0; 057 private static final int TYPE_PLAYLIST = 1; 058 private static final int TYPE_CHAPTERS = 2; 059 060 private static final int PLAYLIST_NONE = 0; 061 private static final int PLAYLIST_RIGHT = 1; 062 private static final int PLAYLIST_BOTTOM = 2; 063 064 private static Color BG_COLOR=new Color(51,51,51); 065 private static Color FG_COLOR=new Color(198,198,198); 066 067 private String video=null; 068 private boolean autostart=false; 069 070 private lucee.runtime.video.Range showPlay=lucee.runtime.video.Range.TRUE; 071 private lucee.runtime.video.Range showPause=lucee.runtime.video.Range.TRUE; 072 private lucee.runtime.video.Range showTimeline=lucee.runtime.video.Range.TRUE; 073 private List params=new ArrayList(); 074 private java.awt.Color bgcolor=BG_COLOR; 075 private java.awt.Color fgcolor=FG_COLOR; 076 private java.awt.Color screencolor=null; 077 private java.awt.Color lightcolor=null; 078 079 080 081 private int width=-1; 082 private int height=-1; 083 private boolean debug; 084 private boolean allowfullscreen; 085 private String strWidth; 086 private String strHeight; 087 private static Map sizes=new ReferenceMap(ReferenceMap.SOFT,ReferenceMap.SOFT); 088 089 // JW 090 private Struct passthrough=null; 091 private String preview; 092 093 private boolean group=false; 094 private boolean playlistThumbnails=true; 095 private int playlistSize=-1; 096 private int playlist=PLAYLIST_NONE; 097 private String target="_self"; 098 private boolean linkfromdisplay; 099 private String overstretch; 100 private boolean download; 101 private String id; 102 private String align; 103 private static int _id=0; 104 105 public VideoPlayerJW() { 106 107 } 108 109 @Override 110 public void release() { 111 super.release(); 112 video=null; 113 autostart=false; 114 115 showPlay=lucee.runtime.video.Range.TRUE; 116 showPause=lucee.runtime.video.Range.TRUE; 117 showTimeline=lucee.runtime.video.Range.TRUE; 118 params.clear(); 119 debug=false; 120 121 id=null; 122 group=false; 123 playlist = PLAYLIST_NONE; 124 playlistSize=-1; 125 playlistThumbnails=true; 126 target="_self"; 127 linkfromdisplay=false; 128 overstretch=null; 129 /*group="yes" 130 playlist="right,bottom,none" 131 playlistSize="300" 132 playlistThumbnails="300" 133 134 */ 135 align=null; 136 137 138 bgcolor=BG_COLOR; 139 fgcolor=FG_COLOR; 140 screencolor=null; 141 lightcolor=null; 142 width=-1; 143 height=-1; 144 145 strWidth=null; 146 strHeight=null; 147 148 // JW 149 passthrough=null; 150 preview=null; 151 allowfullscreen=false; 152 download=false; 153 } 154 155 156 157 protected void setParam(VideoPlayerParamBean param) { 158 params.add(param); 159 } 160 161 /** 162 * @param video the video to set 163 */ 164 public void setVideo(String video) { 165 this.video = video; 166 } 167 168 /** 169 * @param autostart the autostart to set 170 */ 171 public void setAutostart(boolean autostart) { 172 this.autostart = autostart; 173 } 174 175 /** 176 * @param showPlay the showPlay to set 177 * @throws PageException 178 */ 179 public void setShowplay(String showPlay) throws PageException { 180 this.showPlay = lucee.runtime.video.Range.toRange(showPlay); 181 } 182 public void setId(String id) throws PageException { 183 this.id=Caster.toVariableName(id); 184 } 185 186 /** 187 * @param showPause the showPause to set 188 * @throws PageException 189 */ 190 public void setShowpause(String showPause) throws PageException { 191 this.showPause = lucee.runtime.video.Range.toRange(showPause); 192 } 193 194 /** 195 * @param showTimeline the showTimeline to set 196 * @throws PageException 197 */ 198 public void setShowtimeline(String showTimeline) throws PageException { 199 this.showTimeline = lucee.runtime.video.Range.toRange(showTimeline); 200 } 201 202 /** 203 * @param color the background color to set 204 * @throws PageException 205 */ 206 public void setBgcolor(String color) throws PageException { 207 this.bgcolor = ColorCaster.toColor(color); 208 } 209 public void setBackgroundcolor(String color) throws PageException { 210 setBgcolor(color); 211 } 212 public void setBackground(String color) throws PageException { 213 setBgcolor(color); 214 } 215 public void setScreencolor(String color) throws PageException { 216 this.screencolor = ColorCaster.toColor(color); 217 } 218 public void setLightcolor(String color) throws PageException { 219 this.lightcolor = ColorCaster.toColor(color); 220 } 221 222 223 /** 224 * @param color the background color to set 225 * @throws PageException 226 */ 227 public void setFgcolor(String color) throws PageException { 228 this.fgcolor = ColorCaster.toColor(color); 229 } 230 231 public void setForeground(String color) throws PageException { 232 setFgcolor(color); 233 } 234 235 public void setForegroundcolor(String color) throws PageException { 236 setFgcolor(color); 237 } 238 239 /** 240 * @param width the width to set 241 */ 242 public void setWidth(String strWidth) { 243 this.strWidth = strWidth; 244 this.width = Caster.toIntValue(strWidth,-1); 245 } 246 247 /** 248 * @param height the height to set 249 */ 250 public void setHeight(String strHeight) { 251 this.strHeight = strHeight; 252 this.height = Caster.toIntValue(strHeight,-1); 253 } 254 255 @Override 256 public int doStartTag() throws PageException { 257 return EVAL_BODY_INCLUDE; 258 } 259 public int doEndTag() throws PageException { 260 261 // fill top video to params 262 if(video!=null) { 263 VideoPlayerParamBean vppb = new VideoPlayerParamBean(); 264 vppb.setVideo(pageContext,video); 265 if(!StringUtil.isEmpty(preview))vppb.setImage(pageContext,preview); 266 params.add(vppb); 267 } 268 else { 269 if(!StringUtil.isEmpty(preview)) 270 throw new ApplicationException("attribute [preview] is only allowed when attribute [video] is used"); 271 } 272 273 if(params.size()==0) 274 throw new ApplicationException("you have to define at least one video source"); 275 276 277 278 279 280 // calculate dimension 281 int[] dim = calculateDimension(pageContext,params,width,strWidth,height,strHeight); 282 283 //print.out(width+":"+height); 284 //print.out(strWidth+":"+strHeight); 285 width=dim[0]; 286 height=dim[1]; 287 288 //print.out(width+":"+height); 289 290 291 // playlist 292 int dspHeight=-1,dspWidth=-1; 293 if(playlist!=PLAYLIST_NONE) { 294 if(playlistSize<20)playlistSize=playlist==PLAYLIST_BOTTOM?100:200; 295 if(playlist==PLAYLIST_BOTTOM) { 296 dspHeight=height; 297 height+=playlistSize; 298 } 299 else { 300 dspWidth=width; 301 width+=playlistSize; 302 } 303 } 304 else playlistThumbnails=false; 305 306 307 VideoPlayerParamBean param; 308 309 310 String id=getId();// 311 String placeholderId="ph_"+id; 312 String flashId="swf_"+id; 313 314 StringBuffer sb=new StringBuffer(); 315 316 write(sb,"<script type=\"text/javascript\" src=\"/lucee/swfobject.js.cfm\"></script>"); 317 write(sb,"<div "); 318 319 320 if(passthrough!=null) { 321 Iterator<Entry<Key, Object>> it = passthrough.entryIterator(); 322 Entry<Key, Object> e; 323 String key; 324 while(it.hasNext()) { 325 e = it.next(); 326 key=e.getKey().getString(); 327 if(StringUtil.startsWithIgnoreCase(key, "div.")) 328 write(sb,key.substring(4)+"=\""+Caster.toString(e.getValue())+"\" "); 329 } 330 } 331 write(sb,(align!=null?"align=\""+align+"\"":"")+" id=\""+placeholderId+"\"><a href=\"http://www.macromedia.com/go/getflashplayer\">Get the Flash Player</a> to see this player.</a></div>"); 332 333 334 335 write(sb,"<script type=\"text/javascript\">\n"); 336 write(sb,"var so = new SWFObject(\"/lucee/mediaplayer.swf.cfm\", \""+flashId+"\", \""+width+"\", \""+(height)+"\", \"8\", \""+format("#",bgcolor)+"\");\n"); 337 338 // script 339 addParam(sb,"allowscriptaccess","always"); 340 addVariable(sb,"enablejs","true"); 341 addVariable(sb,"javascriptid",flashId); 342 343 addVariable(sb,"shuffle","false"); 344 addVariable(sb,"linktarget",target); 345 addVariable(sb,"linkfromdisplay",Caster.toString(linkfromdisplay)); 346 addVariable(sb,"abouttxt","Lucee Video Player"); 347 addVariable(sb,"aboutlnk","http://www.lucee.org"); 348 349 // control 350 addParam(sb,"allowfullscreen",Caster.toString(allowfullscreen)); 351 addParam(sb,"usefullscreen",Caster.toString(allowfullscreen)); 352 addVariable(sb,"autostart",Caster.toString(autostart)); 353 if(!StringUtil.isEmpty(overstretch))addVariable(sb,"overstretch",overstretch); 354 addVariable(sb,"showdownload",Caster.toString(download)); 355 356 357 358 359 // color 360 if(lightcolor==null)lightcolor=fgcolor.brighter(); 361 if(screencolor==null)screencolor=Color.BLACK;//fgcolor.brighter(); 362 addVariable(sb,"backcolor",format("0x",bgcolor)); 363 addVariable(sb,"frontcolor",format("0x",fgcolor)); 364 addVariable(sb,"lightcolor",format("0x",lightcolor)); 365 addVariable(sb,"screencolor",format("0x",screencolor)); 366 367 if(passthrough!=null) { 368 Iterator<Entry<Key, Object>> it = passthrough.entryIterator(); 369 Entry<Key, Object> e; 370 String key; 371 while(it.hasNext()) { 372 e = it.next(); 373 key=e.getKey().getString(); 374 if(StringUtil.startsWithIgnoreCase(key, "param.")) 375 addParam(sb,key.substring(6),Caster.toString(e.getValue())); 376 else if(StringUtil.startsWithIgnoreCase(key, "variable.")) 377 addVariable(sb,key.substring(9),Caster.toString(e.getValue())); 378 else if(StringUtil.startsWithIgnoreCase(key, "div.")); 379 else 380 addVariable(sb,key,Caster.toString(e.getValue())); 381 } 382 } 383 384 if(params.size()>1 && group) addVariable(sb,"repeat","true"); 385 386 /*if(playlist!=PLAYLIST_NONE) { 387 if(playlistSize<20)playlistSize=playlist==PLAYLIST_BOTTOM?300:200; 388 if(playlist==PLAYLIST_BOTTOM) { 389 addVariable(sb,"displayheight",Caster.toString(height)); 390 height+=playlistSize; 391 } 392 else { 393 addVariable(sb,"displaywidth",Caster.toString(width)); 394 width+=playlistSize; 395 } 396 if(playlistThumbnails && hasImages())addVariable(sb,"thumbsinplaylist","true"); 397 }*/ 398 399 // dimension 400 if(dspWidth>0)addVariable(sb,"displaywidth",Caster.toString(dspWidth)); 401 if(dspHeight>0)addVariable(sb,"displayheight",Caster.toString(dspHeight)); 402 addVariable(sb,"width",Caster.toString(width)); 403 addVariable(sb,"height",Caster.toString(height)); 404 if(playlistThumbnails && hasImages())addVariable(sb,"thumbsinplaylist","true"); 405 406 //if(preview!=null) addVariable(sb,"image",toPath(preview)); 407 //Iterator it = params.iterator(); 408 //addVariable("file","/rvp/videos/David.flv"); 409 //addVariable("captions","http://localhost:8080/caption.cfm"); 410 //while(it.hasNext()) { 411 //param=(VideoPlayerParamBean) it.next(); 412 //addVariable(sb,"file",toPath(param.getResource())); 413 //break; 414 //} 415 //addVariable("image","video.jpg"); 416 417 write(sb,"so.write(\""+placeholderId+"\");\n"); 418 //if(params.size()>1) { 419 Iterator it = params.iterator(); 420 while(it.hasNext()) { 421 param=(VideoPlayerParamBean) it.next(); 422 addItem(sb,flashId,param); 423 } 424 //} 425 write(sb,"</script>"); 426 try { 427 if(debug) { 428 pageContext.forceWrite("<pre>"+StringUtil.replace(sb.toString(), "<", "<", false)+"</pre>"); 429 } 430 pageContext.forceWrite(sb.toString()); 431 432 433 } 434 catch (IOException e) { 435 436 } 437 return EVAL_PAGE; 438 } 439 440 private synchronized String getId() { 441 if(!StringUtil.isEmpty(id)) return id; 442 _id++; 443 if(_id<0) _id=1; 444 return ""+_id; 445 } 446 447 private boolean hasImages() { 448 Iterator it = params.iterator(); 449 while(it.hasNext()) { 450 if(((VideoPlayerParamBean) it.next()).getImage()!=null) return true; 451 } 452 return false; 453 } 454 455 private void addItem(StringBuffer sb, String id, VideoPlayerParamBean param) { 456 //sb.append("setTimeout('thisMovie(\""+id+"\").addItem({file:\""+JSStringFormat.invoke(path)+"\"},null);',1000);\n"); 457 458 // file 459 String file = "file:'"+JSStringFormat.invoke(toPath(param.getResource()))+"'"; 460 461 // image 462 String image=""; 463 if(param.getImage()!=null) { 464 image=",image:'"+JSStringFormat.invoke(toPath(param.getImage()))+"'"; 465 } 466 467 // title 468 String title=""; 469 if(!StringUtil.isEmpty(param.getTitle())) { 470 title=",title:'"+JSStringFormat.invoke(param.getTitle())+"'"; 471 } 472 473 // link 474 String link=""; 475 if(!StringUtil.isEmpty(param.getLink())) { 476 link=",link:'"+JSStringFormat.invoke(param.getLink())+"'"; 477 } 478 479 // author 480 String author=""; 481 if(!StringUtil.isEmpty(param.getAuthor())) { 482 author=",author:'"+JSStringFormat.invoke(param.getAuthor())+"'"; 483 } 484 485 sb.append("addItem('"+id+"',{"+file+title+image+link+author+"});\n"); 486 } 487 488 private void addVariable(StringBuffer sb, String name, String value) { 489 value=JSStringFormat.invoke(value); 490 if(!(value.equals("false") || value.equals("true"))) 491 value="'"+value+"'"; 492 sb.append("so.addVariable('"+JSStringFormat.invoke(name)+"',"+value+");\n"); 493 } 494 495 private void addParam(StringBuffer sb,String name, String value) { 496 sb.append("so.addParam('"+name+"','"+value+"');\n"); 497 } 498 499 private static int[] calculateDimension(PageContext pc,List params,int width, String strWidth,int height, String strHeight) throws PageException { 500 Iterator it = params.iterator(); 501 ArrayList sources=new ArrayList(); 502 //Resource[] sources=new Resource[params.size()]; 503 VideoPlayerParamBean param; 504 505 while(it.hasNext()) { 506 param = (VideoPlayerParamBean) it.next(); 507 if(param.getVideo()!=null) 508 sources.add(new VideoInputImpl(param.getVideo())); 509 } 510 return VideoUtilImpl.getInstance().calculateDimension(pc, (VideoInput[])sources.toArray(new VideoInput[sources.size()]), width, strWidth, height, strHeight); 511 512 } 513 514 private String toPath(Resource res) { 515 if(!(res instanceof FileResource)) return res.getAbsolutePath(); 516 517 //Config config=pageContext.getConfig(); 518 PageSource ps = pageContext.toPageSource(res,null); 519 if(ps==null) return res.getAbsolutePath(); 520 521 String relPath = ps.getRealpath(); 522 relPath=relPath.replace('\\', '/'); 523 if(relPath.endsWith("/"))relPath=relPath.substring(0,relPath.length()-1); 524 525 //print.out("real:"+relPath); 526 String mapping=ps.getMapping().getVirtual(); 527 mapping=mapping.replace('\\', '/'); 528 if(mapping.endsWith("/"))mapping=mapping.substring(0,mapping.length()-1); 529 530 return mapping+relPath; 531 532 533 } 534 535 536 537 private void write(StringBuffer sb, String string) { 538 sb.append(string); 539 } 540 541 542 543 private static String format(String prefix, Color color) { 544 return prefix+toHex(color.getRed())+toHex(color.getGreen())+toHex(color.getBlue()); 545 } 546 547 548 549 private static String toHex(int value) { 550 String str = Integer.toHexString(value); 551 if(str.length()==1) return "0".concat(str); 552 return str; 553 } 554 555 /** 556 * @param debug the debug to set 557 */ 558 public void setDebug(boolean debug) { 559 this.debug = debug; 560 } 561 562 /** 563 * @param passthrough the passthrough to set 564 */ 565 public void setPassthrough(Struct passthrough) { 566 this.passthrough = passthrough; 567 } 568 569 /** 570 * @param preview the preview to set 571 * @throws ExpressionException 572 */ 573 public void setPreview(String preview) { 574 this.preview = preview;//ResourceUtil.toResourceExisting(pageContext, preview); 575 } 576 577 /** 578 * @param allowfullscreen the allowfullscreen to set 579 */ 580 public void setAllowfullscreen(boolean allowfullscreen) { 581 this.allowfullscreen = allowfullscreen; 582 } 583 584 public void setAlign(String strAlign) throws ApplicationException { 585 if(StringUtil.isEmpty(strAlign)) return; 586 strAlign=strAlign.trim().toLowerCase(); 587 if("right".equals(strAlign)) this.align = "right"; 588 else if("center".equals(strAlign)) this.align = "center"; 589 else if("left".equals(strAlign)) this.align = "left"; 590 else 591 throw new ApplicationException("invalid value for attribute align ["+strAlign+"], valid values are [left,center,right]"); 592 } 593 594 /** 595 * @param group the group to set 596 */ 597 public void setGroup(boolean group) { 598 this.group = group; 599 } 600 public void setLinktarget(String target) { 601 this.target = target; 602 } 603 public void setTarget(String target) { 604 this.target = target; 605 } 606 public void setLinkfromdisplay(boolean linkfromdisplay) { 607 this.linkfromdisplay = linkfromdisplay; 608 } 609 610 /** 611 * @param playlistThumbnails the playlistThumbnails to set 612 */ 613 public void setPlaylistthumbnails(boolean playlistThumbnails) { 614 this.playlistThumbnails = playlistThumbnails; 615 } 616 public void setThumbnails(boolean playlistThumbnails) { 617 setPlaylistthumbnails(playlistThumbnails); 618 } 619 public void setThumbs(boolean playlistThumbnails) { 620 setPlaylistthumbnails(playlistThumbnails); 621 } 622 623 /** 624 * @param playlistSize the playlistSize to set 625 */ 626 public void setPlaylistsize(double playlistSize) throws ApplicationException { 627 if(playlistSize<=40) throw new ApplicationException("playlist size has to be a positive number, at least 41px"); 628 this.playlistSize = (int) playlistSize; 629 } 630 631 /** 632 * @param playlist the playlist to set 633 */ 634 public void setPlaylist(String strPlaylist) throws PageException { 635 strPlaylist=strPlaylist.trim().toLowerCase(); 636 if("right".equals(strPlaylist)) playlist=PLAYLIST_RIGHT; 637 else if("bottom".equals(strPlaylist)) playlist=PLAYLIST_BOTTOM; 638 else if("none".equals(strPlaylist)) playlist=PLAYLIST_NONE; 639 else if(Decision.isBoolean(strPlaylist)) { 640 playlist=Caster.toBooleanValue(strPlaylist)?PLAYLIST_BOTTOM:PLAYLIST_NONE; 641 } 642 else throw new ApplicationException("invalid playlist definition ["+strPlaylist+"], valid values are [right,bottom,none]"); 643 } 644 645 /** 646 * @param overstretch the overstretch to set 647 */ 648 public void setOverstretch(String overstretch) throws PageException { 649 overstretch=overstretch.trim().toLowerCase(); 650 if("fit".equals(overstretch)) overstretch="fit"; 651 else if("none".equals(overstretch)) overstretch="none"; 652 else if("proportion".equals(overstretch)) overstretch="true"; 653 else if(Decision.isBoolean(overstretch)) { 654 overstretch=Caster.toString(Caster.toBooleanValue(overstretch)); 655 } 656 else throw new ApplicationException("invalid overstretch definition ["+overstretch+"], valid values are [fit,none,true,false]"); 657 658 this.overstretch = overstretch; 659 } 660 661 /** 662 * @param download the download to set 663 */ 664 public void setDownload(boolean download) { 665 this.download = download; 666 } 667 668}