class AdParameters(object): """ Some ad serving systems may want to send data to the media file when first initialized. For example, the media file may use ad server data to identify the context used to display the creative, what server to talk to, or even which creative to display. The optional <AdParameters> element for the Linear creative enables this data exchange. The optional attribute xmlEncoded is available for the <AdParameters> element to identify whether the ad parameters are xmldencoded """ REQUIRED = ("data", ) CONVERTERS = ( Converter(unicode, ("data",)), Converter(bool, ("xml_encoded", )), ) data = attr.ib() xml_encoded = attr.ib() @classmethod def make(cls, data, xml_encoded=None): instance = check_and_convert( cls, args_dict=dict( data=data, xml_encoded=xml_encoded, ), ) return instance
class Ad(object): """ """ REQUIRED = ("id", ) SOME_OFS = (SomeOf(attr_names=("wrapper", "inline")), ) CONVERTERS = (Converter(unicode, ("id", )), ) CLASSES = ( ClassChecker("wrapper", Wrapper), ClassChecker("inline", Inline), ) id = attr.ib() wrapper = attr.ib() inline = attr.ib() @classmethod def make(cls, id, wrapper=None, inline=None): instance = check_and_convert( cls, args_dict=dict( id=id, wrapper=wrapper, inline=inline, ), ) return instance @classmethod def make_wrapper(cls, id, wrapper): return cls.make(id=id, wrapper=wrapper) @classmethod def make_inline(cls, id, inline): return cls.make(id=id, inline=inline)
class Wrapper(object): """ """ REQUIRED = ("ad_system", "vast_ad_tag_uri") CONVERTERS = ( Converter(unicode, ("ad_system", "ad_title", "impression", "error")), ) CLASSES = (ClassChecker("creatives", Creative, True), ) ad_system = attr.ib() vast_ad_tag_uri = attr.ib() ad_title = attr.ib() impression = attr.ib() error = attr.ib() creatives = attr.ib() @classmethod def make(cls, ad_system, vast_ad_tag_uri, ad_title=None, impression=None, error=None, creatives=None): instance = check_and_convert( cls, args_dict=dict( ad_system=ad_system, vast_ad_tag_uri=vast_ad_tag_uri, ad_title=ad_title, impression=impression, error=error, creatives=creatives, ), ) return instance
class Inline(object): """ 2.2.4 The <InLine> Element The last ad server in the ad supply chain serves an <InLine> element. Within the nested elements of an <InLine> element are all the files and URIs necessary to display the ad. 2.2.4.1 Required InLine Elements Contained directly within the <InLine> element are the following required elements: • <AdSystem>: the name of the ad server that returned the ad • <AdTitle>: the common name of the ad • <Impression>: a URI that directs the video player to a tracking resource file that the video player should request when the first frame of the ad is displayed • <Creatives>: the container for one or more <Creative> elements """ REQUIRED = ("ad_system", "ad_title", "impression", "creatives") CONVERTERS = (Converter(unicode, ("ad_system", "ad_title", "impression")), ) CLASSES = (ClassChecker("creatives", Creative, True), ) ad_system = attr.ib() ad_title = attr.ib() impression = attr.ib() creatives = attr.ib() @classmethod def make(cls, ad_system, ad_title, impression, creatives): instance = check_and_convert( cls, args_dict=dict( ad_system=ad_system, ad_title=ad_title, impression=impression, creatives=creatives, ), ) return instance
class Linear(object): """ The most common type of video advertisement trafficked in the industry is a “linear ad”, which is an ad that displays in the same area as the content but not at the same time as the content. In fact, the video player must interrupt the content before displaying a linear ad. Linear ads are often displayed right before the video content plays. This ad position is called a “predroll” position. For this reason, a linear ad is often called a “predroll.” A <Linear> element has two required child elements, the <Duration> and the <MediaFiles> element. Additionally three optional child elements are offered: <VideoClicks>, <AdParameters> and <TrackingEvents>. """ REQUIRED = ("duration", "media_files") CONVERTERS = (Converter(int, ("duration", )), ) CLASSES = ( ClassChecker("media_files", MediaFile, True), ClassChecker("tracking_events", TrackingEvent, True), ClassChecker("video_clicks", VideoClicks, False), ClassChecker("ad_parameters", AdParameters, False), ) VALIDATORS = (validators.make_greater_then_validator("duration", 0, False), ) duration = attr.ib() media_files = attr.ib() video_clicks = attr.ib() ad_parameters = attr.ib() tracking_events = attr.ib() @classmethod def make(cls, duration, media_files, video_clicks=None, ad_parameters=None, tracking_events=None): instance = check_and_convert( cls, args_dict=dict( duration=duration, media_files=media_files, video_clicks=video_clicks, ad_parameters=ad_parameters, tracking_events=tracking_events, ), ) validators.validate(instance) return instance def as_dict(self): from collections import OrderedDict return attr.asdict(self, dict_factory=OrderedDict, retain_collection_types=True)
class TrackingEvent(object): """ Event for user interaction with the Creative """ REQUIRED = ("tracking_event_uri", "tracking_event_type") CONVERTERS = ( Converter(unicode, ("tracking_event_uri", )), Converter(TrackingEventType, ("tracking_event_type", )), ) tracking_event_uri = attr.ib() tracking_event_type = attr.ib() @classmethod def make(cls, tracking_event_uri, tracking_event_type): instance = check_and_convert( cls, args_dict=dict( tracking_event_uri=tracking_event_uri, tracking_event_type=tracking_event_type, ), ) return instance
class StaticResource(object): REQUIRED = ("resource", "mime_type") CONVERTERS = (Converter(str, ("resource", "mime_type")), ) resource = attr.ib() mime_type = attr.ib() @classmethod def make(cls, resource, mime_type): instance = check_and_convert( cls, args_dict=dict( resource=resource, mime_type=mime_type, ), ) return instance
class UriWithId(object): REQUIRED = ("resource", ) CONVERTERS = (Converter(str, ("resource", "id")), ) resource = attr.ib() id = attr.ib() @classmethod def make(cls, resource, id=None): instance = check_and_convert( cls, args_dict=dict( resource=resource, id=id, ), ) return instance
class VideoClicks(object): """ A container for URI elements, for when a user interacts with the video """ CONVERTERS = (Converter( str, ("click_through", "click_tracking", "custom_click")), ) click_through = attr.ib() click_tracking = attr.ib() custom_click = attr.ib() @classmethod def make(cls, click_through=None, click_tracking=None, custom_click=None): instance = check_and_convert( cls, args_dict=dict( click_through=click_through, click_tracking=click_tracking, custom_click=custom_click, ), ) return instance
class Creative(object): """ A creative in VAST is a file that is part of a VAST ad. Multiple creative may be provided in the form of Linear, NonLinear, or Companions. Multiple creative of the same kind may also be provided in different technical formats so that the file most suited to the user’s device can be displayed (only the creative best suited to the technology/device would be used in this case). Despite how many or what type of creative are included as part of the Ad, all creative files should generally represent the same creative concept. Within the <InLine> element is one <Creatives> element. The <Creatives> element provides details about the files for each creative to be included as part of the ad experience. Multiple <Creative> may be nested within the <Creatives> element. Note the plural spelling of the primary element <Creatives> and the singular spelling of the nested element <Creative>. Each nested <Creative> element contains one of: <Linear>, <NonLinear> or <CompanionAds>. The following attributes are available for the <Creative> element: • id: an ad server-defined identifier for the creative • sequence: the numerical order in which each sequenced creative should display (not to be confused with the <Ad> sequence attribute used to define Ad Pods) • adId: identifies the ad with which the creative is served • apiFramework: the technology used for any included API All creative attributes are optional. """ SOME_OFS = ( SomeOf(attr_names=("linear", "non_linear")), ) CONVERTERS = ( Converter(unicode, ("id", "ad_id")), Converter(ApiFramework, ("api_framework",)), Converter(int, ("sequence",)) ) CLASSES = ( ClassChecker("linear", Linear), ClassChecker("non_linear", NonLinear), ) VALIDATORS = ( validators.make_greater_then_validator("sequence", -1), ) linear = attr.ib() non_linear = attr.ib() companion = attr.ib() id = attr.ib() sequence = attr.ib() ad_id = attr.ib() api_framework = attr.ib() @classmethod def make(cls, linear=None, non_linear=None, companion=None, id=None, sequence=None, ad_id=None, api_framework=None, ): instance = check_and_convert( cls, args_dict=dict( linear=linear, non_linear=non_linear, companion=companion, id=id, sequence=sequence, ad_id=ad_id, api_framework=api_framework, ), ) validators.validate(instance, cls.VALIDATORS) return instance
class CompanionAd(object): """ Commonly text, display ads, rich media, or skins that wrap around the video experience. These ads come in a number of sizes and shapes and typically run alongside or surrounding the video player """ REQUIRED = ("width", "height") CONVERTERS = ( Converter(unicode, ("iframe_resource", "html_resource", "id", "alt_text", "companion_click_through")), Converter(int, ("width", "height", "expanded_width", "expanded_height")), Converter(ApiFramework, ("api_framework", )), ) CLASSES = ( ClassChecker("static_resource", StaticResource), ClassChecker("ad_parameters", AdParameters), ClassChecker("api_framework", ApiFramework), ClassChecker("tracking_events", TrackingEvent, True), ) SOME_OF = ( SomeOf(attr_names=("iframe_resource", "html_resource", "static_resource"), up_to=3), ) width = attr.ib() height = attr.ib() expanded_width = attr.ib() expanded_height = attr.ib() api_framework = attr.ib() id = attr.ib() static_resource = attr.ib() iframe_resource = attr.ib() html_resource = attr.ib() companion_click_through = attr.ib() ad_parameters = attr.ib() alt_text = attr.ib() tracking_events = attr.ib() @classmethod def make( cls, width, height, expanded_width=None, expanded_height=None, api_framework=None, id=None, static_resource=None, iframe_resource=None, html_resource=None, companion_click_through=None, ad_parameters=None, alt_text=None, tracking_events=None, ): instance = check_and_convert( cls, args_dict=dict( width=width, height=height, expanded_width=expanded_width, expanded_height=expanded_height, api_framework=api_framework, id=id, static_resource=static_resource, iframe_resource=iframe_resource, html_resource=html_resource, companion_click_through=companion_click_through, ad_parameters=ad_parameters, alt_text=alt_text, tracking_events=tracking_events, ), ) return instance
class NonLinearAd(object): REQUIRED = ("width", "height") CONVERTERS = ( Converter(unicode, ("iframe_resource", "html_resource", "id")), Converter(int, ("width", "height", "expanded_width", "expanded_height", "min_suggested_duration")), Converter(bool, ("scalable", "maintain_aspect_ratio")), Converter(ApiFramework, ("api_framework", )), ) CLASSES = ( ClassChecker("static_resource", StaticResource), ClassChecker("ad_parameters", AdParameters), ClassChecker("api_framework", ApiFramework), ClassChecker("non_linear_click_through", UriWithId) ) SOME_OFS = ( SomeOf(attr_names=("iframe_resource", "html_resource", "static_resource"), up_to=3), ) width = attr.ib() height = attr.ib() expanded_width = attr.ib() expanded_height = attr.ib() scalable = attr.ib() maintain_aspect_ratio = attr.ib() min_suggested_duration = attr.ib() api_framework = attr.ib() id = attr.ib() static_resource = attr.ib() iframe_resource = attr.ib() html_resource = attr.ib() non_linear_click_through = attr.ib() ad_parameters = attr.ib() @classmethod def make( cls, width, height, expanded_width=None, expanded_height=None, scalable=None, maintain_aspect_ratio=None, min_suggested_duration=None, api_framework=None, id=None, static_resource=None, iframe_resource=None, html_resource=None, non_linear_click_through=None, ad_parameters=None, ): instance = check_and_convert( cls, args_dict=dict( width=width, height=height, expanded_width=expanded_width, expanded_height=expanded_height, scalable=scalable, maintain_aspect_ratio=maintain_aspect_ratio, min_suggested_duration=min_suggested_duration, api_framework=api_framework, id=id, static_resource=static_resource, iframe_resource=iframe_resource, html_resource=html_resource, non_linear_click_through=non_linear_click_through, ad_parameters=ad_parameters, ), ) return instance
class MediaFile(object): """ 2.3.1.4 Media File Attributes """ REQUIRED = ("asset", "delivery", "type", "width", "height") CONVERTERS = ( Converter(type=unicode, attr_names=("asset", "codec", "id")), Converter(type=int, attr_names=("width", "height", "bitrate", "min_bitrate", "max_bitrate")), Converter(type=bool, attr_names=("scalable", "maintain_aspect_ratio")), Converter(type=MimeType, attr_names=("type", )), Converter(type=ApiFramework, attr_names=("api_framework",)), Converter(type=Delivery, attr_names=("delivery", )) ) VALIDATORS = ( validators.make_greater_then_validator("height", 0), validators.make_greater_then_validator("width", 0), ) asset = attr.ib() delivery = attr.ib() type = attr.ib() width = attr.ib() height = attr.ib() codec = attr.ib() id = attr.ib() bitrate = attr.ib() min_bitrate = attr.ib() max_bitrate = attr.ib() scalable = attr.ib() maintain_aspect_ratio = attr.ib() api_framework = attr.ib() @classmethod def make( cls, asset, delivery, type, width, height, codec=None, id=None, bitrate=None, min_bitrate=None, max_bitrate=None, scalable=None, maintain_aspect_ratio=None, api_framework=None, ): """ Entry point for making MediaFile instances. :param asset: the url to the asset :param delivery: either “progressive” for progressive download protocols (such as HTTP) or “streaming” for streaming protocols. :param type: MIME type for the file container. Popular MIME types include, but are not limited to “video/x- flv” for Flash Video and “video/mp4” for MP4 :param width: the native width of the video file, in pixels :param height: the native height of the video file, in pixels :param codec: the codec used to encode the file which can take values as specified by RFC 4281: http://tools.ietf.org/html/rfc4281 :param id: an identifier for the media file :param bitrate: for progressive load video, the bitrate value specifies the average bitrate for the media file; :param min_bitrate: used in conjunction with max_bitrate for streaming videos :param max_bitrate: used in conjunction with min_bitrate for streaming videos :param scalable: identifies whether the media file is meant to scale to larger dimensions :param maintain_aspect_ratio: identifies whether aspect ratio for media file is maintained :param api_framework: identifies the API needed to execute an interactive media file :return: """ instance = check_and_convert( cls, args_dict=dict( asset=asset, delivery=delivery, type=type, width=width, height=height, codec=codec, id=id, bitrate=bitrate, min_bitrate=min_bitrate, max_bitrate=max_bitrate, scalable=scalable, maintain_aspect_ratio=maintain_aspect_ratio, api_framework=api_framework, ), ) if instance.type in (MimeType.FLASH, MimeType.JS): vs = list(cls.VALIDATORS) elif instance.delivery == Delivery.PROGRESSIVE: vs = list(cls.VALIDATORS) + [cls._validate_bitrate] else: vs = list(cls.VALIDATORS) + [cls._validate_min_max_bitrate] validators.validate(instance, vs) return instance @staticmethod def _validate_bitrate(instance): if instance.bitrate is None: return "media file bitrate cannot be None for progressive media" if instance.bitrate < 0: return "media file bitrate must be > 0 but was %s" % instance.bitrate @staticmethod def _validate_min_max_bitrate(instance): errors = [] if instance.min_bitrate is None: errors.append("media file min_bitrate cannot be None for streaming media") if instance.max_bitrate is None: errors.append("media file min_bitrate cannot be None for streaming media") if not errors and instance.min_bitrate > instance.max_bitrate: msg = "media file min_bitrate={min_bitrate} is greater than max_bitrate={max_bitrate}" errors.append(msg.format(min_bitrate=instance.min_bitrate, max_bitrate=instance.max_bitrate)) return ",".join(errors) or None