From 49fa4914541f6018cab310d8fc6a0ad77c6b0bd2 Mon Sep 17 00:00:00 2001 From: Ankit raj <113342181+ankit-v2-3@users.noreply.github.com> Date: Wed, 26 Jun 2024 20:41:06 +0530 Subject: [PATCH 1/6] fix: search and vision interface --- videodb/__init__.py | 2 ++ videodb/_constants.py | 4 ++-- videodb/collection.py | 4 ++++ videodb/search.py | 21 +++++++++++++++++---- videodb/video.py | 25 ++++++++++++++++++------- 5 files changed, 43 insertions(+), 13 deletions(-) diff --git a/videodb/__init__.py b/videodb/__init__.py index b96558d..7aa45a7 100644 --- a/videodb/__init__.py +++ b/videodb/__init__.py @@ -7,6 +7,7 @@ from videodb._utils._video import play_stream from videodb._constants import ( VIDEO_DB_API, + IndexType, SceneExtractionType, MediaType, SearchType, @@ -30,6 +31,7 @@ "VideodbError", "AuthenticationError", "InvalidRequestError", + "IndexType", "SearchError", "play_stream", "MediaType", diff --git a/videodb/_constants.py b/videodb/_constants.py index e0ed017..f45141f 100644 --- a/videodb/_constants.py +++ b/videodb/_constants.py @@ -18,12 +18,12 @@ class SearchType: class IndexType: - semantic = "semantic" + spoken = "spoken" scene = "scene" class SceneExtractionType: - scene_based = "scene" + shot_based = "shot" time_based = "time" diff --git a/videodb/collection.py b/videodb/collection.py index 040979f..25bcb47 100644 --- a/videodb/collection.py +++ b/videodb/collection.py @@ -10,6 +10,7 @@ ) from videodb._constants import ( ApiPath, + IndexType, SearchType, ) from videodb.video import Video @@ -100,6 +101,7 @@ def search( self, query: str, search_type: Optional[str] = SearchType.semantic, + index_type: Optional[str] = IndexType.spoken, result_threshold: Optional[int] = None, score_threshold: Optional[float] = None, dynamic_score_percentage: Optional[float] = None, @@ -108,6 +110,8 @@ def search( return search.search_inside_collection( collection_id=self.id, query=query, + search_type=search_type, + index_type=index_type, result_threshold=result_threshold, score_threshold=score_threshold, dynamic_score_percentage=dynamic_score_percentage, diff --git a/videodb/search.py b/videodb/search.py index e9e6942..168d10f 100644 --- a/videodb/search.py +++ b/videodb/search.py @@ -1,6 +1,7 @@ from abc import ABC, abstractmethod from videodb._utils._video import play_stream from videodb._constants import ( + IndexType, SearchType, ApiPath, SemanticSearchDefaultValues, @@ -109,6 +110,8 @@ def search_inside_video( self, video_id: str, query: str, + search_type: str, + index_type: str, result_threshold: Optional[int] = None, score_threshold: Optional[float] = None, dynamic_score_percentage: Optional[float] = None, @@ -117,7 +120,8 @@ def search_inside_video( search_data = self._connection.post( path=f"{ApiPath.video}/{video_id}/{ApiPath.search}", data={ - "index_type": SearchType.semantic, + "search_type": search_type, + "index_type": index_type, "query": query, "score_threshold": score_threshold or SemanticSearchDefaultValues.score_threshold, @@ -133,6 +137,8 @@ def search_inside_collection( self, collection_id: str, query: str, + search_type: str, + index_type: str, result_threshold: Optional[int] = None, score_threshold: Optional[float] = None, dynamic_score_percentage: Optional[float] = None, @@ -141,7 +147,8 @@ def search_inside_collection( search_data = self._connection.post( path=f"{ApiPath.collection}/{collection_id}/{ApiPath.search}", data={ - "index_type": SearchType.semantic, + "search_type": search_type, + "index_type": index_type, "query": query, "score_threshold": score_threshold or SemanticSearchDefaultValues.score_threshold, @@ -162,6 +169,8 @@ def search_inside_video( self, video_id: str, query: str, + search_type: str, + index_type: str, result_threshold: Optional[int] = None, score_threshold: Optional[float] = None, dynamic_score_percentage: Optional[float] = None, @@ -170,7 +179,8 @@ def search_inside_video( search_data = self._connection.post( path=f"{ApiPath.video}/{video_id}/{ApiPath.search}", data={ - "index_type": SearchType.keyword, + "search_type": search_type, + "index_type": index_type, "query": query, "score_threshold": score_threshold, "result_threshold": result_threshold, @@ -190,6 +200,8 @@ def search_inside_video( self, video_id: str, query: str, + search_type: str, + index_type: str, result_threshold: Optional[int] = None, score_threshold: Optional[float] = None, dynamic_score_percentage: Optional[float] = None, @@ -198,7 +210,8 @@ def search_inside_video( search_data = self._connection.post( path=f"{ApiPath.video}/{video_id}/{ApiPath.search}", data={ - "index_type": SearchType.scene, + "search_type": search_type, + "index_type": IndexType.scene, "query": query, "score_threshold": score_threshold, "result_threshold": result_threshold, diff --git a/videodb/video.py b/videodb/video.py index 845e32d..6c4fb65 100644 --- a/videodb/video.py +++ b/videodb/video.py @@ -49,6 +49,7 @@ def search( self, query: str, search_type: Optional[str] = SearchType.semantic, + index_type: Optional[str] = IndexType.spoken, result_threshold: Optional[int] = None, score_threshold: Optional[float] = None, dynamic_score_percentage: Optional[float] = None, @@ -58,6 +59,8 @@ def search( return search.search_inside_video( video_id=self.id, query=query, + search_type=search_type, + index_type=index_type, result_threshold=result_threshold, score_threshold=score_threshold, dynamic_score_percentage=dynamic_score_percentage, @@ -152,7 +155,7 @@ def index_spoken_words( self._connection.post( path=f"{ApiPath.video}/{self.id}/{ApiPath.index}", data={ - "index_type": IndexType.semantic, + "index_type": IndexType.spoken, "language_code": language_code, "force": force, "callback_url": callback_url, @@ -207,11 +210,11 @@ def _format_scene_collection(self, scene_collection_data: dict) -> SceneCollecti def extract_scenes( self, - extraction_type: SceneExtractionType = SceneExtractionType.scene_based, + extraction_type: SceneExtractionType = SceneExtractionType.shot_based, extraction_config: dict = {}, force: bool = False, callback_url: str = None, - ): + ) -> Optional[SceneCollection]: scenes_data = self._connection.post( path=f"{ApiPath.video}/{self.id}/{ApiPath.scenes}", data={ @@ -225,10 +228,14 @@ def extract_scenes( return None return self._format_scene_collection(scenes_data.get("scene_collection")) - def get_scene_collection(self, collection_id: str): + def get_scene_collection(self, collection_id: str) -> Optional[SceneCollection]: + if not collection_id: + raise ValueError("collection_id is required") scenes_data = self._connection.get( path=f"{ApiPath.video}/{self.id}/{ApiPath.scenes}/{collection_id}" ) + if not scenes_data: + return None return self._format_scene_collection(scenes_data.get("scene_collection")) def list_scene_collection(self): @@ -238,13 +245,15 @@ def list_scene_collection(self): return scene_collections_data.get("scene_collections", []) def delete_scene_collection(self, collection_id: str) -> None: + if not collection_id: + raise ValueError("collection_id is required") self._connection.delete( path=f"{ApiPath.video}/{self.id}/{ApiPath.scenes}/{collection_id}" ) def index_scenes( self, - extraction_type: SceneExtractionType = SceneExtractionType.scene_based, + extraction_type: SceneExtractionType = SceneExtractionType.shot_based, extraction_config: Dict = {}, prompt: Optional[str] = None, model: Optional[str] = None, @@ -253,7 +262,7 @@ def index_scenes( scenes: Optional[List[Scene]] = None, force: Optional[bool] = False, callback_url: Optional[str] = None, - ) -> Optional[List]: + ) -> Optional[str]: scenes_data = self._connection.post( path=f"{ApiPath.video}/{self.id}/{ApiPath.index}/{ApiPath.scene}", data={ @@ -270,7 +279,7 @@ def index_scenes( ) if not scenes_data: return None - return scenes_data.get("scene_index_records", []) + return scenes_data.get("scene_index_id") def list_scene_index(self) -> List: index_data = self._connection.get( @@ -287,6 +296,8 @@ def get_scene_index(self, scene_index_id: str) -> Optional[List]: return index_data.get("scene_index_records", []) def delete_scene_index(self, scene_index_id: str) -> None: + if not scene_index_id: + raise ValueError("scene_index_id is required") self._connection.delete( path=f"{ApiPath.video}/{self.id}/{ApiPath.index}/{ApiPath.scene}/{scene_index_id}" ) From 125cf89371e2de0176c1ac576c92108c3dc07d1d Mon Sep 17 00:00:00 2001 From: Ankit raj <113342181+ankit-v2-3@users.noreply.github.com> Date: Wed, 26 Jun 2024 20:46:00 +0530 Subject: [PATCH 2/6] fix: index type --- videodb/_constants.py | 2 +- videodb/collection.py | 2 +- videodb/video.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/videodb/_constants.py b/videodb/_constants.py index f45141f..0d9a02b 100644 --- a/videodb/_constants.py +++ b/videodb/_constants.py @@ -18,7 +18,7 @@ class SearchType: class IndexType: - spoken = "spoken" + spoken_word = "spoken_word" scene = "scene" diff --git a/videodb/collection.py b/videodb/collection.py index 25bcb47..b610009 100644 --- a/videodb/collection.py +++ b/videodb/collection.py @@ -101,7 +101,7 @@ def search( self, query: str, search_type: Optional[str] = SearchType.semantic, - index_type: Optional[str] = IndexType.spoken, + index_type: Optional[str] = IndexType.spoken_word, result_threshold: Optional[int] = None, score_threshold: Optional[float] = None, dynamic_score_percentage: Optional[float] = None, diff --git a/videodb/video.py b/videodb/video.py index 6c4fb65..5e713ab 100644 --- a/videodb/video.py +++ b/videodb/video.py @@ -49,7 +49,7 @@ def search( self, query: str, search_type: Optional[str] = SearchType.semantic, - index_type: Optional[str] = IndexType.spoken, + index_type: Optional[str] = IndexType.spoken_word, result_threshold: Optional[int] = None, score_threshold: Optional[float] = None, dynamic_score_percentage: Optional[float] = None, @@ -155,7 +155,7 @@ def index_spoken_words( self._connection.post( path=f"{ApiPath.video}/{self.id}/{ApiPath.index}", data={ - "index_type": IndexType.spoken, + "index_type": IndexType.spoken_word, "language_code": language_code, "force": force, "callback_url": callback_url, From 3841880d86fd77e444b6766a94841acb2a702e72 Mon Sep 17 00:00:00 2001 From: Ankit raj <113342181+ankit-v2-3@users.noreply.github.com> Date: Thu, 27 Jun 2024 16:48:14 +0530 Subject: [PATCH 3/6] feat: add scene and frame describe --- videodb/_constants.py | 6 ++++++ videodb/image.py | 9 +++++++++ videodb/scene.py | 14 +++++++++++++- videodb/video.py | 1 + 4 files changed, 29 insertions(+), 1 deletion(-) diff --git a/videodb/_constants.py b/videodb/_constants.py index 0d9a02b..400850e 100644 --- a/videodb/_constants.py +++ b/videodb/_constants.py @@ -27,6 +27,10 @@ class SceneExtractionType: time_based = "time" +class SceneModels: + gpt4_o = "gpt4-o" + + class Workflows: add_subtitles = "add_subtitles" @@ -58,6 +62,8 @@ class ApiPath: invoices = "invoices" scenes = "scenes" scene = "scene" + frame = "frame" + describe = "describe" class Status: diff --git a/videodb/image.py b/videodb/image.py index c1bd2ef..bc427dd 100644 --- a/videodb/image.py +++ b/videodb/image.py @@ -1,5 +1,6 @@ from videodb._constants import ( ApiPath, + SceneModels, ) @@ -61,3 +62,11 @@ def to_json(self): "frame_time": self.frame_time, "description": self.description, } + + def describe(self, prompt: str = None, model_name=SceneModels.gpt4_o): + description_data = self._connection.post( + path=f"{ApiPath.video}/{self.video_id}/{ApiPath.frame}/{self.id}/{ApiPath.describe}", + data={"prompt": prompt, "model_name": model_name}, + ) + self.description = description_data.get("description", None) + return self.description diff --git a/videodb/scene.py b/videodb/scene.py index 182bfe8..c17ca16 100644 --- a/videodb/scene.py +++ b/videodb/scene.py @@ -1,6 +1,6 @@ from typing import List -from videodb._constants import ApiPath +from videodb._constants import ApiPath, SceneModels from videodb.image import Frame @@ -14,6 +14,7 @@ def __init__( description: str, id: str = None, frames: List[Frame] = [], + connection=None, ): self.id = id self.video_id = video_id @@ -21,6 +22,7 @@ def __init__( self.end = end self.frames: List[Frame] = frames self.description = description + self._connection = connection def __repr__(self) -> str: return ( @@ -43,6 +45,16 @@ def to_json(self): "description": self.description, } + def describe(self, prompt: str = None, model_name=SceneModels.gpt4_o) -> None: + if self._connection is None: + raise ValueError("Connection is required to describe a scene") + description_data = self._connection.post( + path=f"{ApiPath.video}/{self.video_id}/{ApiPath.scene}/{self.id}/{ApiPath.describe}", + data={"prompt": prompt, "model_name": model_name}, + ) + self.description = description_data.get("description", None) + return self.description + class SceneCollection: def __init__( diff --git a/videodb/video.py b/videodb/video.py index 5e713ab..f2e2687 100644 --- a/videodb/video.py +++ b/videodb/video.py @@ -197,6 +197,7 @@ def _format_scene_collection(self, scene_collection_data: dict) -> SceneCollecti description=scene.get("description"), id=scene.get("scene_id"), frames=frames, + connection=self._connection, ) scenes.append(scene) From 43001635aa46c9aefee0f61723604aa19f59cb9e Mon Sep 17 00:00:00 2001 From: Ankit raj <113342181+ankit-v2-3@users.noreply.github.com> Date: Thu, 27 Jun 2024 17:05:31 +0530 Subject: [PATCH 4/6] refactor: model to model_name --- videodb/_constants.py | 4 ---- videodb/image.py | 3 +-- videodb/scene.py | 4 ++-- videodb/video.py | 4 ++-- 4 files changed, 5 insertions(+), 10 deletions(-) diff --git a/videodb/_constants.py b/videodb/_constants.py index 400850e..7f12586 100644 --- a/videodb/_constants.py +++ b/videodb/_constants.py @@ -27,10 +27,6 @@ class SceneExtractionType: time_based = "time" -class SceneModels: - gpt4_o = "gpt4-o" - - class Workflows: add_subtitles = "add_subtitles" diff --git a/videodb/image.py b/videodb/image.py index bc427dd..5a97b87 100644 --- a/videodb/image.py +++ b/videodb/image.py @@ -1,6 +1,5 @@ from videodb._constants import ( ApiPath, - SceneModels, ) @@ -63,7 +62,7 @@ def to_json(self): "description": self.description, } - def describe(self, prompt: str = None, model_name=SceneModels.gpt4_o): + def describe(self, prompt: str = None, model_name=None): description_data = self._connection.post( path=f"{ApiPath.video}/{self.video_id}/{ApiPath.frame}/{self.id}/{ApiPath.describe}", data={"prompt": prompt, "model_name": model_name}, diff --git a/videodb/scene.py b/videodb/scene.py index c17ca16..3bb1028 100644 --- a/videodb/scene.py +++ b/videodb/scene.py @@ -1,6 +1,6 @@ from typing import List -from videodb._constants import ApiPath, SceneModels +from videodb._constants import ApiPath from videodb.image import Frame @@ -45,7 +45,7 @@ def to_json(self): "description": self.description, } - def describe(self, prompt: str = None, model_name=SceneModels.gpt4_o) -> None: + def describe(self, prompt: str = None, model_name=None) -> None: if self._connection is None: raise ValueError("Connection is required to describe a scene") description_data = self._connection.post( diff --git a/videodb/video.py b/videodb/video.py index f2e2687..5a0e291 100644 --- a/videodb/video.py +++ b/videodb/video.py @@ -257,7 +257,7 @@ def index_scenes( extraction_type: SceneExtractionType = SceneExtractionType.shot_based, extraction_config: Dict = {}, prompt: Optional[str] = None, - model: Optional[str] = None, + model_name: Optional[str] = None, model_config: Optional[Dict] = None, name: Optional[str] = None, scenes: Optional[List[Scene]] = None, @@ -270,7 +270,7 @@ def index_scenes( "extraction_type": extraction_type, "extraction_config": extraction_config, "prompt": prompt, - "model": model, + "model_name": model_name, "model_config": model_config, "name": name, "force": force, From 157b439fb80085653888fdc858de3db6a5ec0b31 Mon Sep 17 00:00:00 2001 From: Ankit raj <113342181+ankit-v2-3@users.noreply.github.com> Date: Thu, 27 Jun 2024 17:41:34 +0530 Subject: [PATCH 5/6] build: upgrade version and add license --- setup.py | 1 + videodb/__about__.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index b97aad2..db2f955 100644 --- a/setup.py +++ b/setup.py @@ -23,6 +23,7 @@ version=about["__version__"], author=about["__author__"], author_email=about["__email__"], + license=about["__license__"], description="VideoDB Python SDK", long_description=long_description, long_description_content_type="text/markdown", diff --git a/videodb/__about__.py b/videodb/__about__.py index d71b052..aa144bc 100644 --- a/videodb/__about__.py +++ b/videodb/__about__.py @@ -1,8 +1,9 @@ """ About information for videodb sdk""" -__version__ = "0.2.0" +__version__ = "0.2.1" __title__ = "videodb" __author__ = "videodb" __email__ = "contact@videodb.io" __url__ = "https://github.com/video-db/videodb-python" +__license__ = "Apache License 2.0" From a28842cb56cf0b4e82a1db69a7d1a521ddae8152 Mon Sep 17 00:00:00 2001 From: Ankit raj <113342181+ankit-v2-3@users.noreply.github.com> Date: Thu, 27 Jun 2024 17:54:54 +0530 Subject: [PATCH 6/6] build: upgrade twine --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index bf8db06..07fee5c 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,4 +1,4 @@ ruff==0.1.7 pytest==7.4.3 -twine==4.0.2 +twine==5.1.1 wheel==0.42.0