Skip to main content

ContentAI Metadata Flattening Service

Project description


A method to flatten generated JSON data into timed CSV events in support of analytic workflows within the ContentAI Platform, published as the extractor dsai_metadata_flatten. There is also a pypi package of this package published for easy incorporation in other projects.

  1. Getting Started

  2. Execution

  3. Testing

  4. Future Development

  5. Changes

Getting Started

This library is used as a single-run executable.
Runtime parameters can be passed for processing that configure the returned results and can be examined in more detail in the main script.

NOTE: Not all flattening functions will respect/obey properties defined here.

  • force_overwrite - (bool) - force existing files to be overwritten (default=False)

  • compressed - (bool) - compress output CSVs instead of raw write (default=True, e.g. append ‘.gz’)

  • all_frames - (bool) - for video-based events, log all instances in box or just the center (default=False)

  • time_offset - (int) - when merging events for an asset split into

    multiple parts, time in seconds (default=0); negative numbers will cause a truncation (skip) of events happening before the zero time mark (added v0.7.1)

  • time_offset_source - (str) - check for this one-line file path with

    number of seconds offset according to time_offset rules; (added v1.4.0)

  • verbose - (bool) - verbose input/output configuration printing (default=False)

  • extractor - (string) - specify one extractor to flatten, skipping nested module import (default=all, e.g. dsai_metadata)

  • generator - (string) - cify one generator for output, skipping nested module import (*``=all, empty=none), e.g. ``flattened_csv)


CSV Schema (CSV)

One output of this flattening will be a set of CSV files if the flattened_csv is enabled as a generator. One file is created for each discovered/input parser/extractor. The standard schema for these CSV files has the following fields.

  • time_begin = time in seconds of event start

  • time_end = time in seconds of end (may be equal to time_start if instantaneous)

  • time_event = exact time in seconds (may be equal to time_start if instantaneous)

  • source_event = source media for event to add granularity for event inpact (e.g. face, video, audio, speech, image, ocr, script)

  • tag = simple text word or phrase

  • tag_type = descriptor for type of tag; e.g. tag=concept/label/emotion, keyword=special word, shot=segment, transcript=text, moderation=moderation, word=text/speech word, person=face or skeleton, phrase=long utterance, face=face emotion/properties, identity=face or speaker recognition, scene=semantic scenes/commercials/commercial_lead, brand=product or logo mention, emotion=visual or audio sentiment/emotion

  • score = confidence/probability

  • details = possible bounding box or other long-form (JSON-encoded) details

  • extractor = name of extractor for insight

Example Programmatic Use

While this library is primarily used as an extractor in ContentAI, it can be programmatically called within another extractor to simplify incoming data into a simple list for analysis. Several of these examples are available as code examples in the testing scripts.

  1. This dictionary-based call example will parse output of the azure_videoindexer and return it as a dictionary only (do not generate CSV or JSON output).

from contentai_metadata_flatten.main import flatten

