Skip to content
Merged
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
8e79b15
feat: add timelinev2
ankit-v2-3 Jun 23, 2025
a8fdf4e
fix: fit
ankit-v2-3 Jun 23, 2025
1e4a5f6
build: update v
ankit-v2-3 Jun 23, 2025
2b51bfc
fix: image asset
ankit-v2-3 Jun 26, 2025
2940995
feat: add audio asset
ankit-v2-3 Jul 2, 2025
2672b72
fix: asset enum
ankit-v2-3 Jul 2, 2025
3537e39
feat: add text asset
ankit-v2-3 Jul 8, 2025
ac78d55
fix: volume range
ankit-v2-3 Jul 17, 2025
fcf4796
feat: add caption asset
ankit-v2-3 Aug 5, 2025
9d5a6b0
fix: caption animation
ankit-v2-3 Aug 5, 2025
92dc137
fix: border style
ankit-v2-3 Aug 5, 2025
339e3a0
feat: add timeline download
ankit-v2-3 Aug 18, 2025
5027f9a
fix: position enum
ankit-v2-3 Sep 4, 2025
b28968a
fix: border style enum
ankit-v2-3 Sep 8, 2025
b15ab4f
docs: add docstrings
ankit-v2-3 Nov 21, 2025
33a271f
feat: increase api gateway timeout
ankit-v2-3 Dec 5, 2025
c379a86
feat: add editor
ankit-v2-3 Dec 10, 2025
2c3d152
fix: asset start
ankit-v2-3 Dec 12, 2025
092100d
docs: add docstrings
ankit-v2-3 Dec 12, 2025
7dee62d
Merge branch 'main' into ankit/add-videodb-editor
ankit-v2-3 Dec 12, 2025
8282460
fix: ci/cd
ankit-v2-3 Dec 12, 2025
d340301
fix: ci/cd
ankit-v2-3 Dec 12, 2025
f3af108
fix: ci/cd
ankit-v2-3 Dec 12, 2025
cc7e2b9
fix: ci/cd
ankit-v2-3 Dec 12, 2025
9bb1f56
docs: add docstings
ankit-v2-3 Dec 12, 2025
c1518c0
fix: ci/cd
ankit-v2-3 Dec 12, 2025
0d3a546
feat: add base64 ass string
ankit-v2-3 Dec 15, 2025
2889cb9
fix: Fit Enum
ankit-v2-3 Dec 24, 2025
1f325b1
Add reframe, smart vertical reframe and download functionality for vi…
ashish-spext Dec 25, 2025
4054df1
Add support for audio transcription and remove not required segmenter…
ashish-spext Dec 25, 2025
de2e32a
build: update version
ankit-v2-3 Dec 26, 2025
280ecf3
fix: https payload limit
ankit-v2-3 Jan 6, 2026
d6bf260
fix: payload limit to 100kb
ankit-v2-3 Jan 6, 2026
bd2c489
fix: remove font weight
ankit-v2-3 Jan 6, 2026
d3c606f
Merge branch 'release-0-4-0' into ankit/add-videodb-editor
ankit-v2-3 Jan 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 56 additions & 13 deletions videodb/editor.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import json
import requests

from typing import List, Optional, Union
from enum import Enum

from videodb._constants import ApiPath
from videodb.exceptions import InvalidRequestError


MAX_PAYLOAD_SIZE = 100 * 1024


class AssetType(str, Enum):
Expand Down Expand Up @@ -349,7 +356,6 @@ class Font:
:ivar int size: Font size in pixels
:ivar str color: Font color in hex format (e.g., "#FFFFFF")
:ivar float opacity: Font opacity (0.0 to 1.0)
:ivar int weight: (optional) Font weight (100 to 900)
"""

def __init__(
Expand All @@ -358,29 +364,24 @@ def __init__(
size: int = 48,
color: str = "#FFFFFF",
opacity: float = 1.0,
weight: Optional[int] = None,
):
"""Initialize a Font instance.

:param str family: Font family name (default: "Clear Sans")
:param int size: Font size in pixels (default: 48)
:param str color: Font color in hex format (default: "#FFFFFF")
:param float opacity: Font opacity between 0.0 and 1.0 (default: 1.0)
:param int weight: (optional) Font weight between 100 and 900
:raises ValueError: If size < 1, opacity not in [0.0, 1.0], or weight not in [100, 900]
:raises ValueError: If size < 1, opacity not in [0.0, 1.0]
"""
if size < 1:
raise ValueError("size must be at least 1")
if not (0.0 <= opacity <= 1.0):
raise ValueError("opacity must be between 0.0 and 1.0")
if weight is not None and not (100 <= weight <= 900):
raise ValueError("weight must be between 100 and 900")

self.family = family
self.size = size
self.color = color
self.opacity = opacity
self.weight = weight

def to_json(self) -> dict:
"""Convert the font settings to a JSON-serializable dictionary.
Expand All @@ -394,8 +395,6 @@ def to_json(self) -> dict:
"color": self.color,
"opacity": self.opacity,
}
if self.weight is not None:
data["weight"] = self.weight
return data


Expand Down Expand Up @@ -1100,17 +1099,61 @@ def generate_stream(self) -> str:
Makes an API request to render the timeline and generate streaming URLs.
Updates the stream_url and player_url instance variables.

If the timeline data exceeds the max payload size, it will be uploaded
as a file first to avoid HTTP content length limits.

:return: The stream URL of the generated video
:rtype: str
"""
stream_data = self.connection.post(
path=ApiPath.editor,
data=self.to_json(),
)
timeline_data = self.to_json()
json_str = json.dumps(timeline_data)
payload_size = len(json_str.encode("utf-8"))

if payload_size > MAX_PAYLOAD_SIZE:
# Upload timeline data as a file to avoid HTTP content length limits
timeline_url = self._upload_timeline_data(json_str)
stream_data = self.connection.post(
path=ApiPath.editor,
data={"timeline_url": timeline_url},
)
else:
stream_data = self.connection.post(
path=ApiPath.editor,
data=timeline_data,
)

self.stream_url = stream_data.get("stream_url")
self.player_url = stream_data.get("player_url")
return stream_data.get("stream_url", None)

def _upload_timeline_data(self, json_str: str) -> str:
"""Upload timeline JSON data as a file and return the URL.

:param str json_str: The JSON string of timeline data to upload
:return: The URL of the uploaded file
:rtype: str
:raises InvalidRequestError: If upload fails
"""
# Get a presigned upload URL
upload_url_data = self.connection.get(
path=f"{ApiPath.collection}/{self.connection.collection_id}/{ApiPath.upload_url}",
params={"name": "timeline_data.json"},
)
upload_url = upload_url_data.get("upload_url")

# Upload the JSON data as a file
try:
files = {"file": ("timeline_data.json", json_str, "application/json")}
response = requests.post(upload_url, files=files)
response.raise_for_status()
except requests.exceptions.RequestException as e:
raise InvalidRequestError(
f"Failed to upload timeline data: {str(e)}",
getattr(e, "response", None),
) from None

return upload_url

def download_stream(self, stream_url: str) -> dict:
"""Download a stream from the timeline.

Expand Down