Skip to main content

Download notebook · View source · Open in Colab

Work with Videos

A Video stores video data together with metadata properties. ApertureDB can filter videos by those properties and return their bytes. This example adds a cooking video, retrieves and updates its metadata, and deletes it.

The examples require Python 3.10 or later, an ApertureDB connection profile or environment credentials, and permission to create, read, update, and delete objects. A unique documentation_run property separates the example data from other records. Cleanup uses that value, so keep the setup variables available until cleanup completes.

Connect to ApertureDB​

ApertureDB Cloud The SDK connects with an API key from your Cloud instance. Obtain the key from Connect > Generate API Key and add it to a .env file in this directory:

APERTUREDB_KEY=your_key_here

Local evaluation: follow the deployment guide, then configure your connection profile.

%pip install --quiet "aperturedb==0.5.1" "python-dotenv==1.1.1"
Note: you may need to restart the kernel to use updated packages.
# Option A: ApertureDB Cloud
from dotenv import load_dotenv
load_dotenv() # loads APERTUREDB_KEY from .env into the environment
False
# For local evaluation, configure the profile from your versioned deployment package.
# https://docs.aperturedata.io/administration/deployment/DockerCompose
from aperturedb.CommonLibrary import create_connector

client = create_connector()

import uuid

documentation_run = uuid.uuid4().hex
ingredient_class = "CookbookIngredient_" + documentation_run
connection_class = "CookbookIngredientLink_" + documentation_run

def checked_query(query, input_blobs=None):
response, output_blobs = client.query(query, input_blobs or [])
assert isinstance(response, list) and len(response) == len(query), response
for command in response:
result = next(iter(command.values()))
assert result.get("status") == 0, response
return response, output_blobs

response, _ = checked_query([{"GetStatus": {}}])
client.print_last_response()
[
{
"GetStatus": {
"info": "OK",
"status": 0,
"system": "ApertureDB",
"version": "0.19.15"
}
}
]

Create or Add a Cooking Video to ApertureDB​

AddVideo stores the supplied cooking video with metadata describing the dish. The video bytes accompany the JSON command as an input blob.

The Python SDK loaders handle bulk ingestion.

# Download the sample file
from pathlib import Path
from urllib.request import urlopen
import hashlib

fixture_path = Path("data/crepe_flambe.mp4")
fixture_path.parent.mkdir(exist_ok=True)
fixture_url = "https://raw.githubusercontent.com/aperture-data/Cookbook/e333f6c59070b9165033d9ddd5af852a6b9624ba/notebooks/simple/data/crepe_flambe.mp4"
fixture_sha256 = "53275e11da0dcef74044be4ec9fd219d56e83fc8f57ae900da0120bf8551eaf1"
if not fixture_path.exists() or hashlib.sha256(fixture_path.read_bytes()).hexdigest() != fixture_sha256:
with urlopen(fixture_url, timeout=60) as response:
fixture_path.write_bytes(response.read())
assert hashlib.sha256(fixture_path.read_bytes()).hexdigest() == fixture_sha256
print("Verified fixture:", fixture_path.name)
Verified fixture: crepe_flambe.mp4
query = [{
"AddVideo": {
# Notice the missing "class" property since we already know its a Video (represented as _Video in ApertureDB)
"properties": {
'documentation_run': documentation_run,
"name": "crepe_flambe",
"id": 45,
"category": "dessert",
"cuisine": "French",
"location": "Brittany",
"caption": "Special Brittany flambe crepe"
},
"if_not_found": ['all', ['$id', '==', 45], ['$documentation_run', '==', documentation_run]]
}
}]

# Read the image data as a binary blob
fd = open("data/crepe_flambe.mp4", 'rb')
array = [ fd.read() ]
fd.close()

response, blobs = checked_query(query, array)

client.print_last_response()
[
{
"AddVideo": {
"status": 0
}
}
]

Query video by its metadata attributes​

FindVideo selects the cooking video by its metadata and returns its property values.

from aperturedb import NotebookHelpers as nh   # Our helper package for image displays and other utilities

query = [{
"FindVideo": {
"constraints": ['all', ['all', ['$name', '>=', 'crepe'], ['$location', '>', 'A']], ['$documentation_run', '==', documentation_run]],
"blobs": True, # This is set to False by default
"results": {
"all_properties": True
}
}
}]

response, blobs = checked_query(query)

client.print_last_response()
num_videos = response[0]["FindVideo"]["returned"]
for count in range(num_videos):
nh.display_video_mp4(blobs[count])

assert response[0]["FindVideo"]["returned"] == 1
assert len(blobs) == 1 and blobs[0]
[
{
"FindVideo": {
"blobs_start": 0,
"entities": [
{
"_blob_index": 0,
"_duration_us": 5500000,
"_fps": 30,
"_frame_count": 165,
"_frame_height": 480,
"_frame_width": 852,
"_uniqueid": "5.0.240",
"caption": "Special Brittany flambe crepe",
"category": "dessert",
"cuisine": "French",
"documentation_run": "71dd58274cf749ab8984c34980b41f8e",
"id": 45,
"location": "Brittany",
"name": "crepe_flambe"
}
],
"returned": 1,
"status": 0
}
}
]

Update properties of the video already in ApertureDB​

UpdateVideo changes existing metadata values or adds properties to matching videos.

query = [{
"UpdateVideo": {
"properties": {
"contributor": "Vishakha" # property will get added if missing or the value will be updated
},
"constraints": ['all', ['$name', '==', 'crepe_flambe'], ['$documentation_run', '==', documentation_run]],
}
}]

response, blobs = checked_query(query)

client.print_last_response()
[
{
"UpdateVideo": {
"count": 1,
"status": 0
}
}
]
query = [{
"FindVideo": {
"constraints": ['all', ['$name', '==', 'crepe_flambe'], ['$documentation_run', '==', documentation_run]],
"results": {
"all_properties": True
}
}
}]

response, blobs = checked_query(query)

client.print_last_response()

assert response[0]["FindVideo"]["entities"][0]["contributor"] == "Vishakha"
[
{
"FindVideo": {
"entities": [
{
"_duration_us": 5500000,
"_fps": 30,
"_frame_count": 165,
"_frame_height": 480,
"_frame_width": 852,
"_uniqueid": "5.0.240",
"caption": "Special Brittany flambe crepe",
"category": "dessert",
"contributor": "Vishakha",
"cuisine": "French",
"documentation_run": "71dd58274cf749ab8984c34980b41f8e",
"id": 45,
"location": "Brittany",
"name": "crepe_flambe"
}
],
"returned": 1,
"status": 0
}
}
]

Delete the video​

DeleteVideo removes the video selected by its metadata and this run's identifier.

query = [{
"DeleteVideo": {
"constraints": ['all', ['$name', '==', 'crepe_flambe'], ['$documentation_run', '==', documentation_run]]
}
}]

response, blobs = checked_query(query)

client.print_last_response()
[
{
"DeleteVideo": {
"count": 1,
"status": 0
}
}
]

Verify deletion​

After deletion, the same video query returns no matches.

query = [{
"FindVideo": {
"constraints": ['all', ['$name', '==', 'crepe_flambe'], ['$documentation_run', '==', documentation_run]],
"results": {
"all_properties": True
}
}
}]

response, blobs = checked_query(query)

client.print_last_response()

assert response[0]["FindVideo"]["returned"] == 0
[
{
"FindVideo": {
"returned": 0,
"status": 0
}
}
]

What's next?​