1 10 package org.mmbase.applications.media.builders; 11 12 import org.mmbase.applications.media.filters.MainFilter; 13 import org.mmbase.applications.media.urlcomposers.URLComposer; 14 import org.mmbase.applications.media.cache.URLCache; 15 16 import java.util.*; 17 18 import org.mmbase.module.core.*; 19 import org.mmbase.module.corebuilders.InsRel; 20 import org.mmbase.util.*; 21 import org.mmbase.util.functions.*; 22 import org.mmbase.util.logging.Logger; 23 import org.mmbase.util.logging.Logging; 24 25 26 39 40 public class MediaFragments extends MMObjectBuilder { 41 42 private static Logger log = Logging.getLoggerInstance(MediaFragments.class); 43 44 public static final String FUNCTION_URLS = "urls"; 46 public static final String FUNCTION_FILTEREDURLS = "filteredurls"; 47 public static final String FUNCTION_URL = "url"; 48 public static final String FUNCTION_NUDEURL = "nudeurl"; 49 public static final String FUNCTION_PARENTS = "parents"; 50 public static final String FUNCTION_ROOT = "root"; 51 public static final String FUNCTION_SUBFRAGMENT = "issubfragment"; 52 public static final String FUNCTION_SUBFRAGMENTS = "subfragments"; 53 public static final String FUNCTION_AVAILABLE = "available"; 54 public static final String FUNCTION_FORMAT = "format"; 55 public static final String FUNCTION_DURATION = "duration"; 56 57 public final static Parameter[] URLS_PARAMETERS = { new Parameter("format", List.class), new Parameter("bitrate", String .class), Parameter.REQUEST }; 59 public final static Parameter[] FILTEREDURLS_PARAMETERS = URLS_PARAMETERS; 60 public final static Parameter[] URL_PARAMETERS = URLS_PARAMETERS; 61 public final static Parameter[] NUDEURL_PARAMETERS = URLS_PARAMETERS; 62 public final static Parameter[] PARENTS_PARAMETERS = {}; 63 public final static Parameter[] ROOT_PARAMETERS = {}; 64 public final static Parameter[] ISSUBFRAGMENT_PARAMETERS = {}; 65 public final static Parameter[] SUBFRAGMENT_SPARAMETERS = {}; 66 public final static Parameter[] AVAILABLE_PARAMETERS = URLS_PARAMETERS; 67 public final static Parameter[] FORMAT_PARAMETERS = URLS_PARAMETERS; 68 public final static Parameter[] DURATION_PARAMETERS = {}; 69 70 71 74 private static boolean initDone = false; 77 78 private URLCache cache = URLCache.getCache(); 79 80 public boolean init() { 81 if(initDone) { 82 return super.init(); 83 } 84 log.service("Init of Media Fragments builder"); 85 initDone = true; 87 boolean result = super.init(); 88 retrieveClassificationInfo(); 90 91 return result; 92 } 93 94 98 static protected Map translateURLArguments(List arguments, Map info) { 99 if (info == null) info = new HashMap(); 100 if (arguments != null) { 101 if (arguments instanceof Parameters) { 102 info.putAll(((Parameters) arguments).toMap()); 103 } else { 104 info.put("format", arguments); 105 } 106 } 107 return info; 108 } 109 110 113 protected Object executeFunction(MMObjectNode node, String function, List args) { 114 if (log.isDebugEnabled()) { 115 log.debug("executeFunction " + function + "(" + args + ") on " + node); 116 } 117 if (function.equals("info")) { 118 List empty = new Vector(); 119 java.util.Map info = (java.util.Map ) super.executeFunction(node, function, empty); 120 info.put(FUNCTION_URL, "(<format>) Returns the 'best' url for this fragment. Hashtable can be filled with speed/channel/ or other info to evalute the url."); 121 info.put(FUNCTION_URLS, "(info) A list of all possible URLs to this fragment (Really URLComposer.URLComposer's)"); 122 info.put(FUNCTION_ROOT, "() Returns the 'parent' MMObjectNode's number of the parent or null"); 123 info.put(FUNCTION_SUBFRAGMENT, "() Wether this fragment is a subfragment (returns a Boolean)"); 124 info.put(FUNCTION_SUBFRAGMENTS, "() Returns a stack of parents (Stack of MMObjectNode)"); 125 info.put(FUNCTION_AVAILABLE, "() Wether this fragment is 'available'. A fragment can be unaivable when there is a related publishtimes which defines it 'unpublished'"); 126 info.put("gui", "(state|channels|codec|format|..) Gui representation of this object."); 128 129 if (args == null || args.size() == 0) { 130 return info; 131 } else { 132 return info.get(args.get(0)); 133 } 134 } else if (FUNCTION_URLS.equals(function)) { 135 return getURLs(node, translateURLArguments(args, null), null,null); 136 } else if (FUNCTION_FILTEREDURLS.equals(function)) { 137 return getFilteredURLs(node, translateURLArguments(args, null),null); 138 } else if (FUNCTION_SUBFRAGMENT.equals(function)) { 139 return Boolean.valueOf(isSubFragment(node)); 140 } else if (FUNCTION_ROOT.equals(function)) { 141 MMObjectNode parent = getRootFragment(node); 142 return parent; 143 } else if (FUNCTION_PARENTS.equals(function)) { 144 return getParentFragments(node); 145 } else if (FUNCTION_ROOT.equals(function)) { 146 return "" + getRootFragment(node).getNumber(); 147 } else if (FUNCTION_AVAILABLE.equals(function)) { 148 List pt = node.getRelatedNodes("publishtimes"); 149 if (pt.size() == 0) { 150 return Boolean.TRUE; 151 } else { 152 MMObjectNode publishtime = (MMObjectNode) pt.get(0); 153 int now = (int) (System.currentTimeMillis() / 1000); 154 int begin = publishtime.getIntValue("begin"); 155 int end = publishtime.getIntValue("end"); 156 Boolean available = Boolean.TRUE; 157 if (begin > 0 && now < begin) available = Boolean.FALSE; 158 if (end > 0 && now > end) available = Boolean.FALSE; 159 return available; 160 } 161 } else if (FUNCTION_URL.equals(function)) { 162 return getURL(node, translateURLArguments(args, null)); 163 } else if (FUNCTION_NUDEURL.equals(function)) { 164 Map info = translateURLArguments(args, null); 165 info.put("nude", "true"); 166 return getURL(getRootFragment(node), info); 167 } else if (FUNCTION_FORMAT.equals(function)) { 168 return getFormat(node, translateURLArguments(args, null)); 169 } else if (FUNCTION_DURATION.equals(function)) { 170 StringBuffer buf = new StringBuffer (); 171 org.mmbase.applications.media.urlcomposers.RealURLComposer.appendTime(calculateLength(node), buf); 172 return buf.toString(); 173 } 174 log.debug("Function not matched in mediafragments"); 175 return super.executeFunction(node, function, args); 176 } 177 178 179 184 protected long calculateLength(MMObjectNode node) { 185 long start = node.getLongValue("start"); 186 long stop = node.getLongValue("stop"); 187 long length = node.getLongValue("length"); 188 189 if(stop != 0) { 190 return stop - start; 191 } else if (length != 0) { 192 return length - start; 193 } 194 log.debug("length cannot be evaluated, no stoptime and no length"); 195 return 0; 196 } 197 198 203 public String getGUIIndicator(MMObjectNode node) { 204 String url = node.getFunctionValue(FUNCTION_URL, null).toString(); 205 String title = node.getStringValue("title"); 206 if ("".equals(title)) title = "***"; 207 if (! "".equals(url)) { 208 if (url.startsWith("/")) { 209 url = MMBaseContext.getHtmlRootUrlPath() + url.substring(1); 210 } 211 return "<a HREF=\"" + url + "\" alt=\"\" >" + title + "</a>"; 212 } else { 213 return "[" + title + "]"; 214 } 215 } 216 217 218 public String getGUIIndicator(String field, MMObjectNode node) { 219 if (getField(field).getGUIType().equals("relativetime")) { StringBuffer buf = new StringBuffer (); 221 org.mmbase.applications.media.urlcomposers.RealURLComposer.appendTime(node.getIntValue(field), buf); 222 return buf.toString(); 223 } 224 return super.getGUIIndicator(field, node); 225 } 226 227 228 234 protected List getURLs(MMObjectNode fragment, Map info, List urls, Set cacheExpireObjects) { 235 if (urls == null) urls = new ArrayList(); 236 237 Iterator i = getSources(fragment).iterator(); 238 while (i.hasNext()) { 239 MMObjectNode source = (MMObjectNode) i.next(); 240 MediaSources bul = (MediaSources) source.getBuilder(); bul.getURLs(source, fragment, info, urls, cacheExpireObjects); 242 } 243 return urls; 244 } 245 246 protected List getFilteredURLs(MMObjectNode fragment, Map info, Set cacheExpireObjects) { 247 log.debug("getfilteredurls"); 248 List urls = getURLs(fragment, info, null,cacheExpireObjects); 249 return MainFilter.getInstance().filter(urls); 250 } 251 252 253 261 protected String getURL(MMObjectNode fragment, Map info) { 262 log.debug("Getting url of a fragment."); 263 String key = URLCache.toKey(fragment, info); 264 if(cache.containsKey(key)) { 265 String url = (String ) cache.get(key); 266 if (log.isDebugEnabled()) { 267 log.debug("Cache hit, key = " + key); 268 log.debug("Resolved url = " + url); 269 } 270 return url; 271 } else { 272 log.debug("No cache hit, key = " + key); 273 } 274 275 Set cacheExpireObjects = new HashSet(); 276 List urls = getFilteredURLs(fragment, info, cacheExpireObjects); 277 String result = ""; 278 if (urls.size() > 0) { 279 result = ((URLComposer) urls.get(0)).getURL(); 280 } 281 if (log.isDebugEnabled()) { 282 log.debug("Add to cache, key = " + key); 283 log.debug("Resolved url = " + result); 284 } 285 cache.put(key, result, cacheExpireObjects); 287 return result; 288 } 289 290 protected String getFormat(MMObjectNode fragment, Map info) { 291 log.debug("Getting format of a fragment."); 292 List urls = getFilteredURLs(fragment, info, null); 295 if (urls.size() > 0) { 296 return ((URLComposer) urls.get(0)).getFormat().toString(); 297 } else { 298 return ""; } 300 } 301 302 307 public boolean isSubFragment(MMObjectNode mediafragment) { 308 int mediacount = mediafragment.getRelationCount("mediasources"); 309 return (mediacount == 0 && mediafragment.getRelationCount("mediafragments") > 0); 310 } 311 312 315 protected boolean addParentFragment(Stack fragments) { 316 MMObjectNode fragment = (MMObjectNode) fragments.peek(); 317 int role = mmb.getRelDef().getNumberByName("posrel"); 318 InsRel insrel = mmb.getRelDef().getBuilder(role); 319 Enumeration e = insrel.getRelations(fragment.getNumber(), mmb.getBuilder("mediafragments").getObjectType(), role); 320 while (e.hasMoreElements()) { 321 MMObjectNode relation = (MMObjectNode) e.nextElement(); 322 if (relation.getIntValue("dnumber") == fragment.getNumber()) { if (log.isDebugEnabled()) { 324 log.debug("Yes, found parent of " + fragment.getNumber() + " " + relation.getIntValue("snumber")); 325 } 326 MMObjectNode parent = getNode(relation.getIntValue("snumber")); 327 if (fragments.contains(parent)) { 328 log.warn("Circular fragment nesting detected " + fragments + " breaking infinite loop"); 329 return false; 330 } 331 fragments.push(parent); 332 return true; 333 } 334 } 335 return false; 336 } 337 338 343 public Stack getParentFragments(MMObjectNode fragment) { 344 Stack result = new Stack(); 345 result.push(fragment); 346 if (log.isDebugEnabled()) { 347 log.debug("Finding parents of node " + fragment.getNumber()); 348 } 349 while (addParentFragment(result)); 350 return result; 351 } 352 353 354 362 public MMObjectNode getRootFragment(MMObjectNode fragment) { 363 Stack s = getParentFragments(fragment); 364 return (MMObjectNode) s.peek(); 365 } 366 367 373 public List getSources(MMObjectNode fragment) { 374 if (log.isDebugEnabled()) log.debug("Get mediasources mediafragment " + fragment.getNumber()); 375 MMObjectNode root = getRootFragment(fragment); 376 List mediasources = root.getRelatedNodes("mediasources"); 377 if (mediasources == null) { 378 log.warn("Could not get related nodes of type mediasources"); 379 } 380 if (log.isDebugEnabled()) log.debug("Mediafragment contains "+mediasources.size()+" mediasources"); 381 382 return mediasources; 383 } 384 385 386 391 public void removeSources(MMObjectNode fragment) { 392 List ms = getSources(fragment); 393 for (Iterator mediaSources = ms.iterator() ;mediaSources.hasNext();) { 394 MMObjectNode source = (MMObjectNode) mediaSources.next(); 395 MMObjectBuilder parent = source.getBuilder(); 396 parent.removeRelations(source); 397 parent.removeNode(source); 398 } 399 } 400 401 404 private Map classification = null; 405 406 413 private void retrieveClassificationInfo() { 414 415 MMObjectBuilder lookup = mmb.getMMObject("lookup"); 416 if(lookup == null) { 417 log.debug("Downwards compatible classification code not used."); 418 return; 419 } 420 log.debug("Using downwards compatible classification code."); 421 classification = new Hashtable(); 422 MMObjectNode fn = getNode(mmb.getTypeDef().getIntValue("mediafragments")); 423 Vector nodes = fn.getRelatedNodes("lookup"); 424 for (Enumeration e = nodes.elements();e.hasMoreElements();) { 425 MMObjectNode node = (MMObjectNode)e.nextElement(); 426 String index = node.getStringValue("index"); 427 String value = node.getStringValue("value"); 428 log.debug("classification uses: " + index + " -> " + value); 429 classification.put(index,value); 430 } 431 return; 432 } 433 434 441 public String replace(PageInfo sp,StringTokenizer command) { 442 if (command.hasMoreTokens()) { 443 String token=command.nextToken(); 444 445 log.debug("scan - "+token); 446 if (token.equals("GETURL")) { 447 Integer number=null, userSpeed=null, userChannels=null; 448 if (command.hasMoreTokens()) number=new Integer (command.nextToken()); 449 if (command.hasMoreTokens()) userSpeed=new Integer (command.nextToken()); 450 if (command.hasMoreTokens()) userChannels=new Integer (command.nextToken()); 451 if (number!=null) { 452 MMObjectNode media = getNode(number.intValue()); 453 if(!media.getBuilder().isExtensionOf(mmb.getBuilder("mediafragments"))) { 454 log.error("Number "+number+" is not a media/audio/video fragment "+media); 455 return "Number "+number+" is not a media/audio/video fragment "+media; 456 } 457 Map info = new HashMap(); 458 if(userSpeed!=null) { 459 info.put("speed",""+userSpeed); 460 } 461 if(userChannels!=null) { 462 info.put("channels",""+userChannels); 463 } 464 return getURL(media, info); 465 } else { 466 log.error("No mediafragment specified"); 467 return null; 468 } 469 } 470 log.error("only command GETURL is supported"); 471 return "only command GETURL is supported"; 472 } 473 log.error("No commands defined."); 474 return "No commands defined."; 475 } 476 477 public Object getObjectValue(MMObjectNode node, String field) { 478 if (field.equals("lengthsec")) { 479 long val=node.getLongValue("length"); 480 return ""+val/1000; 481 } 482 return super.getObjectValue(node,field); 483 } 484 485 public boolean setValue(MMObjectNode node,String fieldname) { 486 if (fieldname.equals("lengthsec")) { 487 long val=node.getLongValue("lengthsec"); 488 log.info("store value in seconds: "+val); 489 node.setValue("length",new Long (val*1000)); 490 node.storeValue("lengthsec",null); 491 return false; 492 } 493 return super.setValue(node,fieldname); 494 } 495 496 501 502 public boolean equals(MMObjectNode o1, MMObjectNode o2) { 503 int n1 = o1.getNumber(); 504 int n2 = o2.getNumber(); 505 if (n1 > 0 && n2 > 0) { return n1 == n2; 507 } else { 508 String t1 = o1.getStringValue("_number"); 509 String t2 = o2.getStringValue("_number"); 510 if (t1 == null) { 511 return n1 == n2 && t2 == null; 512 } else { 513 return n1 == n2 && t1.equals(t2); 514 } 515 } 516 517 } 518 519 } 520 | Popular Tags |