You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

188 lines
8.6 KiB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
  1. #!/usr/bin/env python3
  2. ''' ADI builder
  3. author: Steven Kuterna steven.kuterna@devoteam.com
  4. '''
  5. import sys
  6. import argparse
  7. import logging
  8. import os
  9. import shutil
  10. import yaml
  11. from lxml import etree as ET
  12. import hashlib
  13. from datetime import datetime
  14. loglevel= logging.WARNING
  15. logger = logging.getLogger()
  16. logger.setLevel(loglevel)
  17. ch = logging.StreamHandler()
  18. ch.setLevel(loglevel)
  19. formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
  20. ch.setFormatter(formatter)
  21. logger.addHandler(ch)
  22. def insertMetadata(tree, config):
  23. if config['Verb'] is None: config['Verb'] = ''
  24. if config['CreateDate'] is None:
  25. now = datetime.now()
  26. date = now.strftime("%Y-%m-%d")
  27. config['CreateDate'] = date
  28. try:
  29. attributes= { 'Asset_Class': 'package',
  30. 'Asset_ID': str(config['AssetId']),
  31. 'Asset_Name': str(config['AssetName']),
  32. 'Provider': str(config['Provider']),
  33. 'Provider_ID': str(config['ProviderId']),
  34. 'Product': 'MOD',
  35. 'Description': str(config['Description']),
  36. 'Creation_Date': str(config['CreateDate']),
  37. 'Verb': str(config['Verb']),
  38. 'Version_Major': str(config['VersionMajor']),
  39. 'Version_Minor': str(config['VersionMinor'])
  40. }
  41. except KeyError as e:
  42. logger.error('failed to add Metadata AMS attribute '+str(e))
  43. sys.exit(0)
  44. ET.SubElement(tree, 'AMS', attributes)
  45. ET.SubElement(tree, 'App_Data', {'App': 'MOD', 'Name': "Metadata_Spec_Version", 'Value': "CableLabsVOD1.1"})
  46. ET.SubElement(tree, 'App_Data', {'App': 'MOD', 'Name': "Provider_Content_Tier", 'Value': "VU"})
  47. def createAMSattributes(asset_type, metadata):
  48. if metadata['Verb'] is None: metadata['Verb'] = ''
  49. try:
  50. ams_attributes= { 'Asset_Class': asset_type,
  51. 'Asset_ID': asset_type.upper() + str(metadata['AssetId']),
  52. 'Asset_Name': str(metadata['AssetName'] + ' ' + asset_type),
  53. 'Provider': str(metadata['Provider']),
  54. 'Provider_ID': str(metadata['ProviderId']),
  55. 'Product': 'MOD',
  56. 'Description': str(metadata['Description'] + ' ' + asset_type),
  57. 'Creation_Date': str(metadata['CreateDate']),
  58. 'Verb': str(metadata['Verb']),
  59. 'Version_Major': str(metadata['VersionMajor']),
  60. 'Version_Minor': str(metadata['VersionMinor'])
  61. }
  62. except KeyError as e:
  63. logger.error('failed to add Metadata AMS attribute '+str(e))
  64. sys.exit(0)
  65. return ams_attributes
  66. def addAsset(tree, asset_type, asset, metadata, output):
  67. if not 'Content' in asset:
  68. for subasset in asset:
  69. addAsset(tree, asset_type, asset[subasset], metadata, output)
  70. return
  71. logger.info(f'insert asset: {asset}')
  72. ams_attributes = createAMSattributes(asset_type, metadata)
  73. assetTree = ET.SubElement(tree, 'Asset')
  74. assetMetadata = ET.SubElement(assetTree, 'Metadata')
  75. ET.SubElement(assetMetadata, 'AMS', ams_attributes)
  76. ET.SubElement(assetMetadata, 'App_Data', {'App': 'MOD', 'Name': 'Type', 'Value': asset_type})
  77. for element in asset:
  78. if type(asset[element]) is dict:
  79. for subelement in asset[element]:
  80. if type(asset[element][subelement]) is dict:
  81. for subsubelemment in asset[element][subelement]:
  82. ET.SubElement(assetMetadata, 'App_Data', {'App': 'MOD', 'Name': subsubelemment, 'Value': str(asset[element][subelement][subsubelemment])})
  83. else:
  84. if asset[element][subelement] is None:
  85. asset[element][subelement] = ''
  86. ET.SubElement(assetMetadata, 'App_Data', {'App': 'MOD', 'Name': element, 'Value': str(asset[element][subelement])})
  87. else:
  88. # is it content? add it under the Asset level
  89. if str(element).upper() == 'CONTENT_CHECKSUM' or str(element).upper() == 'CONTENT_FILESIZE':
  90. continue
  91. if str(element).upper() == 'CONTENT':
  92. if not os.path.exists(args.input):
  93. logger.error(f"content file {asset[element]} is not found, exiting...")
  94. sys.exit(0)
  95. ET.SubElement(assetTree, 'Content', {'Value': asset[element]})
  96. checksum = hashlib.md5(open(asset[element],'rb').read()).hexdigest()
  97. filesize = os.path.getsize(asset[element])
  98. ET.SubElement(assetMetadata, 'App_Data', {'App': 'MOD', 'Name': 'Content_CheckSum', 'Value': str(checksum)})
  99. ET.SubElement(assetMetadata, 'App_Data', {'App': 'MOD', 'Name': 'Content_Filesize', 'Value': str(filesize)})
  100. shutil.copyfile(asset[element], f'{output}/{asset[element]}')
  101. continue
  102. # no content, add it under the Metadata level
  103. if asset[element] is None:
  104. asset[element] = ''
  105. ET.SubElement(assetMetadata, 'App_Data', {'App': 'MOD', 'Name': element, 'Value': str(asset[element])})
  106. def addTitle(tree, title, metadata):
  107. logger.info(f'insert title: {title}')
  108. ams_attributes = createAMSattributes('title', metadata)
  109. titleTree = ET.SubElement(tree, 'Metadata')
  110. ET.SubElement(titleTree, 'AMS', ams_attributes)
  111. ET.SubElement(titleTree, 'App_Data', {'App': 'MOD', 'Name': 'Type', 'Value': 'title'})
  112. for element in title:
  113. # check if there are multiple elements available
  114. if type(title[element]) == dict:
  115. for subelement in title[element]:
  116. # check if it contains multiple attributes like Language and a Value
  117. if type(title[element][subelement]) == dict:
  118. attributes = {'App': 'MOD', 'Name': element}
  119. # if the attribute is the same as the element change it to 'Value'
  120. for subsubelement in title[element][subelement]:
  121. if subsubelement == element:
  122. attributes.update({'Value': title[element][subelement][subsubelement]})
  123. else:
  124. # just add it to the attributes list
  125. attributes.update({subsubelement: title[element][subelement][subsubelement]})
  126. # add the multiattribute element to the tree
  127. ET.SubElement(titleTree, 'App_Data', attributes)
  128. continue
  129. # single attribute, add to the tree
  130. if title[element][subelement] is None:
  131. title[element][subelement] = ''
  132. ET.SubElement(titleTree, 'App_Data', {'App': 'MOD', 'Name': element, 'Value': title[element][subelement]})
  133. else:
  134. # single element, add to the tree
  135. if title[element] is None:
  136. title[element] = ''
  137. ET.SubElement(titleTree, 'App_Data', {'App': 'MOD', 'Name': element, 'Value': str(title[element])})
  138. def insertAssets(tree, metadata, assets, output):
  139. for asset in assets:
  140. if asset.upper() == 'TITLE':
  141. addTitle(tree, assets[asset], metadata)
  142. else:
  143. addAsset(tree, asset, assets[asset], metadata, output)
  144. return
  145. if __name__ == "__main__" :
  146. argparser = argparse.ArgumentParser(description='Creates ADI from yaml...')
  147. argparser.add_argument('--input', required=False, default="adi.yaml", help="yaml input to build adi.xml")
  148. argparser.add_argument('--output', required=True, help="output directory for xml and content files")
  149. args = argparser.parse_args()
  150. logger.debug(f"generating adi based on {args.input}")
  151. if not os.path.exists(args.input):
  152. logger.error(f"file {args.input} is not found, exiting...")
  153. sys.exit(0)
  154. with open(args.input, 'r') as ymlfile:
  155. cfg = yaml.load(ymlfile, Loader=yaml.FullLoader)
  156. try:
  157. os.mkdir(args.output)
  158. except FileExistsError:
  159. logger.warning(f'directory {args.output} already exists!')
  160. root = ET.Element('ADI')
  161. metadataTree = ET.SubElement(root, 'Metadata')
  162. insertMetadata(metadataTree, cfg['metadata'])
  163. assetTree = ET.SubElement(root, 'Asset')
  164. insertAssets(assetTree, cfg['metadata'], cfg['asset'], args.output)
  165. tree = ET.ElementTree(root)
  166. try:
  167. tree.write(f'{args.output}/adi.xml', encoding='utf-8', xml_declaration=True, pretty_print=True, doctype='<!DOCTYPE ADI SYSTEM "ADI.DTD">')
  168. except Exception as e:
  169. print(str(e))