예제 #1
0
    def get_event(self, event_type: str, xml: bytes):
        self.event = self._parse_event(event_type, xml)
        media_type = self._get_media_type()
        metadata = self._parse_metadata(media_type)

        timestamp = self._get_xpath_from_event("./vrt:timestamp")

        if event_type == "getMetadataResponse":
            correlation_id = self._get_xpath_from_event("./vrt:correlationId")
            status = self._get_xpath_from_event("./vrt:status")

            if status == "SUCCESS":
                return GetMetadataResponseEvent(event_type, metadata,
                                                timestamp, correlation_id,
                                                status, media_type)
            else:
                # TODO: report back to VRT
                raise InvalidEventException(
                    f"getMetadataResponse status wasn't 'SUCCES': {status}")

        if event_type == "metadataUpdatedEvent":
            media_id = self._get_xpath_from_event("./vrt:mediaId")

            return MetadataUpdatedEvent(event_type, metadata, timestamp,
                                        media_id, media_type)

        raise InvalidEventException(f"Can't handle '{event_type}' events")
예제 #2
0
    def _parse_event(self, event_type: str, xml: bytes):
        """Parse the input XML to a DOM"""
        try:
            tree = etree.parse(BytesIO(xml.strip()))
        except etree.XMLSyntaxError:
            raise InvalidEventException("Event is not valid XML.")

        try:
            return tree.xpath(f"/vrt:{event_type}", namespaces=NAMESPACES)[0]
        except IndexError:
            raise InvalidEventException(f"Event is not a '{event_type}'.")
예제 #3
0
    def __timecode_to_frames(self, timecode: str, framerate: int) -> int:
        try:
            hours, minutes, seconds, frames = [
                int(part) for part in timecode.split(":")
            ]
        except (TypeError, AttributeError, ValueError) as error:
            raise InvalidEventException(f"Invalid timecode in the event.",
                                        timecode=timecode)

        return (hours * 3600 + minutes * 60 + seconds) * framerate + frames
예제 #4
0
    def _validate_metadata(self):
        if bool(self.soc) ^ bool(self.eoc):
            raise InvalidEventException(
                f"Only SOC or EOC is present. They should both be present or none at all."
            )

        duration_frames = self.__timecode_to_frames(self.duration,
                                                    self.framerate)
        som_frames = self.__timecode_to_frames(self.som, self.framerate)
        soc_frames = (self.__timecode_to_frames(self.soc, self.framerate)
                      if self.soc else som_frames)
        eoc_frames = (self.__timecode_to_frames(self.eoc, self.framerate)
                      if self.eoc else som_frames + duration_frames)
        eom_frames = (self.__timecode_to_frames(self.eom, self.framerate)
                      if self.eom else eoc_frames)

        timecodes = [som_frames, soc_frames, eoc_frames, eom_frames]

        if timecodes != sorted(timecodes):
            raise InvalidEventException(
                f"Something is wrong with the SOM, SOC, EOC, EOM order.")
예제 #5
0
 def _get_xpath_from_event(self,
                           xpath,
                           xml=False,
                           optional: bool = False) -> str:
     try:
         if xml:
             return etree.tostring(
                 self.event.xpath(xpath,
                                  namespaces=NAMESPACES)[0]).decode("utf-8")
         else:
             return self.event.xpath(xpath, namespaces=NAMESPACES)[0].text
     except IndexError:
         if optional:
             return ""
         else:
             raise InvalidEventException(
                 f"'{xpath}' is not present in the event.")
예제 #6
0
    def _calculate_resolution_xpath(self) -> str:
        """ Calculates the XPATH for the resolution information.

        It will use hires information if it is available. Otherwise, use lores information.

        Returns:
            The XPATH for the resolution information.

        Raises:
            InvalidEventException: If no hires and no lores are available.
        """
        resolutions = ("hires", "lores")
        xpath_resolutions = [
            f"//ebu:format[@formatDefinition='current'][./ebu:videoFormat[@videoFormatDefinition='{res}']]"
            for res in resolutions
        ]
        for xpath_resolution in xpath_resolutions:
            if self.event.xpath(xpath_resolution, namespaces=NAMESPACES):
                return xpath_resolution
        raise InvalidEventException("No hires/lores information available.")
예제 #7
0
    def _get_media_type(self) -> str:
        is_video = bool(
            self._get_xpath_from_event(
                f"//ebu:format[@formatDefinition='current']/ebu:videoFormat",
                xml=True,
                optional=True,
            ))
        is_audio = bool(
            self._get_xpath_from_event(
                f"//ebu:format[@formatDefinition='current']/ebu:audioFormat",
                xml=True,
                optional=True,
            ))

        if is_video:
            return "video"
        if is_audio:
            return "audio"

        raise InvalidEventException("Unknown media type.")