def _to_xml(self): # Check that the object has valid attributes set and raise an exception if they are # None or an empty string. # # All objects require either a URL to be set, so the framework can automatically generate # unique identifiers (rating_key) and metadata URLs (key). If the URL is not provided, # developers must set the key and rating_key attributes manually and provide a valid # callback for full metadata objects. # empty = ('', None) # If the metadata object has a 'file' attribute, synthesize a MediaObject and PartObject for it. if hasattr(self, 'file') and self.file: # Only allow agents to do this. assert self._core.plugin_class == 'Agent' def check(attr): if getattr(self, attr, None): raise AttributeError("Conflict with 'file' attribute", attr) check('url') check('key') check('rating_key') if len(self.items): raise AttributeError("The 'file' attribute cannot be provided for objects with a media & part hierarchy.") self.add(self._sandbox.environment[MediaObject.__name__]( parts=[self._sandbox.environment[PartObject.__name__](file=self.file)] )) self.file = None has_part_with_file = True else: has_part_with_file = len(self.items) and len(self.items[0].parts) and self.items[0].parts[0].file if self._template.require_key_and_rating_key: if ((hasattr(self, 'url') and self.url not in empty) or \ (hasattr(self, 'key') and self.key not in empty and hasattr(self, 'rating_key') and self.rating_key not in empty)) == False and not \ (Framework.code.context.flags.indirect in self._context.flags or self._context.request.uri.startswith('/:/plugins/')): if not has_part_with_file: raise Framework.exceptions.AttributeException('If no URL is provided, the key and rating_key attributes must be set.') # When sending an indirect response, check for an empty key attribute and try to set it. # Missing keys make the LGTV unhappy. if Framework.code.context.flags.indirect in self._context.flags and self.key in empty and len(self.items) > 0 and len(self.items[0].parts) > 0: self.key = self.items[0].parts[0].key # If no source icon is defined, use a hosted resource with the current sandbox's identifier. if self.source_icon == None and not (hasattr(self._template, 'suppress_source_icon') and self._template.suppress_source_icon): self.source_icon = self._core.runtime.hosted_resource_url('image', 'source', self._sandbox.identifier) el = Framework.modelling.objects.ModelInterfaceObject._to_xml(self) # If the URL attribute is defined, add some URL Service magic where required if hasattr(self, 'url') and self.url != None: url = self._attributes['url'] lookup_key = self._core.services.lookup_url_for_media_url(url, syncable = Framework.code.context.flags.syncable in self._core.sandbox.context.flags) # If a key isn't set, synthesize one using the URL service lookup method if hasattr(self, 'key') and self.key == None: el.set('key', lookup_key) # If a ratingKey isn't set, use the URL if hasattr(self, 'rating_key') == False or (hasattr(self, 'rating_key') and self.rating_key == None): el.set('ratingKey', self.url) # If there are no child objects, and this class expects children, add them by calling the URL service if len(self._objects) == 0 and len(type(self)._child_types) > 0: # If the media objects function flagged as deferred, set the deferred attribute on the metadata object service = self._core.services.service_for_url(url) if self._core.services.function_in_service_is_deferred(Framework.components.services.MEDIA_OBJECTS_FUNCTION_NAME, service): self.deferred = True # If the item is deferred, add a synthetic item for older clients if self.deferred: el.set('deferred', '1') self._append_children( el, [self._sandbox.environment[MediaObject.__name__]( parts = [ self._sandbox.environment[PartObject.__name__](key = indirect_callback_string(lookup_key + "&indirect=1&limit=1")) ] )] ) # If the item is not deferred, add its children normally else: items = self._core.services.media_objects_for_url(url, allow_deferred=True, metadata_class=type(self)) if items: self._append_children(el, items) elif has_part_with_file: part = self.items[0].parts[0] file_url = urlparse.urljoin('file:', urllib.pathname2url(part.file)) el.set('ratingKey', file_url) # If this object supports setting HTTP headers, it's a non-media object - apply HTTP headers and POST callback info if 'http_headers' in self._attribute_list: ObjectWithHTTPHeaders._apply_http_headers(self, el) ObjectWithPOSTCallback._apply_post_url_and_headers(self.key, el) return el
def _to_xml(self): if len(self.parts) > 0: part = self.parts[0] if isinstance(part.key, WebkitURL): if self.protocol == None: self.protocol = 'webkit' elif isinstance(part.key, HLSURL): if self.protocol == None: self.protocol = 'hls' if self.audio_codec == None: self.audio_codec = 'aac' if self.video_codec == None: self.video_codec = 'h264' if self.container == None: self.container = 'mpegts' elif isinstance(part.key, RTMPURL): if self.protocol == None: self.protocol = 'rtmp' elif isinstance(part.key, EmbedURL): if self.protocol == None: self.protocol = 'embed' # Convert resolution to width/height if not already set. if self.video_resolution and self.width == None and self.height == None: height = 360 if self.video_resolution == 'sd' else int(self.video_resolution) aspect_ratio = float(self.aspect_ratio) if self.aspect_ratio != None and float(self.aspect_ratio) > 0 else float(16) / float(9) self.height = height self.width = int(8 * round((self.height * aspect_ratio)/8)) # Copy old-style attributes to parts and create stream objects if none exist. for part in self.parts: if part.container == None and self.container != None: part.container = self.container if part.optimized_for_streaming == None and self.optimized_for_streaming != None: part.optimized_for_streaming = self.optimized_for_streaming if part.duration == None and self.duration != None: part.duration = self.duration if len(self.parts) == 1: if part.duration == None and self.duration != None: part.duration = self.duration else: self._warn("Media object has multiple parts - unable to synthesize duration.") if len(part.streams) == 0: self._warn("Media part has no streams - attempting to synthesize") bitrate = int(self.bitrate) if self.bitrate is not None else None if 'VideoStreamObject' in self._sandbox.environment: video_stream = self._sandbox.environment['VideoStreamObject']( width = self.width, height = self.height, codec = self.video_codec, ) if len(self.parts) == 1: video_stream.duration = self.duration if bitrate > 256: video_stream.bitrate = bitrate - 256 part.add(video_stream) if 'AudioStreamObject' in self._sandbox.environment: audio_stream = self._sandbox.environment['AudioStreamObject']( codec = self.audio_codec, duration = self.duration, channels = self.audio_channels, ) if len(self.parts) == 1: audio_stream.duration = self.duration if bitrate > 256: audio_stream.bitrate = 256 part.add(audio_stream) # Add media info to the URL for indirect callbacks. if isinstance(part.key, indirect_callback_string): media_info = dict( bitrate = self.bitrate, aspect_ratio = self.aspect_ratio, audio_channels = self.audio_channels, audio_codec = self.audio_codec, video_codec = self.video_codec, video_resolution = self.video_resolution, container = self.container, video_frame_rate = self.video_frame_rate, duration = self.duration, width = self.width, height = self.height, protocol = self.protocol, optimized_for_streaming = self.optimized_for_streaming ) # Rewrite the key to include media info, making sure we copy POST properties. new_key = indirect_callback_string(part.key + ('&' if '?' in part.key else '?') + 'mediaInfo=' + urllib.quote(self._core.data.json.to_string(media_info))) new_key.post_url = part.key.post_url new_key.post_headers = part.key.post_headers part.key = new_key # If this part has stream info added, make sure that one of each type is flagged as selected. all_video_streams = [stream for stream in part.streams if isinstance(stream, VideoStreamObject)] all_audio_streams = [stream for stream in part.streams if isinstance(stream, AudioStreamObject)] for stream_list in (all_video_streams, all_audio_streams): if len(stream_list) > 0: selected_count = len([stream for stream in stream_list if stream.selected is True]) if selected_count == 0: stream_list[0].selected = True elif selected_count > 1: self._core.log.warn("Part with key '%s' has multiple streams of the same type that are marked as selected.", part.key) el = Framework.modelling.objects.Container._to_xml(self) # If the first media part is an IndirectFunction instance, flag it as 'indirect' if isinstance(self._objects[0].key, indirect_callback_string): el.set('indirect', '1') return el