Skip to content

Commit b93e11c

Browse files
authored
Merge pull request #20 from video-db/fix/vision-interface
Fix/vision and search interface
2 parents 62a7bc5 + a28842c commit b93e11c

10 files changed

+72
-17
lines changed

requirements-dev.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
ruff==0.1.7
22
pytest==7.4.3
3-
twine==4.0.2
3+
twine==5.1.1
44
wheel==0.42.0

setup.py

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
version=about["__version__"],
2424
author=about["__author__"],
2525
author_email=about["__email__"],
26+
license=about["__license__"],
2627
description="VideoDB Python SDK",
2728
long_description=long_description,
2829
long_description_content_type="text/markdown",

videodb/__about__.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
""" About information for videodb sdk"""
22

33

4-
__version__ = "0.2.0"
4+
__version__ = "0.2.1"
55
__title__ = "videodb"
66
__author__ = "videodb"
77
__email__ = "[email protected]"
88
__url__ = "https://github.com/video-db/videodb-python"
9+
__license__ = "Apache License 2.0"

videodb/__init__.py

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from videodb._utils._video import play_stream
88
from videodb._constants import (
99
VIDEO_DB_API,
10+
IndexType,
1011
SceneExtractionType,
1112
MediaType,
1213
SearchType,
@@ -30,6 +31,7 @@
3031
"VideodbError",
3132
"AuthenticationError",
3233
"InvalidRequestError",
34+
"IndexType",
3335
"SearchError",
3436
"play_stream",
3537
"MediaType",

videodb/_constants.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ class SearchType:
1818

1919

2020
class IndexType:
21-
semantic = "semantic"
21+
spoken_word = "spoken_word"
2222
scene = "scene"
2323

2424

2525
class SceneExtractionType:
26-
scene_based = "scene"
26+
shot_based = "shot"
2727
time_based = "time"
2828

2929

@@ -58,6 +58,8 @@ class ApiPath:
5858
invoices = "invoices"
5959
scenes = "scenes"
6060
scene = "scene"
61+
frame = "frame"
62+
describe = "describe"
6163

6264

6365
class Status:

videodb/collection.py

+4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
)
1111
from videodb._constants import (
1212
ApiPath,
13+
IndexType,
1314
SearchType,
1415
)
1516
from videodb.video import Video
@@ -100,6 +101,7 @@ def search(
100101
self,
101102
query: str,
102103
search_type: Optional[str] = SearchType.semantic,
104+
index_type: Optional[str] = IndexType.spoken_word,
103105
result_threshold: Optional[int] = None,
104106
score_threshold: Optional[float] = None,
105107
dynamic_score_percentage: Optional[float] = None,
@@ -108,6 +110,8 @@ def search(
108110
return search.search_inside_collection(
109111
collection_id=self.id,
110112
query=query,
113+
search_type=search_type,
114+
index_type=index_type,
111115
result_threshold=result_threshold,
112116
score_threshold=score_threshold,
113117
dynamic_score_percentage=dynamic_score_percentage,

videodb/image.py

+8
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,11 @@ def to_json(self):
6161
"frame_time": self.frame_time,
6262
"description": self.description,
6363
}
64+
65+
def describe(self, prompt: str = None, model_name=None):
66+
description_data = self._connection.post(
67+
path=f"{ApiPath.video}/{self.video_id}/{ApiPath.frame}/{self.id}/{ApiPath.describe}",
68+
data={"prompt": prompt, "model_name": model_name},
69+
)
70+
self.description = description_data.get("description", None)
71+
return self.description

videodb/scene.py

+12
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,15 @@ def __init__(
1414
description: str,
1515
id: str = None,
1616
frames: List[Frame] = [],
17+
connection=None,
1718
):
1819
self.id = id
1920
self.video_id = video_id
2021
self.start = start
2122
self.end = end
2223
self.frames: List[Frame] = frames
2324
self.description = description
25+
self._connection = connection
2426

2527
def __repr__(self) -> str:
2628
return (
@@ -43,6 +45,16 @@ def to_json(self):
4345
"description": self.description,
4446
}
4547

48+
def describe(self, prompt: str = None, model_name=None) -> None:
49+
if self._connection is None:
50+
raise ValueError("Connection is required to describe a scene")
51+
description_data = self._connection.post(
52+
path=f"{ApiPath.video}/{self.video_id}/{ApiPath.scene}/{self.id}/{ApiPath.describe}",
53+
data={"prompt": prompt, "model_name": model_name},
54+
)
55+
self.description = description_data.get("description", None)
56+
return self.description
57+
4658

4759
class SceneCollection:
4860
def __init__(

videodb/search.py

+17-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from abc import ABC, abstractmethod
22
from videodb._utils._video import play_stream
33
from videodb._constants import (
4+
IndexType,
45
SearchType,
56
ApiPath,
67
SemanticSearchDefaultValues,
@@ -109,6 +110,8 @@ def search_inside_video(
109110
self,
110111
video_id: str,
111112
query: str,
113+
search_type: str,
114+
index_type: str,
112115
result_threshold: Optional[int] = None,
113116
score_threshold: Optional[float] = None,
114117
dynamic_score_percentage: Optional[float] = None,
@@ -117,7 +120,8 @@ def search_inside_video(
117120
search_data = self._connection.post(
118121
path=f"{ApiPath.video}/{video_id}/{ApiPath.search}",
119122
data={
120-
"index_type": SearchType.semantic,
123+
"search_type": search_type,
124+
"index_type": index_type,
121125
"query": query,
122126
"score_threshold": score_threshold
123127
or SemanticSearchDefaultValues.score_threshold,
@@ -133,6 +137,8 @@ def search_inside_collection(
133137
self,
134138
collection_id: str,
135139
query: str,
140+
search_type: str,
141+
index_type: str,
136142
result_threshold: Optional[int] = None,
137143
score_threshold: Optional[float] = None,
138144
dynamic_score_percentage: Optional[float] = None,
@@ -141,7 +147,8 @@ def search_inside_collection(
141147
search_data = self._connection.post(
142148
path=f"{ApiPath.collection}/{collection_id}/{ApiPath.search}",
143149
data={
144-
"index_type": SearchType.semantic,
150+
"search_type": search_type,
151+
"index_type": index_type,
145152
"query": query,
146153
"score_threshold": score_threshold
147154
or SemanticSearchDefaultValues.score_threshold,
@@ -162,6 +169,8 @@ def search_inside_video(
162169
self,
163170
video_id: str,
164171
query: str,
172+
search_type: str,
173+
index_type: str,
165174
result_threshold: Optional[int] = None,
166175
score_threshold: Optional[float] = None,
167176
dynamic_score_percentage: Optional[float] = None,
@@ -170,7 +179,8 @@ def search_inside_video(
170179
search_data = self._connection.post(
171180
path=f"{ApiPath.video}/{video_id}/{ApiPath.search}",
172181
data={
173-
"index_type": SearchType.keyword,
182+
"search_type": search_type,
183+
"index_type": index_type,
174184
"query": query,
175185
"score_threshold": score_threshold,
176186
"result_threshold": result_threshold,
@@ -190,6 +200,8 @@ def search_inside_video(
190200
self,
191201
video_id: str,
192202
query: str,
203+
search_type: str,
204+
index_type: str,
193205
result_threshold: Optional[int] = None,
194206
score_threshold: Optional[float] = None,
195207
dynamic_score_percentage: Optional[float] = None,
@@ -198,7 +210,8 @@ def search_inside_video(
198210
search_data = self._connection.post(
199211
path=f"{ApiPath.video}/{video_id}/{ApiPath.search}",
200212
data={
201-
"index_type": SearchType.scene,
213+
"search_type": search_type,
214+
"index_type": IndexType.scene,
202215
"query": query,
203216
"score_threshold": score_threshold,
204217
"result_threshold": result_threshold,

videodb/video.py

+21-9
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ def search(
4949
self,
5050
query: str,
5151
search_type: Optional[str] = SearchType.semantic,
52+
index_type: Optional[str] = IndexType.spoken_word,
5253
result_threshold: Optional[int] = None,
5354
score_threshold: Optional[float] = None,
5455
dynamic_score_percentage: Optional[float] = None,
@@ -58,6 +59,8 @@ def search(
5859
return search.search_inside_video(
5960
video_id=self.id,
6061
query=query,
62+
search_type=search_type,
63+
index_type=index_type,
6164
result_threshold=result_threshold,
6265
score_threshold=score_threshold,
6366
dynamic_score_percentage=dynamic_score_percentage,
@@ -152,7 +155,7 @@ def index_spoken_words(
152155
self._connection.post(
153156
path=f"{ApiPath.video}/{self.id}/{ApiPath.index}",
154157
data={
155-
"index_type": IndexType.semantic,
158+
"index_type": IndexType.spoken_word,
156159
"language_code": language_code,
157160
"force": force,
158161
"callback_url": callback_url,
@@ -194,6 +197,7 @@ def _format_scene_collection(self, scene_collection_data: dict) -> SceneCollecti
194197
description=scene.get("description"),
195198
id=scene.get("scene_id"),
196199
frames=frames,
200+
connection=self._connection,
197201
)
198202
scenes.append(scene)
199203

@@ -207,11 +211,11 @@ def _format_scene_collection(self, scene_collection_data: dict) -> SceneCollecti
207211

208212
def extract_scenes(
209213
self,
210-
extraction_type: SceneExtractionType = SceneExtractionType.scene_based,
214+
extraction_type: SceneExtractionType = SceneExtractionType.shot_based,
211215
extraction_config: dict = {},
212216
force: bool = False,
213217
callback_url: str = None,
214-
):
218+
) -> Optional[SceneCollection]:
215219
scenes_data = self._connection.post(
216220
path=f"{ApiPath.video}/{self.id}/{ApiPath.scenes}",
217221
data={
@@ -225,10 +229,14 @@ def extract_scenes(
225229
return None
226230
return self._format_scene_collection(scenes_data.get("scene_collection"))
227231

228-
def get_scene_collection(self, collection_id: str):
232+
def get_scene_collection(self, collection_id: str) -> Optional[SceneCollection]:
233+
if not collection_id:
234+
raise ValueError("collection_id is required")
229235
scenes_data = self._connection.get(
230236
path=f"{ApiPath.video}/{self.id}/{ApiPath.scenes}/{collection_id}"
231237
)
238+
if not scenes_data:
239+
return None
232240
return self._format_scene_collection(scenes_data.get("scene_collection"))
233241

234242
def list_scene_collection(self):
@@ -238,29 +246,31 @@ def list_scene_collection(self):
238246
return scene_collections_data.get("scene_collections", [])
239247

240248
def delete_scene_collection(self, collection_id: str) -> None:
249+
if not collection_id:
250+
raise ValueError("collection_id is required")
241251
self._connection.delete(
242252
path=f"{ApiPath.video}/{self.id}/{ApiPath.scenes}/{collection_id}"
243253
)
244254

245255
def index_scenes(
246256
self,
247-
extraction_type: SceneExtractionType = SceneExtractionType.scene_based,
257+
extraction_type: SceneExtractionType = SceneExtractionType.shot_based,
248258
extraction_config: Dict = {},
249259
prompt: Optional[str] = None,
250-
model: Optional[str] = None,
260+
model_name: Optional[str] = None,
251261
model_config: Optional[Dict] = None,
252262
name: Optional[str] = None,
253263
scenes: Optional[List[Scene]] = None,
254264
force: Optional[bool] = False,
255265
callback_url: Optional[str] = None,
256-
) -> Optional[List]:
266+
) -> Optional[str]:
257267
scenes_data = self._connection.post(
258268
path=f"{ApiPath.video}/{self.id}/{ApiPath.index}/{ApiPath.scene}",
259269
data={
260270
"extraction_type": extraction_type,
261271
"extraction_config": extraction_config,
262272
"prompt": prompt,
263-
"model": model,
273+
"model_name": model_name,
264274
"model_config": model_config,
265275
"name": name,
266276
"force": force,
@@ -270,7 +280,7 @@ def index_scenes(
270280
)
271281
if not scenes_data:
272282
return None
273-
return scenes_data.get("scene_index_records", [])
283+
return scenes_data.get("scene_index_id")
274284

275285
def list_scene_index(self) -> List:
276286
index_data = self._connection.get(
@@ -287,6 +297,8 @@ def get_scene_index(self, scene_index_id: str) -> Optional[List]:
287297
return index_data.get("scene_index_records", [])
288298

289299
def delete_scene_index(self, scene_index_id: str) -> None:
300+
if not scene_index_id:
301+
raise ValueError("scene_index_id is required")
290302
self._connection.delete(
291303
path=f"{ApiPath.video}/{self.id}/{ApiPath.index}/{ApiPath.scene}/{scene_index_id}"
292304
)

0 commit comments

Comments
 (0)