dict_result = flatten({"path_content": "content/jobs", "extractor": "azure_videoindexer",
                       "generator": "", "verbose": True, "path_result": ".", args=[])
  1. This argument call example will parse all extractor outputs and generate a CSV.

from contentai_metadata_flatten.main import flatten

dict_result = flatten(args=["--path_content", "content/jobs/example.mp4",
                            "--generator", "flattened_csv", "--path_result": "content/flattened")
  1. This low-level access to a parser allows more control over which file or directory is parsed by the library and no generators are called. This call example is the same as the first example except that it returns a DataFrame instead of a dictionary and may be slightly faster.

from contentai_metadata_flatten import parsers
import logging

logger = logging.getLogger()

list_parser = parsers.get_by_name("azure_videoindexer")
parser_instance = list_parser[0]['obj']("content/jobs", logger=logger)
config_default = parser_instance.default_config()
result_df = parser_instance.parse(config_default)
  1. Another low-level access to parsers for only certain tag types. This call example allows the parsing of only certain tag types (below only those of type identity and face).

from contentai_metadata_flatten import parsers
import logging

logger = logging.getLogger()

list_parser = parsers.get_by_type(["face", "identity"])
for parser_obj in list_parser:
   parser_instance = parser_obj['obj']("content/jobs", logger=logger)
   config_default = parser_instance.default_config()
   result_df = parser_instance.parse(config_default)

Return Value

The main function now returns a richer dictionary (v1.3.0). For programatic callers of the function the dictionary object contains a data property (all of the flattened data as a list) and a generated property which contains a list of nested dictionaries indicating generated outptu (if enabled). An example output below demonstrates the flattened results as well as two enabled generators.

{'data': [
   {'tag': 'Clock', 'time_begin': 0, 'time_end': 1, 'time_event': 0, 'score': 0.08157, 'details': '{"model": "/m/01x3z"}', 'source_event': 'audio', 'tag_type': 'tag', 'extractor': 'example_extractor'},
   {'tag': 'Sine wave', 'time_begin': 0, 'time_end': 1, 'time_event': 0, 'score': 0.07586, 'details': '{"model": "/m/01v_m0"}', 'source_event': 'audio', 'tag_type': 'tag', 'extractor': 'example_extractor'},
   {'tag': 'Tick-tock', 'time_begin': 0, 'time_end': 1, 'time_event': 0, 'score': 0.07297, 'details': '{"model": "/m/07qjznl"}', 'source_event': 'audio', 'tag_type': 'tag', 'extractor': 'example_extractor'},
   ... ]
'generated': [
   {'generator': 'flattened_csv', 'path': 'testme/example_extractor.csv.gz'},
   {'generator': 'wbTimeTaggedMetadata', 'path': 'testme/wbTimeTaggedMetadata.json.gz'}]

Execution and Deployment

This package is meant to be run as a one-off processing tool that aggregates the insights of other extractors.

command-line standalone

Run the code as if it is an extractor. In this mode, configure a few environment variables to let the code know where to look for content.

One can also run the command-line with a single argument as input and optionally ad runtime configuration (see runtime variables) as part of the EXTRACTOR_METADATA variable as JSON.


Locally Run on Results

For utility, the above line has been wrapped in the bash script

    python -u

This allows a simplified command-line specification of a run configuration, which also allows the passage of metadata into a configuration.

Normal result generation into compressed CSVs (with overwrite).

./ data/wHaT3ver1t1s results/

Result generation with environment variables and integration of results from a file that was split at an offset of three hours.

./ results/1XMDAz9w8T1JFEKHRuNunQhRWL1/ results/ '{"force_overwrite":false,"time_offset":10800}'

Result generation from a single extractor, with its nested directory explicitly specified. (added v0.6.1)

./ results/dsai_metadata results/ '{"extractor":"dsai_metadata"}'

Local Runs with Timing Offsets

The script also searches for a text file called timing.txt in each source directory. If found, it will offset all results by the specified number of seconds before saving them to disk. Also, negative numbers will cause a truncation (skip) of events happening before the zero time mark. (added v0.7.1)

This capability may be useful if you have to manually split a file into multiple smaller files at a pre-determined time offset (e.g. three hours -> 10800 in timing.txt). (added v0.5.2)

echo "10800" > 1XMDAz9w8T1JFEKHRuNunQhRWL1/timing.txt
./ results/1XMDAz9w8T1JFEKHRuNunQhRWL1/ results/

Afterwards, new results can be added arbitrarily and the script can be rerun in the same directory to accomodate different timing offsets.

Example demonstrating integration of multiple output directories.

find results -type d  -d 1 | xargs -I {} ./ {} results/



Deployment is easy and follows standard ContentAI steps.

contentai deploy --cpu 256 --memory 512 metadata-flatten

Alternatively, you can pass an image name to reduce rebuilding a docker instance.

docker build -t metadata-deploy
contentai deploy metadata-flatten --cpu 256 --memory 512 -i metadata-deploy

Locally Downloading Results

You can locally download data from a specific job for this extractor to directly analyze.

contentai data wHaT3ver1t1s --dir data

Run as an Extractor

contentai run https://bucket/video.mp4  -w 'digraph { aws_rekognition_video_celebs -> metadata_flatten}'

JOB ID:     1Tfb1vPPqTQ0lVD1JDPUilB8QNr
CONTENT:    s3://bucket/video.mp4
STATE:      complete
START:      Fri Feb 15 04:38:05 PM (6 minutes ago)
UPDATED:    1 minute ago
END:        Fri Feb 15 04:43:04 PM (1 minute ago)
DURATION:   4 minutes



TASK      STATE      START           DURATION
724a493   complete   5 minutes ago   1 minute

Or run it via the docker image…

docker run --rm  -v `pwd`/:/x -e EXTRACTOR_CONTENT_PATH=/x/file.mp3 -e EXTRACTOR_RESULT_PATH=/x/result2 <docker_image>

View Extractor Logs (stdout)

contentai logs -f <my_extractor>
my_extractor Fri Nov 15 04:39:22 PM writing some data
Job complete in 4m58.265737799s


Testing is included via tox. To launch testing for the entire package, just run tox at the command line. Testing can also be run for a specific file within the package by setting the evironment variable TOX_ARGS. tox

Future Development

  • the remaining known extractors… openpose, dsai_tmstext_classifier_extractor,

    dsai_vinyl_sound_ai, dsai_name_entity_extractor, aws_rekognition_video_segments

  • integration of viewership insights

  • creation of sentiment and mood-based insights (which tags most co-occur here?)


A method to flatten generated JSON data into timed CSV events in support of analytic workflows within the ContentAI Platform.



  • fix for timing offsets; don’t overwrite any output if timing offset indicator


  • add new dsai_ads_detector parser for predictive ad locations



  • minor fix for azure_videoindexer parsing, now first video shot can not contain a keyframe ?


  • minor fix for gcp_videointelligence_text_detection parsing


  • fix for no-output generators

  • fix complete output for returned dictionary of data

  • add richer documentation for library/api usage


  • update output of main parse function to return a dict instead of file listing

  • modify generator specification to allow ALL (* default) or NONE for outputs



  • add parsers for gcp_videointelligence_text_detection, comskip_json, ibm_max_audio_classifier,

    gcp_videointelligence_object_tracking, gcp_videointelligence_people_detection

  • improve testing to iterate over known set of data in testing dir

  • fix generator/parser retrieve for whole name matches, not partials

  • add documentation for new types, explicitly call out person tag_type

  • update the dsai_activity_emotions parser to return tag type emotion (matching that of other AWS, Azure parsers)


  • update azure_videoindexer for tag_type in detected brands (was speech, now video)


  • add unit-testing to package build

  • add command-line / parser input as complement to contentai-driven ENV variables

  • fix bugs around specification of result path or specific generator



  • fix issue about constant reference

  • fix script for extra run param config

  • fix querying for local files in non-contentai environments (regression since 1.1.0)


  • inclusion of other constants for compatibility with other packages

  • refactor/rename of parser classes to mandate a filename output prefix (e.g. flatten_)

  • add dsai_activity_emotions parser (a clone of dsai_activity_classifier)



  • name update for dsai_moderation_image extractor


  • hotfix for build distribution

  • fix for content creation in streamlit/browsing app


  • deployed extractor (docker fix) for updated namespace


  • docs update, testing fixes, version bump for publication


  • rename to contentai-metadata-flatten and publish to pypi as a package!




  • add ability to parse input CSVs but not segment into shot

  • move to a single NLP library (spacy) for applications, using large model (with vectors)




  • update to optimize the pull of asset keys




  • small tweaks/normalization of rounding factor for extractors

  • correct emotion souce type for azure

  • refactor app location for primary streamlit browser - fix mode discovery for modules with specific UX interface

  • update file listing to show data bundle files as well

  • refactor utilities script for reuse in other apps


  • update to parse new version of dsai_places

  • add new parser for detectron2 extractor


  • add static file serving to streamlit app, inspired by this streamlit issue discussion

  • modify some pages to point to downloadable tables (with button click)

  • create new download page/mode that lists the generated and source files

  • minor refactor of app’s docker image for better caching in local creation and testing


  • add dsai_moderation_text parser, update dsai_moderation parser for version robustness - add min threshold (0.05) to both moderation detectors


  • add recursion to file-based discovery method for processed assets - unify read of JSON and text files with internalaized function call in extractor base class

  • fix some extractors to use single name reference self.EXTRACTOR


  • fix transcript parsing in azure_videoindexer component

  • add speaker differentiation as an identity block in azure_videoindexer (similar to aws_transcribe)


  • add timeline viewing to the event_table mode of streamlit app



  • fixes to main streamlit app for partial extractors (e.g. missing identity, sparse brand)


  • add parser for dsai_moderation


  • add parser for dsai_activity_classifier

  • fix bug for faulty rejection of flatten_aws_transcribe results


  • add parsers for pyscenedetect, dsai_sceneboundary, aws_transcribe, yolo3, aws_rekognition_video_text_detect

  • add speaker identity (from speech) to gcp_videointelligence_speech_transcription

  • add type field (maps to tag_type) to output generated by wbTimeTaggedTmetadata generator - add hashing against data (e.g. box) within JSON metadata generator


  • add parsers for dsai_yt8m (youtube8M or mediapipe)


  • add parsers for dsai_activity_slowfast (activity) and dsai_places (scene/settings)

  • add source_type sub-field to event_table browsing mode


  • add manifest option to application for multiple assets

  • fix app docker file for placement/generation of code with a specific user ID

  • fix CI/CD integration for auto launch

  • fix app explorer bugs (derive ‘words’ from transcript/keywords if none)


  • hotfix for missing data in dsai_metadata parser


  • slight refactor of how parsers are discovered, to allow search by name or type (for use as package)

  • fix package import for contentai local file

  • switch tag_type of ocr to transcript and ocr for source_type (azure_videoindexer)


  • adding music parser dsai_musicnn for different audio regions


  • convert to package for other modules to install

  • switch document to RST from MD

  • add primitive testing capabilities (to be filled)



  • added truncation/trim of events before zero mark if time offset is negative

  • re-brand extractor as dsai_metadata_flatten for ownership consistency


  • create new set of generator class objects for varying output generator

  • add new generator input for limiting output to a single type



  • rename rekognition_face_collection to aws_rekognition_face_collection for consistency


  • split documentation and changes

  • add new cae_metadata type of parser

  • modify source_type of detected faces in azure_videoindexer to face

  • modify to add new extractor input for limit to scanning (skips sub-dir check)


  • adding CI/CD script for gitlab

  • validate usage as a flattening service

  • modify source_type for aws_rekognition_video_celebs to face



  • adding face_attributes visualization mode for exploration of face data

  • fix face processing to split out to tag_type as face with richer subtags


  • add labeling component to application (for video/image inspection)

  • fix shot duration computeation in application (do not overwrite original event duration)

  • add text-search for scanning named entities, words from transcript


  • fix bugs in gcp_videointelligence_logo_recognition (timing) and aws_rekognition_video_faces (face emotions)

  • add new detection of timing.txt for integration of multiple results and their potential time offsets

  • added verbose flag to input of main parser

  • rename rekognition_face_collection for consistency with other parsers


  • split app modules into different visualization modes (overview, event_table, brand_expansion)

    • brand_expansion uses kNN search to expand from shots with brands to similar shots and returns those brands

    • event_table allows specific exploration of identity (e.g. celebrities) and brands witih image/video playback

    • NOTE The new application requires scikit-learn to perform live indexing of features

  • dramatically improved frame targeting (time offset) for event instances (video) in application


  • split main function into sepearate auto-discovered modules

  • add new user collection detection parser rekognition_face_collection (custom face collections)



  • fixes for gcp moderation flattening

  • fixes for app rendering (switch most graphs to scatter plot)

  • make all charts interactive again

  • fix for time zone/browser challenge in rendering


  • fixes for azure_videoindexer parser

  • add sentiment and emotion summary

  • rework graph generation and add bran/entity search capability


  • add new azure_videoindexer parser

  • switch flattened reference from logo to brand; explicit to moderation

  • add parsing library pytimeparse for simpler ingest

  • fix bug to delete old data bundle if reference files are available


  • add new time_offset parameter to environment/run configuration

  • fix bug for reusing/rewriting existing files

  • add output prefix flatten_ to all generated CSVs to avoid collision with other extractor input


  • fix docker image for nlp tasks, fix stop word aggregation


  • adding video playback (and image preview) via inline command-line execution of ffmpeg in application

  • create new for all-in-one explorer app creation



  • argument input capabilities for exploration app

  • sort histograms in exploration app by count not alphabet


  • browsing bugfixes for exploration application


  • added new streamlit code for data explorer interface

    • be sure to install extra packages if using this app and starting from scratch (e.g. new flattened files)

    • if you’re working from a cached model, you can also drop it in from a friend



  • schema change for verb/action consistency time_start -> time_begin

  • add additional row field tag_type to describe type of tag (see generated-insights)

  • add processing type gcp_videointelligence_logo_recognition

  • allow compression as a requirement/input for generated files (compressed as input)


  • add initial package, requirements, docker image

  • add basic readme for usage example

  • processes types gcp_videointelligence_label, gcp_videointelligence_shot_change, gcp_videointelligence_explicit_content, gcp_videointelligence_speech_transcription, aws_rekognition_video_content_moderation, aws_rekognition_video_celebs, aws_rekognition_video_labels, aws_rekognition_video_faces, aws_rekognition_video_person_tracking,

Project details

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

contentai_metadata_flatten-1.4.1.tar.gz (58.2 kB view hashes)

Uploaded source

Built Distribution

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page