Source code for src.constraintSets.daptConstraints

# SPDX-FileCopyrightText: Copyright © 2026 BBC
#
# SPDX-License-Identifier: BSD-3-Clause

from .constraintSet import ConstraintSet
from src.preParseChecks.preParseCheck import BadEncodingCheck, NullByteCheck, \
    ByteOrderMarkCheck
from src.preParseChecks.xmlStructureCheck import XmlStructureCheck
from src.schemas.daptSchema import DAPTSchema
from src.xmlChecks.xsdValidator import xsdValidator
from src.xmlChecks.ttXmlCheck import timeBaseCheck, \
    ttTagAndNamespaceCheck, contentProfilesCheck
from src.xmlChecks.xmlIdCheck import unqualifiedIdAttributeCheck, \
    duplicateXmlIdCheck, IDREFSelementApplicabilityCheck
from src.xmlChecks.headXmlCheck import headCheck
from src.xmlChecks.copyrightCheck import copyrightCheck
from src.xmlChecks.actorRefsCheck import actorRefsCheck
from src.xmlChecks.bodyXmlCheck import bodyCheck
from src.xmlChecks.daptLangCheck import daptLangAudioNonMatchingCheck, \
    nonEmptyLangRootCheck
from src.xmlChecks.daptmDescTypeCheck import daptmDescTypeCheck
from src.xmlChecks.daptmRepresentsCheck import daptmRepresentsCheck
from src.xmlChecks.daptTimingXmlCheck import daptTimingCheck
from src.xmlChecks.ttmlRoleCheck import ttmlRoleTypeCheck
from src.xmlChecks.pruner import Pruner
from src.xmlChecks.daptUtils import ns_daptm, ns_dapt_extension
from src.xmlChecks.ttmlUtils import ns_ttml, ns_ttp, ns_ttm, ns_tta, ns_tt_feature
from src.xmlUtils import ns_xml
from src.validationLogging.validationCodes import ValidationCode
from src.validationLogging.validationLogger import ValidationLogger
from src.validationLogging.validationSummariser import \
    XmlPassChecker, TtmlPassChecker, DaptPassChecker

recognised_namespaces = set([
    ns_xml,  # xml
    ns_ttml,  # tt
    ns_ttp,  # ttp
    ns_tta,  # tta
    ns_ttm,  # ttm
    ns_tt_feature,
    ns_daptm,  # daptm
    ns_dapt_extension,
    'urn:ebu:tt:metadata',  # ebuttm
])

# We will not prune attributes in no namespace if they are
# defined on any element in TTML or DAPT, even if they are
# not defined on the specific element on which they occur.
known_no_ns_attributes = set([
    'agent',
    'animate',
    'begin',
    'calcMode',
    'clipBegin',
    'clipEnd',
    'condition',
    'dur',
    'encoding',
    'end',
    'family',
    'fill',
    'format',
    'keySplines',
    'keyTimes',
    'length',
    'name',
    'range',
    'region',
    'repeatCount',
    'src',
    'style',
    'timeContainer',
    'type',
    'weight',
])


[docs] class DaptConstraintSet(ConstraintSet): _preParseChecks = [ ByteOrderMarkCheck(), # encoding check will remove BOM BadEncodingCheck(), # check encoding before null bytes NullByteCheck(), XmlStructureCheck() ] _xmlChecks = [ duplicateXmlIdCheck(), Pruner( no_prune_namespaces=recognised_namespaces, no_prune_no_namespace_attributes=known_no_ns_attributes), xsdValidator(xml_schema=DAPTSchema, schema_name='DAPT'), unqualifiedIdAttributeCheck(), IDREFSelementApplicabilityCheck(), ttTagAndNamespaceCheck(), nonEmptyLangRootCheck(), daptLangAudioNonMatchingCheck(), timeBaseCheck( timeBase_acceptlist=['media'], timeBase_required=False), contentProfilesCheck( contentProfiles_atleastonelist=[ 'http://www.w3.org/ns/ttml/profile/dapt1.0/content', ], contentProfiles_denylist=[], contentProfiles_required=True ), headCheck( sub_checks=[ copyrightCheck(copyright_required=False), actorRefsCheck(), ]), daptmDescTypeCheck(), daptmRepresentsCheck(), ttmlRoleTypeCheck(), # bodyCheck( # sub_checks=[ # ]), ] # Note that daptTimingCheck is appended in init method def __init__( self, epoch: float = 0.0, segment_dur: float | None = None, segment_relative_timing: bool = False) -> None: super().__init__() self._xmlChecks.append( daptTimingCheck( epoch=epoch, segment_dur=segment_dur, segment_relative_timing=segment_relative_timing) )
[docs] @staticmethod def summarise(validation_results: ValidationLogger) -> tuple[int, int]: xmlFails, xmlWarns, xmlSkips = \ XmlPassChecker.failuresAndWarningsAndSkips(validation_results) if xmlSkips == 0 and xmlFails == 0: validation_results.good( location='Document', message='Document appears to be valid XML with {} ' 'XML-related warnings'.format(xmlWarns), code=ValidationCode.xml_document_validity ) elif xmlSkips == 0: validation_results.error( location='Document', message='Document is not valid XML with {} ' 'XML-related failures and ' '{} warnings'.format(xmlFails, xmlWarns), code=ValidationCode.xml_document_validity ) else: validation_results.skip( location='Document', message='{} XML checks skipped, ' 'document {} with ' '{} XML-related failures and ' '{} warnings'.format( xmlSkips, 'validity as XML unclear' if xmlFails == 0 else 'is not valid XML', xmlFails, xmlWarns), code=ValidationCode.xml_document_validity ) ttmlFails, ttmlWarns, ttmlSkips = \ TtmlPassChecker.failuresAndWarningsAndSkips(validation_results) if ttmlSkips == 0 and ttmlFails == 0: validation_results.good( location='Document', message='Document appears to be valid TTML with {} ' 'TTML-related warnings'.format(xmlWarns), code=ValidationCode.ttml_document_validity ) elif ttmlSkips == 0: validation_results.error( location='Document', message='Document is not valid TTML with {} ' 'TTML-related failures and ' '{} warnings'.format(ttmlFails, ttmlWarns), code=ValidationCode.ttml_document_validity ) else: validation_results.skip( location='Document', message='{} TTML checks skipped, ' 'document {} with ' '{} TTML-related failures and ' '{} warnings'.format( ttmlSkips, 'validity as TTML unclear' if ttmlFails == 0 else 'is not valid TTML', ttmlFails, ttmlWarns), code=ValidationCode.ttml_document_validity ) daptFails, daptWarns, daptSkips = \ DaptPassChecker.failuresAndWarningsAndSkips(validation_results) if daptSkips == 0 and daptFails == 0: validation_results.good( location='Document', message='Document appears to be valid DAPT with {} ' 'DAPT-related warnings'.format(daptWarns), code=ValidationCode.dapt_document_validity ) elif daptSkips == 0: validation_results.error( location='Document', message='Document is not valid DAPT with {} ' 'DAPT-related failures and ' '{} warnings'.format(daptFails, daptWarns), code=ValidationCode.dapt_document_validity ) else: validation_results.skip( location='Document', message='{} DAPT checks skipped, ' 'document {} with ' '{} DAPT-related failures and ' '{} warnings'.format( daptSkips, 'validity as DAPT unclear' if daptFails == 0 else 'is not valid DAPT', daptFails, daptWarns), code=ValidationCode.ebuttd_document_validity ) totalFails = xmlFails + ttmlFails + daptFails totalSkips = xmlSkips + ttmlSkips + daptSkips return totalFails, totalSkips