From 320b1a4835679e80836daf0399f6fd6adfeac0cf Mon Sep 17 00:00:00 2001 From: Ankit raj <113342181+ankit-v2-3@users.noreply.github.com> Date: Sun, 10 Mar 2024 16:55:20 +0530 Subject: [PATCH 001/112] feat: add get, create collection --- videodb/client.py | 28 ++++++++++++++++++++++++++++ videodb/collection.py | 8 ++++++++ 2 files changed, 36 insertions(+) diff --git a/videodb/client.py b/videodb/client.py index fd823f9..8b35c06 100644 --- a/videodb/client.py +++ b/videodb/client.py @@ -3,6 +3,7 @@ from typing import ( Optional, Union, + List, ) from videodb._constants import ( @@ -39,6 +40,33 @@ def get_collection(self, collection_id: Optional[str] = "default") -> Collection collection_data.get("description"), ) + def get_collections(self) -> List[Collection]: + collections_data = self.get(path=ApiPath.collection) + return [ + Collection( + self, + collection.get("id"), + collection.get("name"), + collection.get("description"), + ) + for collection in collections_data.get("collections") + ] + + def create_collection(self, name: str, description: str) -> Collection: + collection_data = self.post( + path=ApiPath.collection, + data={ + "name": name, + "description": description, + }, + ) + return Collection( + self, + collection_data.get("id"), + collection_data.get("name"), + collection_data.get("description"), + ) + def upload( self, file_path: str = None, diff --git a/videodb/collection.py b/videodb/collection.py index 8073ec9..17e87f8 100644 --- a/videodb/collection.py +++ b/videodb/collection.py @@ -27,6 +27,14 @@ def __init__(self, _connection, id: str, name: str = None, description: str = No self.name = name self.description = description + def __repr__(self) -> str: + return ( + f"Collection(" + f"id={self.id}, " + f"name={self.name}, " + f"description={self.description})" + ) + def get_videos(self) -> List[Video]: videos_data = self._connection.get(path=f"{ApiPath.video}") return [Video(self._connection, **video) for video in videos_data.get("videos")] From 4b54cd69f2ad489ebe5c40bf2b15cc604dedc328 Mon Sep 17 00:00:00 2001 From: Ankit raj <113342181+ankit-v2-3@users.noreply.github.com> Date: Mon, 11 Mar 2024 18:02:23 +0530 Subject: [PATCH 002/112] feat: add force index --- videodb/video.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/videodb/video.py b/videodb/video.py index a2bcbab..57561e7 100644 --- a/videodb/video.py +++ b/videodb/video.py @@ -116,18 +116,18 @@ def get_transcript_text(self, force: bool = False) -> str: self._fetch_transcript(force) return self.transcript_text - def index_spoken_words(self) -> None: + def index_spoken_words(self, force: bool = False) -> None: """Semantic indexing of spoken words in the video :raises InvalidRequestError: If the video is already indexed :return: None if the indexing is successful :rtype: None """ - self._fetch_transcript() self._connection.post( path=f"{ApiPath.video}/{self.id}/{ApiPath.index}", data={ "index_type": IndexType.semantic, + "force": force, }, ) From 8a6edbd9f0901e842863bcd7f5abbbb176b7b4d2 Mon Sep 17 00:00:00 2001 From: Ankit raj <113342181+ankit-v2-3@users.noreply.github.com> Date: Mon, 11 Mar 2024 19:54:08 +0530 Subject: [PATCH 003/112] fix: get v/a/img path --- videodb/collection.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/videodb/collection.py b/videodb/collection.py index 17e87f8..ff37769 100644 --- a/videodb/collection.py +++ b/videodb/collection.py @@ -36,7 +36,9 @@ def __repr__(self) -> str: ) def get_videos(self) -> List[Video]: - videos_data = self._connection.get(path=f"{ApiPath.video}") + videos_data = self._connection.get( + path=f"{ApiPath.collection}/{self.id}/{ApiPath.video}" + ) return [Video(self._connection, **video) for video in videos_data.get("videos")] def get_video(self, video_id: str) -> Video: @@ -54,7 +56,9 @@ def delete_video(self, video_id: str) -> None: return self._connection.delete(path=f"{ApiPath.video}/{video_id}") def get_audios(self) -> List[Audio]: - audios_data = self._connection.get(path=f"{ApiPath.audio}") + audios_data = self._connection.get( + path=f"{ApiPath.collection}/{self.id}/{ApiPath.audio}" + ) return [Audio(self._connection, **audio) for audio in audios_data.get("audios")] def get_audio(self, audio_id: str) -> Audio: @@ -65,7 +69,9 @@ def delete_audio(self, audio_id: str) -> None: return self._connection.delete(path=f"{ApiPath.audio}/{audio_id}") def get_images(self) -> List[Image]: - images_data = self._connection.get(path=f"{ApiPath.image}") + images_data = self._connection.get( + path=f"{ApiPath.collection}/{self.id}/{ApiPath.image}" + ) return [Image(self._connection, **image) for image in images_data.get("images")] def get_image(self, image_id: str) -> Image: From ee30a5a19a8b0dc7f6aa8fa11a3a7d06661d840e Mon Sep 17 00:00:00 2001 From: Ankit raj <113342181+ankit-v2-3@users.noreply.github.com> Date: Mon, 11 Mar 2024 20:13:33 +0530 Subject: [PATCH 004/112] fix: create collection --- videodb/client.py | 1 + 1 file changed, 1 insertion(+) diff --git a/videodb/client.py b/videodb/client.py index 8b35c06..1b059b7 100644 --- a/videodb/client.py +++ b/videodb/client.py @@ -60,6 +60,7 @@ def create_collection(self, name: str, description: str) -> Collection: "description": description, }, ) + self.collection_id = collection_data.get("id", "default") return Collection( self, collection_data.get("id"), From 3429e7f1c16633fa15eb4eca58273924479211de Mon Sep 17 00:00:00 2001 From: Ankit raj <113342181+ankit-v2-3@users.noreply.github.com> Date: Wed, 13 Mar 2024 15:14:41 +0530 Subject: [PATCH 005/112] feat: add progress bar --- videodb/_utils/_http_client.py | 7 +++++-- videodb/video.py | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/videodb/_utils/_http_client.py b/videodb/_utils/_http_client.py index 4555411..35eb5c5 100644 --- a/videodb/_utils/_http_client.py +++ b/videodb/_utils/_http_client.py @@ -87,7 +87,7 @@ def _make_request( def _handle_request_error(self, e: requests.exceptions.RequestException) -> None: """Handle request errors""" - + self.show_progress = False if isinstance(e, requests.exceptions.HTTPError): try: error_message = e.response.json().get("message", "Unknown error") @@ -198,8 +198,11 @@ def get( self.show_progress = show_progress return self._make_request(method=self.session.get, path=path, **kwargs) - def post(self, path: str, data=None, **kwargs) -> requests.Response: + def post( + self, path: str, data=None, show_progress: Optional[bool] = False, **kwargs + ) -> requests.Response: """Make a post request""" + self.show_progress = show_progress return self._make_request(self.session.post, path, json=data, **kwargs) def put(self, path: str, data=None, **kwargs) -> requests.Response: diff --git a/videodb/video.py b/videodb/video.py index 57561e7..357660a 100644 --- a/videodb/video.py +++ b/videodb/video.py @@ -129,6 +129,7 @@ def index_spoken_words(self, force: bool = False) -> None: "index_type": IndexType.semantic, "force": force, }, + show_progress=True, ) def index_scenes( From 753d2722b7163e6fd92ae072d498cfd8eb132467 Mon Sep 17 00:00:00 2001 From: Ankit raj <113342181+ankit-v2-3@users.noreply.github.com> Date: Thu, 14 Mar 2024 12:20:40 +0530 Subject: [PATCH 006/112] feat: add update coll. --- videodb/client.py | 16 ++++++++++++++++ videodb/collection.py | 33 ++++++++++++++++++++++++--------- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/videodb/client.py b/videodb/client.py index 1b059b7..643807b 100644 --- a/videodb/client.py +++ b/videodb/client.py @@ -68,6 +68,22 @@ def create_collection(self, name: str, description: str) -> Collection: collection_data.get("description"), ) + def update_collection(self, id: str, name: str, description: str) -> Collection: + collection_data = self.patch( + path=f"{ApiPath.collection}/{id}", + data={ + "name": name, + "description": description, + }, + ) + self.collection_id = collection_data.get("id", "default") + return Collection( + self, + collection_data.get("id"), + collection_data.get("name"), + collection_data.get("description"), + ) + def upload( self, file_path: str = None, diff --git a/videodb/collection.py b/videodb/collection.py index ff37769..fcafa4d 100644 --- a/videodb/collection.py +++ b/videodb/collection.py @@ -37,12 +37,15 @@ def __repr__(self) -> str: def get_videos(self) -> List[Video]: videos_data = self._connection.get( - path=f"{ApiPath.collection}/{self.id}/{ApiPath.video}" + path=f"{ApiPath.video}", + params={"collection_id": self.id}, ) return [Video(self._connection, **video) for video in videos_data.get("videos")] def get_video(self, video_id: str) -> Video: - video_data = self._connection.get(path=f"{ApiPath.video}/{video_id}") + video_data = self._connection.get( + path=f"{ApiPath.video}/{video_id}", params={"collection_id": self.id} + ) return Video(self._connection, **video_data) def delete_video(self, video_id: str) -> None: @@ -53,33 +56,45 @@ def delete_video(self, video_id: str) -> None: :return: None if the delete is successful :rtype: None """ - return self._connection.delete(path=f"{ApiPath.video}/{video_id}") + return self._connection.delete( + path=f"{ApiPath.video}/{video_id}", params={"collection_id": self.id} + ) def get_audios(self) -> List[Audio]: audios_data = self._connection.get( - path=f"{ApiPath.collection}/{self.id}/{ApiPath.audio}" + path=f"{ApiPath.audio}", + params={"collection_id": self.id}, ) return [Audio(self._connection, **audio) for audio in audios_data.get("audios")] def get_audio(self, audio_id: str) -> Audio: - audio_data = self._connection.get(path=f"{ApiPath.audio}/{audio_id}") + audio_data = self._connection.get( + path=f"{ApiPath.audio}/{audio_id}", params={"collection_id": self.id} + ) return Audio(self._connection, **audio_data) def delete_audio(self, audio_id: str) -> None: - return self._connection.delete(path=f"{ApiPath.audio}/{audio_id}") + return self._connection.delete( + path=f"{ApiPath.audio}/{audio_id}", params={"collection_id": self.id} + ) def get_images(self) -> List[Image]: images_data = self._connection.get( - path=f"{ApiPath.collection}/{self.id}/{ApiPath.image}" + path=f"{ApiPath.image}", + params={"collection_id": self.id}, ) return [Image(self._connection, **image) for image in images_data.get("images")] def get_image(self, image_id: str) -> Image: - image_data = self._connection.get(path=f"{ApiPath.image}/{image_id}") + image_data = self._connection.get( + path=f"{ApiPath.image}/{image_id}", params={"collection_id": self.id} + ) return Image(self._connection, **image_data) def delete_image(self, image_id: str) -> None: - return self._connection.delete(path=f"{ApiPath.image}/{image_id}") + return self._connection.delete( + path=f"{ApiPath.image}/{image_id}", params={"collection_id": self.id} + ) def search( self, From bb5ad25fc858d09a2df7c2969f9da77462df5051 Mon Sep 17 00:00:00 2001 From: Ankit raj <113342181+ankit-v2-3@users.noreply.github.com> Date: Mon, 18 Mar 2024 20:21:43 +0530 Subject: [PATCH 007/112] feat: async index --- videodb/video.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/videodb/video.py b/videodb/video.py index 357660a..33ffd27 100644 --- a/videodb/video.py +++ b/videodb/video.py @@ -116,7 +116,7 @@ def get_transcript_text(self, force: bool = False) -> str: self._fetch_transcript(force) return self.transcript_text - def index_spoken_words(self, force: bool = False) -> None: + def index_spoken_words(self, force: bool = False, callback_url: str = None) -> None: """Semantic indexing of spoken words in the video :raises InvalidRequestError: If the video is already indexed @@ -128,6 +128,7 @@ def index_spoken_words(self, force: bool = False) -> None: data={ "index_type": IndexType.semantic, "force": force, + "callback_url": callback_url, }, show_progress=True, ) From 2bcd7e38cdcc97eab8b8fc80779b3a139223d61e Mon Sep 17 00:00:00 2001 From: Ankit raj <113342181+ankit-v2-3@users.noreply.github.com> Date: Tue, 26 Mar 2024 14:06:16 +0530 Subject: [PATCH 008/112] feat: add thumbnail timestamp --- videodb/_constants.py | 1 + videodb/image.py | 4 +++- videodb/video.py | 21 +++++++++++++++++++-- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/videodb/_constants.py b/videodb/_constants.py index 50fe068..b7e0503 100644 --- a/videodb/_constants.py +++ b/videodb/_constants.py @@ -39,6 +39,7 @@ class ApiPath: image = "image" stream = "stream" thumbnail = "thumbnail" + thumbnails = "thumbnails" upload_url = "upload_url" transcription = "transcription" index = "index" diff --git a/videodb/image.py b/videodb/image.py index 69e0ec3..7d83f83 100644 --- a/videodb/image.py +++ b/videodb/image.py @@ -9,13 +9,15 @@ def __init__(self, _connection, id: str, collection_id: str, **kwargs) -> None: self.id = id self.collection_id = collection_id self.name = kwargs.get("name", None) + self.url = kwargs.get("url", None) def __repr__(self) -> str: return ( f"Image(" f"id={self.id}, " f"collection_id={self.collection_id}, " - f"name={self.name})" + f"name={self.name}), " + f"url={self.url}" ) def delete(self) -> None: diff --git a/videodb/video.py b/videodb/video.py index 33ffd27..183a7b9 100644 --- a/videodb/video.py +++ b/videodb/video.py @@ -7,6 +7,7 @@ SubtitleStyle, Workflows, ) +from videodb.image import Image from videodb.search import SearchFactory, SearchResult from videodb.shot import Shot @@ -88,15 +89,31 @@ def generate_stream(self, timeline: Optional[List[Tuple[int, int]]] = None) -> s ) return stream_data.get("stream_url", None) - def generate_thumbnail(self): - if self.thumbnail_url: + def generate_thumbnail(self, time: Optional[float] = None) -> Union[str, Image]: + if self.thumbnail_url and not time: return self.thumbnail_url + + if time: + thumbnail_data = self._connection.post( + path=f"{ApiPath.video}/{self.id}/{ApiPath.thumbnail}", + data={ + "time": time, + }, + ) + return Image(self._connection, **thumbnail_data) + thumbnail_data = self._connection.get( path=f"{ApiPath.video}/{self.id}/{ApiPath.thumbnail}" ) self.thumbnail_url = thumbnail_data.get("thumbnail_url") return self.thumbnail_url + def get_thumbnails(self) -> List[Image]: + thumbnails_data = self._connection.get( + path=f"{ApiPath.video}/{self.id}/{ApiPath.thumbnails}" + ) + return [Image(self._connection, **thumbnail) for thumbnail in thumbnails_data] + def _fetch_transcript(self, force: bool = False) -> None: if self.transcript and not force: return From 5a848b34b5ba3331b169458a3cc8a590691ca28d Mon Sep 17 00:00:00 2001 From: Ankit raj <113342181+ankit-v2-3@users.noreply.github.com> Date: Tue, 26 Mar 2024 14:45:45 +0530 Subject: [PATCH 009/112] fix: image repr --- videodb/image.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/videodb/image.py b/videodb/image.py index 7d83f83..d7e5e0c 100644 --- a/videodb/image.py +++ b/videodb/image.py @@ -16,8 +16,8 @@ def __repr__(self) -> str: f"Image(" f"id={self.id}, " f"collection_id={self.collection_id}, " - f"name={self.name}), " - f"url={self.url}" + f"name={self.name}, " + f"url={self.url})" ) def delete(self) -> None: From bda5702ef24ecf90f13a3a95f0a6307b7b1851c1 Mon Sep 17 00:00:00 2001 From: Ankit raj <113342181+ankit-v2-3@users.noreply.github.com> Date: Tue, 26 Mar 2024 15:14:00 +0530 Subject: [PATCH 010/112] build: update version --- videodb/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/videodb/__init__.py b/videodb/__init__.py index 51225eb..e61c91a 100644 --- a/videodb/__init__.py +++ b/videodb/__init__.py @@ -24,7 +24,7 @@ logger: logging.Logger = logging.getLogger("videodb") -__version__ = "0.1.0" +__version__ = "0.1.1" __author__ = "videodb" __all__ = [ From fa554bea10903f6ca5548ef08ac014e89a67c2c9 Mon Sep 17 00:00:00 2001 From: Ankit raj <113342181+ankit-v2-3@users.noreply.github.com> Date: Fri, 5 Apr 2024 08:19:29 +0530 Subject: [PATCH 011/112] feat: add billing --- videodb/_constants.py | 5 +++++ videodb/client.py | 12 ++++++++++++ 2 files changed, 17 insertions(+) diff --git a/videodb/_constants.py b/videodb/_constants.py index b7e0503..010da86 100644 --- a/videodb/_constants.py +++ b/videodb/_constants.py @@ -48,6 +48,11 @@ class ApiPath: workflow = "workflow" timeline = "timeline" delete = "delete" + billing = "billing" + usage = "usage" + checkout = "checkout" + checkouts = "checkouts" + invoices = "invoices" class Status: diff --git a/videodb/client.py b/videodb/client.py index 643807b..85717b9 100644 --- a/videodb/client.py +++ b/videodb/client.py @@ -84,6 +84,18 @@ def update_collection(self, id: str, name: str, description: str) -> Collection: collection_data.get("description"), ) + def check_usage(self) -> dict: + return self.get(path=f"{ApiPath.billing}/{ApiPath.usage}") + + def checkout(self) -> dict: + return self.post(path=f"{ApiPath.billing}/{ApiPath.checkout}") + + def get_invoices(self) -> List[dict]: + return self.get(path=f"{ApiPath.billing}/{ApiPath.invoices}") + + def get_checkouts(self) -> List[dict]: + return self.get(path=f"{ApiPath.billing}/{ApiPath.checkouts}") + def upload( self, file_path: str = None, From 27b02fd2963e4ec558a6ca885797852e80d51bff Mon Sep 17 00:00:00 2001 From: Ankit raj <113342181+ankit-v2-3@users.noreply.github.com> Date: Mon, 8 Apr 2024 14:38:00 +0530 Subject: [PATCH 012/112] feat: add checkout amount --- videodb/__init__.py | 2 +- videodb/client.py | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/videodb/__init__.py b/videodb/__init__.py index e61c91a..1657f43 100644 --- a/videodb/__init__.py +++ b/videodb/__init__.py @@ -24,7 +24,7 @@ logger: logging.Logger = logging.getLogger("videodb") -__version__ = "0.1.1" +__version__ = "0.1.2" __author__ = "videodb" __all__ = [ diff --git a/videodb/client.py b/videodb/client.py index 85717b9..5f75502 100644 --- a/videodb/client.py +++ b/videodb/client.py @@ -87,8 +87,10 @@ def update_collection(self, id: str, name: str, description: str) -> Collection: def check_usage(self) -> dict: return self.get(path=f"{ApiPath.billing}/{ApiPath.usage}") - def checkout(self) -> dict: - return self.post(path=f"{ApiPath.billing}/{ApiPath.checkout}") + def checkout(self, amount=100) -> dict: + return self.post( + path=f"{ApiPath.billing}/{ApiPath.checkout}", data={"amount": amount} + ) def get_invoices(self) -> List[dict]: return self.get(path=f"{ApiPath.billing}/{ApiPath.invoices}") From 7bbe2a1d8ff4c85913bd4c24c44f6ba082146575 Mon Sep 17 00:00:00 2001 From: Ankit raj <113342181+ankit-v2-3@users.noreply.github.com> Date: Sat, 11 May 2024 18:58:50 +0530 Subject: [PATCH 013/112] fix: remove get checkouts --- videodb/client.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/videodb/client.py b/videodb/client.py index 5f75502..e7a5475 100644 --- a/videodb/client.py +++ b/videodb/client.py @@ -95,9 +95,6 @@ def checkout(self, amount=100) -> dict: def get_invoices(self) -> List[dict]: return self.get(path=f"{ApiPath.billing}/{ApiPath.invoices}") - def get_checkouts(self) -> List[dict]: - return self.get(path=f"{ApiPath.billing}/{ApiPath.checkouts}") - def upload( self, file_path: str = None, From c7290b95a1d6291697f6102124fe5a2adb327784 Mon Sep 17 00:00:00 2001 From: ashish-spext Date: Sun, 2 Jun 2024 22:56:22 +0530 Subject: [PATCH 014/112] Language code support added in spoken word index --- videodb/video.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/videodb/video.py b/videodb/video.py index 183a7b9..685f8a5 100644 --- a/videodb/video.py +++ b/videodb/video.py @@ -133,7 +133,12 @@ def get_transcript_text(self, force: bool = False) -> str: self._fetch_transcript(force) return self.transcript_text - def index_spoken_words(self, force: bool = False, callback_url: str = None) -> None: + def index_spoken_words( + self, + language_code: Optional[str] = None, + force: bool = False, + callback_url: str = None, + ) -> None: """Semantic indexing of spoken words in the video :raises InvalidRequestError: If the video is already indexed @@ -144,6 +149,7 @@ def index_spoken_words(self, force: bool = False, callback_url: str = None) -> N path=f"{ApiPath.video}/{self.id}/{ApiPath.index}", data={ "index_type": IndexType.semantic, + "language_code": language_code, "force": force, "callback_url": callback_url, }, From e2484c59fee1f3c8e6bbbfa439d52b12429a2adc Mon Sep 17 00:00:00 2001 From: Ankit raj <113342181+ankit-v2-3@users.noreply.github.com> Date: Thu, 6 Jun 2024 12:50:07 +0530 Subject: [PATCH 015/112] fix: subtitle alignment --- videodb/_constants.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/videodb/_constants.py b/videodb/_constants.py index 010da86..14fc554 100644 --- a/videodb/_constants.py +++ b/videodb/_constants.py @@ -81,12 +81,12 @@ class SubtitleAlignment: bottom_left = 1 bottom_center = 2 bottom_right = 3 - middle_left = 4 - middle_center = 5 - middle_right = 6 - top_left = 7 - top_center = 8 - top_right = 9 + middle_left = 9 + middle_center = 10 + middle_right = 11 + top_left = 5 + top_center = 6 + top_right = 7 @dataclass From 60c1ee174f9a653c7e9ebebe430f0a0a93612486 Mon Sep 17 00:00:00 2001 From: Ankit raj <113342181+ankit-v2-3@users.noreply.github.com> Date: Thu, 6 Jun 2024 13:57:04 +0530 Subject: [PATCH 016/112] fix: remove checkouts --- videodb/_constants.py | 2 -- videodb/client.py | 5 ----- 2 files changed, 7 deletions(-) diff --git a/videodb/_constants.py b/videodb/_constants.py index 14fc554..dd4afcc 100644 --- a/videodb/_constants.py +++ b/videodb/_constants.py @@ -50,8 +50,6 @@ class ApiPath: delete = "delete" billing = "billing" usage = "usage" - checkout = "checkout" - checkouts = "checkouts" invoices = "invoices" diff --git a/videodb/client.py b/videodb/client.py index e7a5475..39bf38b 100644 --- a/videodb/client.py +++ b/videodb/client.py @@ -87,11 +87,6 @@ def update_collection(self, id: str, name: str, description: str) -> Collection: def check_usage(self) -> dict: return self.get(path=f"{ApiPath.billing}/{ApiPath.usage}") - def checkout(self, amount=100) -> dict: - return self.post( - path=f"{ApiPath.billing}/{ApiPath.checkout}", data={"amount": amount} - ) - def get_invoices(self) -> List[dict]: return self.get(path=f"{ApiPath.billing}/{ApiPath.invoices}") From b98a5a3decf2f18a17ae5b5e7d6fa66e36d811dd Mon Sep 17 00:00:00 2001 From: Ankit raj <113342181+ankit-v2-3@users.noreply.github.com> Date: Thu, 6 Jun 2024 14:00:41 +0530 Subject: [PATCH 017/112] fix: remove Checklist --- .github/pull_request_template.md | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index f913b20..24fb4a9 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -15,10 +15,3 @@ Describe the purpose of this pull request. Describe any testing steps that have been taken or are necessary. Make sure to take in account any existing code change that require some feature to be re-tested. - -**Checklist:** -- [ ] Code follows project coding standards -- [ ] Tests have been added or updated -- [ ] Code Review -- [ ] Manual test after merge -- [ ] All checks passed \ No newline at end of file From 0dc72124c8d05f6e6cb54f766d6165539e0b3c71 Mon Sep 17 00:00:00 2001 From: Ankit raj <113342181+ankit-v2-3@users.noreply.github.com> Date: Tue, 11 Jun 2024 19:40:13 +0530 Subject: [PATCH 018/112] feat: add scene scene extractor --- videodb/_constants.py | 7 ++++ videodb/image.py | 32 ++++++++++++++++ videodb/scene.py | 89 +++++++++++++++++++++++++++++++++++++++++++ videodb/video.py | 86 ++++++++++++++++++++++++++++++++++++++++- 4 files changed, 213 insertions(+), 1 deletion(-) create mode 100644 videodb/scene.py diff --git a/videodb/_constants.py b/videodb/_constants.py index dd4afcc..c46f357 100644 --- a/videodb/_constants.py +++ b/videodb/_constants.py @@ -22,6 +22,12 @@ class IndexType: scene = "scene" +class SceneExtractionType: + scene = "scene" + time_based = "time_based" + compression_based = "compression_based" + + class Workflows: add_subtitles = "add_subtitles" @@ -51,6 +57,7 @@ class ApiPath: billing = "billing" usage = "usage" invoices = "invoices" + scenes = "scenes" class Status: diff --git a/videodb/image.py b/videodb/image.py index d7e5e0c..c0a2984 100644 --- a/videodb/image.py +++ b/videodb/image.py @@ -22,3 +22,35 @@ def __repr__(self) -> str: def delete(self) -> None: self._connection.delete(f"{ApiPath.image}/{self.id}") + + +class Frame(Image): + def __init__( + self, + _connection, + id: str, + video_id: str, + scene_id: str, + url: str, + frame_no: int, + frame_time: float, + description: str, + ): + super().__init__(_connection=_connection, id=id, collection_id=None, url=url) + self.scene_id = scene_id + self.video_id = video_id + self.frame_no = frame_no + self.frame_time = frame_time + self.description = description + + def __repr__(self) -> str: + return ( + f"Frame(" + f"id={self.id}, " + f"video_id={self.video_id}, " + f"scene_id={self.scene_id}, " + f"url={self.url}, " + f"frame_no={self.frame_no}, " + f"frame_time={self.frame_time}, " + f"description={self.description})" + ) diff --git a/videodb/scene.py b/videodb/scene.py new file mode 100644 index 0000000..6b29483 --- /dev/null +++ b/videodb/scene.py @@ -0,0 +1,89 @@ +from typing import List + +from videodb._constants import ApiPath + +from videodb.image import Frame + + +class SceneExtractionConfig: + def __init__( + self, + time: int = 5, + threshold: int = 20, + frame_count: int = 1, + select_frame: str = "first", + ): + self.time = time + self.threshold = threshold + self.frame_count = frame_count + self.select_frame = select_frame + + def __repr__(self) -> str: + return ( + f"SceneExtractionConfig(" + f"time={self.time}, " + f"threshold={self.threshold}, " + f"frame_count={self.frame_count}, " + f"select_frame={self.select_frame})" + ) + + +class Scene: + def __init__( + self, + _connection, + id: str, + video_id: str, + start: float, + end: float, + frames: List[Frame], + description: str, + ): + self._connection = _connection + self.id = id + self.video_id = video_id + self.start = start + self.end = end + self.frames: List[Frame] = frames + self.description = description + + def __repr__(self) -> str: + return ( + f"Scene(" + f"id={self.id}, " + f"video_id={self.video_id}, " + f"start={self.start}, " + f"end={self.end}, " + f"frames={self.frames}, " + f"description={self.description})" + ) + + +class SceneCollection: + def __init__( + self, + _connection, + id: str, + video_id: str, + config: SceneExtractionConfig, + scenes: List[Scene], + ) -> None: + self._connection = _connection + self.id = id + self.video_id = video_id + self.config: SceneExtractionConfig = config + self.scenes: List[Scene] = scenes + + def __repr__(self) -> str: + return ( + f"SceneCollection(" + f"id={self.id}, " + f"video_id={self.video_id}, " + f"config={self.config.__dict__}, " + f"scenes={self.scenes})" + ) + + def delete(self) -> None: + self._connection.delete( + path=f"{ApiPath.video}/{self.video_id}/{ApiPath.scenes}/{self.id}" + ) diff --git a/videodb/video.py b/videodb/video.py index 685f8a5..a1d2b89 100644 --- a/videodb/video.py +++ b/videodb/video.py @@ -3,11 +3,13 @@ from videodb._constants import ( ApiPath, IndexType, + SceneExtractionType, SearchType, SubtitleStyle, Workflows, ) -from videodb.image import Image +from videodb.image import Image, Frame +from videodb.scene import SceneExtractionConfig, Scene, SceneCollection from videodb.search import SearchFactory, SearchResult from videodb.shot import Shot @@ -26,6 +28,7 @@ def __init__(self, _connection, id: str, collection_id: str, **kwargs) -> None: self.transcript = kwargs.get("transcript", None) self.transcript_text = kwargs.get("transcript_text", None) self.scenes = kwargs.get("scenes", None) + self.scene_collections = kwargs.get("scene_collections", None) def __repr__(self) -> str: return ( @@ -184,6 +187,87 @@ def get_scenes(self) -> Union[list, None]: self.scenes = scene_data return scene_data if scene_data else None + def _format_scene_collection(self, collection_data: dict) -> SceneCollection: + scenes = [] + for scene in collection_data.get("scenes", []): + frames = [] + for frame in scene.get("frames", []): + frame = Frame( + self._connection, + frame.get("frame_id"), + self.id, + scene.get("scene_id"), + frame.get("url"), + frame.get("frame_no"), + frame.get("frame_time"), + frame.get("description"), + ) + frames.append(frame) + scene = Scene( + self._connection, + scene.get("scene_id"), + self.id, + scene.get("start"), + scene.get("end"), + frames, + scene.get("description"), + ) + scenes.append(scene) + + config = collection_data.get("config", {}) + + return SceneCollection( + self._connection, + collection_data.get("scenes_collection_id"), + self.id, + SceneExtractionConfig( + config.get("time"), + config.get("threshold"), + config.get("frame_count"), + config.get("select_frame"), + ), + scenes, + ) + + def extract_scenes( + self, + extraction_type: SceneExtractionType = SceneExtractionType.scene, + extraction_config: SceneExtractionConfig = SceneExtractionConfig(), + force: bool = False, + callback_url: str = None, + ): + scenes_data = self._connection.post( + path=f"{ApiPath.video}/{self.id}/{ApiPath.scenes}", + data={ + "index_type": IndexType.scene, + "extraction_type": extraction_type, + "extraction_config": extraction_config.__dict__, + "force": force, + "callback_url": callback_url, + }, + ) + return self._format_scene_collection(scenes_data.get("scenes_collection")) + + def get_scene_collection(self, collection_id: str): + scenes_data = self._connection.get( + path=f"{ApiPath.video}/{self.id}/{ApiPath.scenes}/{collection_id}" + ) + return self._format_scene_collection(scenes_data.get("scenes_collection")) + + def get_scene_collections(self): + scene_collections_data = self._connection.get( + path=f"{ApiPath.video}/{self.id}/{ApiPath.scenes}" + ) + scene_collections = [] + for collection in scene_collections_data.get("scenes_collections", []): + scene_collections.append(self._format_scene_collection(collection)) + return scene_collections + + def delete_scene_collection(self, collection_id: str) -> None: + self._connection.delete( + path=f"{ApiPath.video}/{self.id}/{ApiPath.scenes}/{collection_id}" + ) + def delete_scene_index(self) -> None: self._connection.post( path=f"{ApiPath.video}/{self.id}/{ApiPath.index}/{ApiPath.delete}", From 854055ed2808eeb609461f3b8e62f3001f4f9de5 Mon Sep 17 00:00:00 2001 From: Ankit raj <113342181+ankit-v2-3@users.noreply.github.com> Date: Tue, 11 Jun 2024 20:24:32 +0530 Subject: [PATCH 019/112] feat: add create scene index --- videodb/_constants.py | 1 + videodb/image.py | 11 +++++++++++ videodb/scene.py | 12 ++++++++++-- videodb/video.py | 28 +++++++++++++++++++++++++++- 4 files changed, 49 insertions(+), 3 deletions(-) diff --git a/videodb/_constants.py b/videodb/_constants.py index c46f357..bef5b15 100644 --- a/videodb/_constants.py +++ b/videodb/_constants.py @@ -58,6 +58,7 @@ class ApiPath: usage = "usage" invoices = "invoices" scenes = "scenes" + scene = "scene" class Status: diff --git a/videodb/image.py b/videodb/image.py index c0a2984..aef37e4 100644 --- a/videodb/image.py +++ b/videodb/image.py @@ -54,3 +54,14 @@ def __repr__(self) -> str: f"frame_time={self.frame_time}, " f"description={self.description})" ) + + def to_json(self): + return { + "id": self.id, + "video_id": self.video_id, + "scene_id": self.scene_id, + "url": self.url, + "frame_no": self.frame_no, + "frame_time": self.frame_time, + "description": self.description, + } diff --git a/videodb/scene.py b/videodb/scene.py index 6b29483..b8cebf7 100644 --- a/videodb/scene.py +++ b/videodb/scene.py @@ -31,7 +31,6 @@ def __repr__(self) -> str: class Scene: def __init__( self, - _connection, id: str, video_id: str, start: float, @@ -39,7 +38,6 @@ def __init__( frames: List[Frame], description: str, ): - self._connection = _connection self.id = id self.video_id = video_id self.start = start @@ -58,6 +56,16 @@ def __repr__(self) -> str: f"description={self.description})" ) + def to_json(self): + return { + "id": self.id, + "video_id": self.video_id, + "start": self.start, + "end": self.end, + "frames": [frame.to_json() for frame in self.frames], + "description": self.description, + } + class SceneCollection: def __init__( diff --git a/videodb/video.py b/videodb/video.py index a1d2b89..c2834dd 100644 --- a/videodb/video.py +++ b/videodb/video.py @@ -204,7 +204,6 @@ def _format_scene_collection(self, collection_data: dict) -> SceneCollection: ) frames.append(frame) scene = Scene( - self._connection, scene.get("scene_id"), self.id, scene.get("start"), @@ -268,6 +267,33 @@ def delete_scene_collection(self, collection_id: str) -> None: path=f"{ApiPath.video}/{self.id}/{ApiPath.scenes}/{collection_id}" ) + def create_scene_index( + self, scenes: List[Scene], callback_url: str = None + ) -> List[Scene]: + scenes_data = self._connection.post( + path=f"{ApiPath.video}/{self.id}/{ApiPath.index}/{ApiPath.scene}", + data={ + "scenes": [scene.to_json() for scene in scenes], + "callback_url": callback_url, + }, + ) + return [ + Scene( + scene.get("scene_id"), + self.id, + scene.get("start"), + scene.get("end"), + [], + scene.get("description"), + ) + for scene in scenes_data.get("scene_index_records", []) + ] + + def delete_scene_index(self) -> None: + self._connection.delete( + path=f"{ApiPath.video}/{self.id}/{ApiPath.index}/{ApiPath.scene}" + ) + def delete_scene_index(self) -> None: self._connection.post( path=f"{ApiPath.video}/{self.id}/{ApiPath.index}/{ApiPath.delete}", From 9411c696ad0456d6bd26e433d6dcfac89c701eb7 Mon Sep 17 00:00:00 2001 From: Ankit raj <113342181+ankit-v2-3@users.noreply.github.com> Date: Thu, 13 Jun 2024 10:21:00 +0530 Subject: [PATCH 020/112] fix: response data --- videodb/video.py | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/videodb/video.py b/videodb/video.py index c2834dd..3b6bd06 100644 --- a/videodb/video.py +++ b/videodb/video.py @@ -257,10 +257,7 @@ def get_scene_collections(self): scene_collections_data = self._connection.get( path=f"{ApiPath.video}/{self.id}/{ApiPath.scenes}" ) - scene_collections = [] - for collection in scene_collections_data.get("scenes_collections", []): - scene_collections.append(self._format_scene_collection(collection)) - return scene_collections + return scene_collections_data.get("scenes_collections", []) def delete_scene_collection(self, collection_id: str) -> None: self._connection.delete( @@ -289,12 +286,25 @@ def create_scene_index( for scene in scenes_data.get("scene_index_records", []) ] + def get_scene_indexes(self) -> List: + index_data = self._connection.get( + path=f"{ApiPath.video}/{self.id}/{ApiPath.index}/{ApiPath.scene}" + ) + + return index_data.get("scene_indexes", []) + + def get_scene_index(self, scene_index_id: str) -> Scene: + index_data = self._connection.get( + path=f"{ApiPath.video}/{self.id}/{ApiPath.index}/{ApiPath.scene}/{scene_index_id}" + ) + return index_data.get("scene_index_records", []) + def delete_scene_index(self) -> None: self._connection.delete( path=f"{ApiPath.video}/{self.id}/{ApiPath.index}/{ApiPath.scene}" ) - def delete_scene_index(self) -> None: + def delete_index(self) -> None: self._connection.post( path=f"{ApiPath.video}/{self.id}/{ApiPath.index}/{ApiPath.delete}", data={ From e25bb07aab2f2cf43790edce694963a4f21df118 Mon Sep 17 00:00:00 2001 From: Ankit raj <113342181+ankit-v2-3@users.noreply.github.com> Date: Thu, 13 Jun 2024 10:35:21 +0530 Subject: [PATCH 021/112] fix: create_scene_index --- videodb/video.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/videodb/video.py b/videodb/video.py index 3b6bd06..15f081e 100644 --- a/videodb/video.py +++ b/videodb/video.py @@ -265,12 +265,20 @@ def delete_scene_collection(self, collection_id: str) -> None: ) def create_scene_index( - self, scenes: List[Scene], callback_url: str = None + self, + scenes: List[Scene] = None, + extraction_type: SceneExtractionType = None, + extraction_config: SceneExtractionConfig = None, + force: bool = False, + callback_url: str = None, ) -> List[Scene]: scenes_data = self._connection.post( path=f"{ApiPath.video}/{self.id}/{ApiPath.index}/{ApiPath.scene}", data={ "scenes": [scene.to_json() for scene in scenes], + "extraction_type": extraction_type, + "extraction_config": extraction_config.__dict__, + "force": force, "callback_url": callback_url, }, ) From 9f261a001bf16b74c9840b2d57d3af2c736323e2 Mon Sep 17 00:00:00 2001 From: Ankit raj <113342181+ankit-v2-3@users.noreply.github.com> Date: Thu, 13 Jun 2024 10:37:51 +0530 Subject: [PATCH 022/112] fix: create_scene_index --- videodb/video.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/videodb/video.py b/videodb/video.py index 15f081e..becbc4b 100644 --- a/videodb/video.py +++ b/videodb/video.py @@ -266,9 +266,9 @@ def delete_scene_collection(self, collection_id: str) -> None: def create_scene_index( self, + extraction_type: SceneExtractionType = SceneExtractionType.scene, + extraction_config: SceneExtractionConfig = SceneExtractionConfig(), scenes: List[Scene] = None, - extraction_type: SceneExtractionType = None, - extraction_config: SceneExtractionConfig = None, force: bool = False, callback_url: str = None, ) -> List[Scene]: From 08b6d5f6add718b87ac9185be900fab7c3ed69cc Mon Sep 17 00:00:00 2001 From: Ankit raj <113342181+ankit-v2-3@users.noreply.github.com> Date: Thu, 13 Jun 2024 10:50:47 +0530 Subject: [PATCH 023/112] fix: scene index --- videodb/video.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/videodb/video.py b/videodb/video.py index becbc4b..76864a8 100644 --- a/videodb/video.py +++ b/videodb/video.py @@ -268,7 +268,7 @@ def create_scene_index( self, extraction_type: SceneExtractionType = SceneExtractionType.scene, extraction_config: SceneExtractionConfig = SceneExtractionConfig(), - scenes: List[Scene] = None, + scenes: List[Scene] = [], force: bool = False, callback_url: str = None, ) -> List[Scene]: @@ -307,12 +307,12 @@ def get_scene_index(self, scene_index_id: str) -> Scene: ) return index_data.get("scene_index_records", []) - def delete_scene_index(self) -> None: + def delete_index(self, scene_index_id: str) -> None: self._connection.delete( - path=f"{ApiPath.video}/{self.id}/{ApiPath.index}/{ApiPath.scene}" + path=f"{ApiPath.video}/{self.id}/{ApiPath.index}/{ApiPath.scene}/{scene_index_id}" ) - def delete_index(self) -> None: + def delete_scene_index(self) -> None: self._connection.post( path=f"{ApiPath.video}/{self.id}/{ApiPath.index}/{ApiPath.delete}", data={ From ba4f07ec80a02a150b7a5c7a9fbc8dac73dce9cc Mon Sep 17 00:00:00 2001 From: Ankit raj <113342181+ankit-v2-3@users.noreply.github.com> Date: Thu, 13 Jun 2024 15:35:38 +0530 Subject: [PATCH 024/112] fix: remove SceneExtractionConfig --- videodb/scene.py | 29 +++-------------------------- videodb/video.py | 19 ++++++------------- 2 files changed, 9 insertions(+), 39 deletions(-) diff --git a/videodb/scene.py b/videodb/scene.py index b8cebf7..26b9cff 100644 --- a/videodb/scene.py +++ b/videodb/scene.py @@ -5,29 +5,6 @@ from videodb.image import Frame -class SceneExtractionConfig: - def __init__( - self, - time: int = 5, - threshold: int = 20, - frame_count: int = 1, - select_frame: str = "first", - ): - self.time = time - self.threshold = threshold - self.frame_count = frame_count - self.select_frame = select_frame - - def __repr__(self) -> str: - return ( - f"SceneExtractionConfig(" - f"time={self.time}, " - f"threshold={self.threshold}, " - f"frame_count={self.frame_count}, " - f"select_frame={self.select_frame})" - ) - - class Scene: def __init__( self, @@ -73,13 +50,13 @@ def __init__( _connection, id: str, video_id: str, - config: SceneExtractionConfig, + config: dict, scenes: List[Scene], ) -> None: self._connection = _connection self.id = id self.video_id = video_id - self.config: SceneExtractionConfig = config + self.config: dict = config self.scenes: List[Scene] = scenes def __repr__(self) -> str: @@ -87,7 +64,7 @@ def __repr__(self) -> str: f"SceneCollection(" f"id={self.id}, " f"video_id={self.video_id}, " - f"config={self.config.__dict__}, " + f"config={self.config}, " f"scenes={self.scenes})" ) diff --git a/videodb/video.py b/videodb/video.py index 76864a8..4a282b9 100644 --- a/videodb/video.py +++ b/videodb/video.py @@ -9,7 +9,7 @@ Workflows, ) from videodb.image import Image, Frame -from videodb.scene import SceneExtractionConfig, Scene, SceneCollection +from videodb.scene import Scene, SceneCollection from videodb.search import SearchFactory, SearchResult from videodb.shot import Shot @@ -213,25 +213,18 @@ def _format_scene_collection(self, collection_data: dict) -> SceneCollection: ) scenes.append(scene) - config = collection_data.get("config", {}) - return SceneCollection( self._connection, collection_data.get("scenes_collection_id"), self.id, - SceneExtractionConfig( - config.get("time"), - config.get("threshold"), - config.get("frame_count"), - config.get("select_frame"), - ), + collection_data.get("config", {}), scenes, ) def extract_scenes( self, extraction_type: SceneExtractionType = SceneExtractionType.scene, - extraction_config: SceneExtractionConfig = SceneExtractionConfig(), + extraction_config: dict = {}, force: bool = False, callback_url: str = None, ): @@ -240,7 +233,7 @@ def extract_scenes( data={ "index_type": IndexType.scene, "extraction_type": extraction_type, - "extraction_config": extraction_config.__dict__, + "extraction_config": extraction_config, "force": force, "callback_url": callback_url, }, @@ -267,7 +260,7 @@ def delete_scene_collection(self, collection_id: str) -> None: def create_scene_index( self, extraction_type: SceneExtractionType = SceneExtractionType.scene, - extraction_config: SceneExtractionConfig = SceneExtractionConfig(), + extraction_config: dict = {}, scenes: List[Scene] = [], force: bool = False, callback_url: str = None, @@ -277,7 +270,7 @@ def create_scene_index( data={ "scenes": [scene.to_json() for scene in scenes], "extraction_type": extraction_type, - "extraction_config": extraction_config.__dict__, + "extraction_config": extraction_config, "force": force, "callback_url": callback_url, }, From 577c7788c0259bbf5e9552b892453be74404f990 Mon Sep 17 00:00:00 2001 From: Ankit raj <113342181+ankit-v2-3@users.noreply.github.com> Date: Thu, 13 Jun 2024 16:31:00 +0530 Subject: [PATCH 025/112] feat: add timeout exception --- videodb/_utils/_http_client.py | 5 +++-- videodb/exceptions.py | 10 ++++++++++ videodb/video.py | 12 +----------- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/videodb/_utils/_http_client.py b/videodb/_utils/_http_client.py index 35eb5c5..0b4ae80 100644 --- a/videodb/_utils/_http_client.py +++ b/videodb/_utils/_http_client.py @@ -19,6 +19,7 @@ from videodb.exceptions import ( AuthenticationError, InvalidRequestError, + RequestTimeoutError, ) logger = logging.getLogger(__name__) @@ -109,8 +110,8 @@ def _handle_request_error(self, e: requests.exceptions.RequestException) -> None ) from None elif isinstance(e, requests.exceptions.Timeout): - raise InvalidRequestError( - "Invalid request: Request timed out", e.response + raise RequestTimeoutError( + "Timeout error: Request timed out", e.response ) from None elif isinstance(e, requests.exceptions.ConnectionError): diff --git a/videodb/exceptions.py b/videodb/exceptions.py index 16749b4..2b70008 100644 --- a/videodb/exceptions.py +++ b/videodb/exceptions.py @@ -37,6 +37,16 @@ def __init__(self, message, response=None): self.response = response +class RequestTimeoutError(VideodbError): + """ + Raised when a request times out. + """ + + def __init__(self, message, response=None): + super(RequestTimeoutError, self).__init__(message) + self.response = response + + class SearchError(VideodbError): """ Raised when a search is invalid. diff --git a/videodb/video.py b/videodb/video.py index 4a282b9..469c526 100644 --- a/videodb/video.py +++ b/videodb/video.py @@ -275,17 +275,7 @@ def create_scene_index( "callback_url": callback_url, }, ) - return [ - Scene( - scene.get("scene_id"), - self.id, - scene.get("start"), - scene.get("end"), - [], - scene.get("description"), - ) - for scene in scenes_data.get("scene_index_records", []) - ] + return scenes_data.get("scene_index_records", []) def get_scene_indexes(self) -> List: index_data = self._connection.get( From 55fe2fd83bc1a2b89b54dd8bfa8d639b631453fd Mon Sep 17 00:00:00 2001 From: Ankit raj <113342181+ankit-v2-3@users.noreply.github.com> Date: Thu, 13 Jun 2024 19:25:56 +0530 Subject: [PATCH 026/112] feat: add **kwargs in search --- videodb/search.py | 1 + videodb/video.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/videodb/search.py b/videodb/search.py index 81ff27b..cab4b6d 100644 --- a/videodb/search.py +++ b/videodb/search.py @@ -198,6 +198,7 @@ def search_inside_video( "query": query, "score_threshold": score_threshold, "result_threshold": result_threshold, + **kwargs, }, ) return SearchResult(self._connection, **search_data) diff --git a/videodb/video.py b/videodb/video.py index 469c526..06401fb 100644 --- a/videodb/video.py +++ b/videodb/video.py @@ -53,6 +53,7 @@ def search( result_threshold: Optional[int] = None, score_threshold: Optional[int] = None, dynamic_score_percentage: Optional[int] = None, + **kwargs, ) -> SearchResult: search = SearchFactory(self._connection).get_search(search_type) return search.search_inside_video( @@ -61,6 +62,7 @@ def search( result_threshold=result_threshold, score_threshold=score_threshold, dynamic_score_percentage=dynamic_score_percentage, + **kwargs, ) def delete(self) -> None: From c4a922e023617671804ca77deff052bca20d5786 Mon Sep 17 00:00:00 2001 From: Ankit raj <113342181+ankit-v2-3@users.noreply.github.com> Date: Fri, 14 Jun 2024 11:54:00 +0530 Subject: [PATCH 027/112] fix: scene and frame class --- videodb/image.py | 4 ---- videodb/scene.py | 4 ++-- videodb/video.py | 13 ++++++------- 3 files changed, 8 insertions(+), 13 deletions(-) diff --git a/videodb/image.py b/videodb/image.py index aef37e4..c1bd2ef 100644 --- a/videodb/image.py +++ b/videodb/image.py @@ -32,14 +32,12 @@ def __init__( video_id: str, scene_id: str, url: str, - frame_no: int, frame_time: float, description: str, ): super().__init__(_connection=_connection, id=id, collection_id=None, url=url) self.scene_id = scene_id self.video_id = video_id - self.frame_no = frame_no self.frame_time = frame_time self.description = description @@ -50,7 +48,6 @@ def __repr__(self) -> str: f"video_id={self.video_id}, " f"scene_id={self.scene_id}, " f"url={self.url}, " - f"frame_no={self.frame_no}, " f"frame_time={self.frame_time}, " f"description={self.description})" ) @@ -61,7 +58,6 @@ def to_json(self): "video_id": self.video_id, "scene_id": self.scene_id, "url": self.url, - "frame_no": self.frame_no, "frame_time": self.frame_time, "description": self.description, } diff --git a/videodb/scene.py b/videodb/scene.py index 26b9cff..182bfe8 100644 --- a/videodb/scene.py +++ b/videodb/scene.py @@ -8,12 +8,12 @@ class Scene: def __init__( self, - id: str, video_id: str, start: float, end: float, - frames: List[Frame], description: str, + id: str = None, + frames: List[Frame] = [], ): self.id = id self.video_id = video_id diff --git a/videodb/video.py b/videodb/video.py index 06401fb..219022c 100644 --- a/videodb/video.py +++ b/videodb/video.py @@ -200,18 +200,17 @@ def _format_scene_collection(self, collection_data: dict) -> SceneCollection: self.id, scene.get("scene_id"), frame.get("url"), - frame.get("frame_no"), frame.get("frame_time"), frame.get("description"), ) frames.append(frame) scene = Scene( - scene.get("scene_id"), - self.id, - scene.get("start"), - scene.get("end"), - frames, - scene.get("description"), + video_id=self.id, + start=scene.get("start"), + end=scene.get("end"), + description=scene.get("description"), + id=scene.get("scene_id"), + frames=frames, ) scenes.append(scene) From 7036ece226726a9897ff9a5eb42aa755ffc7d62a Mon Sep 17 00:00:00 2001 From: ashish-spext Date: Fri, 14 Jun 2024 15:15:34 +0530 Subject: [PATCH 028/112] Index Interface Updates - Deleted old scene_index and replaced with renaming new create_scene_index - Added missing prompt - Synced name of scene_collection[s] with server - Replaced delete_scene_index with renaming new delete_index --- videodb/video.py | 40 ++++++++-------------------------------- 1 file changed, 8 insertions(+), 32 deletions(-) diff --git a/videodb/video.py b/videodb/video.py index 219022c..facb2bc 100644 --- a/videodb/video.py +++ b/videodb/video.py @@ -161,22 +161,6 @@ def index_spoken_words( show_progress=True, ) - def index_scenes( - self, - force: bool = False, - prompt: str = None, - callback_url: str = None, - ) -> None: - self._connection.post( - path=f"{ApiPath.video}/{self.id}/{ApiPath.index}", - data={ - "index_type": IndexType.scene, - "force": force, - "prompt": prompt, - "callback_url": callback_url, - }, - ) - def get_scenes(self) -> Union[list, None]: if self.scenes: return self.scenes @@ -216,7 +200,7 @@ def _format_scene_collection(self, collection_data: dict) -> SceneCollection: return SceneCollection( self._connection, - collection_data.get("scenes_collection_id"), + collection_data.get("scene_collection_id"), self.id, collection_data.get("config", {}), scenes, @@ -232,36 +216,36 @@ def extract_scenes( scenes_data = self._connection.post( path=f"{ApiPath.video}/{self.id}/{ApiPath.scenes}", data={ - "index_type": IndexType.scene, "extraction_type": extraction_type, "extraction_config": extraction_config, "force": force, "callback_url": callback_url, }, ) - return self._format_scene_collection(scenes_data.get("scenes_collection")) + return self._format_scene_collection(scenes_data.get("scene_collection")) def get_scene_collection(self, collection_id: str): scenes_data = self._connection.get( path=f"{ApiPath.video}/{self.id}/{ApiPath.scenes}/{collection_id}" ) - return self._format_scene_collection(scenes_data.get("scenes_collection")) + return self._format_scene_collection(scenes_data.get("scene_collection")) def get_scene_collections(self): scene_collections_data = self._connection.get( path=f"{ApiPath.video}/{self.id}/{ApiPath.scenes}" ) - return scene_collections_data.get("scenes_collections", []) + return scene_collections_data.get("scene_collections", []) def delete_scene_collection(self, collection_id: str) -> None: self._connection.delete( path=f"{ApiPath.video}/{self.id}/{ApiPath.scenes}/{collection_id}" ) - def create_scene_index( + def index_scenes( self, extraction_type: SceneExtractionType = SceneExtractionType.scene, extraction_config: dict = {}, + prompt: str = None, scenes: List[Scene] = [], force: bool = False, callback_url: str = None, @@ -272,6 +256,7 @@ def create_scene_index( "scenes": [scene.to_json() for scene in scenes], "extraction_type": extraction_type, "extraction_config": extraction_config, + "prompt": prompt, "force": force, "callback_url": callback_url, }, @@ -291,20 +276,11 @@ def get_scene_index(self, scene_index_id: str) -> Scene: ) return index_data.get("scene_index_records", []) - def delete_index(self, scene_index_id: str) -> None: + def delete_scene_index(self, scene_index_id: str) -> None: self._connection.delete( path=f"{ApiPath.video}/{self.id}/{ApiPath.index}/{ApiPath.scene}/{scene_index_id}" ) - def delete_scene_index(self) -> None: - self._connection.post( - path=f"{ApiPath.video}/{self.id}/{ApiPath.index}/{ApiPath.delete}", - data={ - "index_type": IndexType.scene, - }, - ) - self.scenes = None - def add_subtitle(self, style: SubtitleStyle = SubtitleStyle()) -> str: if not isinstance(style, SubtitleStyle): raise ValueError("style must be of type SubtitleStyle") From cc0cbefbc1d8d99df1c7b6cb483ad0262fbe771f Mon Sep 17 00:00:00 2001 From: Ankit raj <113342181+ankit-v2-3@users.noreply.github.com> Date: Fri, 14 Jun 2024 15:51:32 +0530 Subject: [PATCH 029/112] fix: SceneExtractionType --- videodb/__init__.py | 2 ++ videodb/_constants.py | 5 ++--- videodb/video.py | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/videodb/__init__.py b/videodb/__init__.py index 1657f43..22fbad6 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, + SceneExtractionType, MediaType, SearchType, SubtitleAlignment, @@ -39,6 +40,7 @@ "SubtitleBorderStyle", "SubtitleStyle", "TextStyle", + "SceneExtractionType", ] diff --git a/videodb/_constants.py b/videodb/_constants.py index bef5b15..e0ed017 100644 --- a/videodb/_constants.py +++ b/videodb/_constants.py @@ -23,9 +23,8 @@ class IndexType: class SceneExtractionType: - scene = "scene" - time_based = "time_based" - compression_based = "compression_based" + scene_based = "scene" + time_based = "time" class Workflows: diff --git a/videodb/video.py b/videodb/video.py index facb2bc..b69de4e 100644 --- a/videodb/video.py +++ b/videodb/video.py @@ -208,7 +208,7 @@ def _format_scene_collection(self, collection_data: dict) -> SceneCollection: def extract_scenes( self, - extraction_type: SceneExtractionType = SceneExtractionType.scene, + extraction_type: SceneExtractionType = SceneExtractionType.scene_based, extraction_config: dict = {}, force: bool = False, callback_url: str = None, @@ -243,7 +243,7 @@ def delete_scene_collection(self, collection_id: str) -> None: def index_scenes( self, - extraction_type: SceneExtractionType = SceneExtractionType.scene, + extraction_type: SceneExtractionType = SceneExtractionType.scene_based, extraction_config: dict = {}, prompt: str = None, scenes: List[Scene] = [], From b1110e9d620f504ce254855c09a58927152f64d6 Mon Sep 17 00:00:00 2001 From: Ankit raj <113342181+ankit-v2-3@users.noreply.github.com> Date: Mon, 17 Jun 2024 15:41:08 +0530 Subject: [PATCH 030/112] fix: semantic search --- videodb/collection.py | 4 ++-- videodb/search.py | 21 +++++++++++++-------- videodb/video.py | 13 ++++++------- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/videodb/collection.py b/videodb/collection.py index fcafa4d..040979f 100644 --- a/videodb/collection.py +++ b/videodb/collection.py @@ -101,8 +101,8 @@ def search( query: str, search_type: Optional[str] = SearchType.semantic, result_threshold: Optional[int] = None, - score_threshold: Optional[int] = None, - dynamic_score_percentage: Optional[int] = None, + score_threshold: Optional[float] = None, + dynamic_score_percentage: Optional[float] = None, ) -> SearchResult: search = SearchFactory(self._connection).get_search(search_type) return search.search_inside_collection( diff --git a/videodb/search.py b/videodb/search.py index cab4b6d..e9e6942 100644 --- a/videodb/search.py +++ b/videodb/search.py @@ -110,8 +110,8 @@ def search_inside_video( video_id: str, query: str, result_threshold: Optional[int] = None, - score_threshold: Optional[int] = None, - dynamic_score_percentage: Optional[int] = None, + score_threshold: Optional[float] = None, + dynamic_score_percentage: Optional[float] = None, **kwargs, ): search_data = self._connection.post( @@ -123,6 +123,8 @@ def search_inside_video( or SemanticSearchDefaultValues.score_threshold, "result_threshold": result_threshold or SemanticSearchDefaultValues.result_threshold, + "dynamic_score_percentage": dynamic_score_percentage, + **kwargs, }, ) return SearchResult(self._connection, **search_data) @@ -132,8 +134,8 @@ def search_inside_collection( collection_id: str, query: str, result_threshold: Optional[int] = None, - score_threshold: Optional[int] = None, - dynamic_score_percentage: Optional[int] = None, + score_threshold: Optional[float] = None, + dynamic_score_percentage: Optional[float] = None, **kwargs, ): search_data = self._connection.post( @@ -145,6 +147,8 @@ def search_inside_collection( or SemanticSearchDefaultValues.score_threshold, "result_threshold": result_threshold or SemanticSearchDefaultValues.result_threshold, + "dynamic_score_percentage": dynamic_score_percentage, + **kwargs, }, ) return SearchResult(self._connection, **search_data) @@ -159,8 +163,8 @@ def search_inside_video( video_id: str, query: str, result_threshold: Optional[int] = None, - score_threshold: Optional[int] = None, - dynamic_score_percentage: Optional[int] = None, + score_threshold: Optional[float] = None, + dynamic_score_percentage: Optional[float] = None, **kwargs, ): search_data = self._connection.post( @@ -187,8 +191,8 @@ def search_inside_video( video_id: str, query: str, result_threshold: Optional[int] = None, - score_threshold: Optional[int] = None, - dynamic_score_percentage: Optional[int] = None, + score_threshold: Optional[float] = None, + dynamic_score_percentage: Optional[float] = None, **kwargs, ): search_data = self._connection.post( @@ -198,6 +202,7 @@ def search_inside_video( "query": query, "score_threshold": score_threshold, "result_threshold": result_threshold, + "dynamic_score_percentage": dynamic_score_percentage, **kwargs, }, ) diff --git a/videodb/video.py b/videodb/video.py index b69de4e..1331b97 100644 --- a/videodb/video.py +++ b/videodb/video.py @@ -28,7 +28,6 @@ def __init__(self, _connection, id: str, collection_id: str, **kwargs) -> None: self.transcript = kwargs.get("transcript", None) self.transcript_text = kwargs.get("transcript_text", None) self.scenes = kwargs.get("scenes", None) - self.scene_collections = kwargs.get("scene_collections", None) def __repr__(self) -> str: return ( @@ -51,8 +50,8 @@ def search( query: str, search_type: Optional[str] = SearchType.semantic, result_threshold: Optional[int] = None, - score_threshold: Optional[int] = None, - dynamic_score_percentage: Optional[int] = None, + score_threshold: Optional[float] = None, + dynamic_score_percentage: Optional[float] = None, **kwargs, ) -> SearchResult: search = SearchFactory(self._connection).get_search(search_type) @@ -173,9 +172,9 @@ def get_scenes(self) -> Union[list, None]: self.scenes = scene_data return scene_data if scene_data else None - def _format_scene_collection(self, collection_data: dict) -> SceneCollection: + def _format_scene_collection(self, scene_collection_data: dict) -> SceneCollection: scenes = [] - for scene in collection_data.get("scenes", []): + for scene in scene_collection_data.get("scenes", []): frames = [] for frame in scene.get("frames", []): frame = Frame( @@ -200,9 +199,9 @@ def _format_scene_collection(self, collection_data: dict) -> SceneCollection: return SceneCollection( self._connection, - collection_data.get("scene_collection_id"), + scene_collection_data.get("scene_collection_id"), self.id, - collection_data.get("config", {}), + scene_collection_data.get("config", {}), scenes, ) From ed56178292762eba85f275ea7dedee3b19473c1d Mon Sep 17 00:00:00 2001 From: Ankit raj <113342181+ankit-v2-3@users.noreply.github.com> Date: Tue, 18 Jun 2024 18:25:30 +0530 Subject: [PATCH 031/112] feat: return Scene object --- videodb/scene.py | 3 +++ videodb/video.py | 34 ++++++++++++++++++++++++++++++---- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/videodb/scene.py b/videodb/scene.py index 182bfe8..03086c0 100644 --- a/videodb/scene.py +++ b/videodb/scene.py @@ -13,6 +13,7 @@ def __init__( end: float, description: str, id: str = None, + index_id: str = None, frames: List[Frame] = [], ): self.id = id @@ -20,6 +21,7 @@ def __init__( self.start = start self.end = end self.frames: List[Frame] = frames + self.index_id = index_id self.description = description def __repr__(self) -> str: @@ -30,6 +32,7 @@ def __repr__(self) -> str: f"start={self.start}, " f"end={self.end}, " f"frames={self.frames}, " + f"index_id={self.index_id}, " f"description={self.description})" ) diff --git a/videodb/video.py b/videodb/video.py index 1331b97..5889443 100644 --- a/videodb/video.py +++ b/videodb/video.py @@ -194,6 +194,7 @@ def _format_scene_collection(self, scene_collection_data: dict) -> SceneCollecti description=scene.get("description"), id=scene.get("scene_id"), frames=frames, + index_id=scene.get("index_id"), ) scenes.append(scene) @@ -221,6 +222,8 @@ def extract_scenes( "callback_url": callback_url, }, ) + if not scenes_data: + return None return self._format_scene_collection(scenes_data.get("scene_collection")) def get_scene_collection(self, collection_id: str): @@ -248,7 +251,7 @@ def index_scenes( scenes: List[Scene] = [], force: bool = False, callback_url: str = None, - ) -> List[Scene]: + ) -> List[Scene] or None: scenes_data = self._connection.post( path=f"{ApiPath.video}/{self.id}/{ApiPath.index}/{ApiPath.scene}", data={ @@ -260,7 +263,18 @@ def index_scenes( "callback_url": callback_url, }, ) - return scenes_data.get("scene_index_records", []) + if not scenes_data: + return None + return [ + Scene( + video_id=self.id, + start=scene.get("start"), + end=scene.get("end"), + index_id=scene.get("scene_index_id"), + description=scene.get("description"), + ) + for scene in scenes_data.get("scene_index_records", []) + ] def get_scene_indexes(self) -> List: index_data = self._connection.get( @@ -269,11 +283,23 @@ def get_scene_indexes(self) -> List: return index_data.get("scene_indexes", []) - def get_scene_index(self, scene_index_id: str) -> Scene: + def get_scene_index(self, scene_index_id: str) -> List[Scene] or None: index_data = self._connection.get( path=f"{ApiPath.video}/{self.id}/{ApiPath.index}/{ApiPath.scene}/{scene_index_id}" ) - return index_data.get("scene_index_records", []) + index_records = index_data.get("scene_index_records", []) + if not index_records: + return None + return [ + Scene( + video_id=self.id, + start=scene.get("start"), + end=scene.get("end"), + index_id=scene.get("scene_index_id"), + description=scene.get("description"), + ) + for scene in index_records + ] def delete_scene_index(self, scene_index_id: str) -> None: self._connection.delete( From 09df3559f20093f566c0e518b43b057e90e9a15a Mon Sep 17 00:00:00 2001 From: Ankit raj <113342181+ankit-v2-3@users.noreply.github.com> Date: Wed, 19 Jun 2024 17:48:54 +0530 Subject: [PATCH 032/112] refactor: scene class, method names --- videodb/scene.py | 3 --- videodb/video.py | 17 +++-------------- 2 files changed, 3 insertions(+), 17 deletions(-) diff --git a/videodb/scene.py b/videodb/scene.py index 03086c0..182bfe8 100644 --- a/videodb/scene.py +++ b/videodb/scene.py @@ -13,7 +13,6 @@ def __init__( end: float, description: str, id: str = None, - index_id: str = None, frames: List[Frame] = [], ): self.id = id @@ -21,7 +20,6 @@ def __init__( self.start = start self.end = end self.frames: List[Frame] = frames - self.index_id = index_id self.description = description def __repr__(self) -> str: @@ -32,7 +30,6 @@ def __repr__(self) -> str: f"start={self.start}, " f"end={self.end}, " f"frames={self.frames}, " - f"index_id={self.index_id}, " f"description={self.description})" ) diff --git a/videodb/video.py b/videodb/video.py index 5889443..dad299f 100644 --- a/videodb/video.py +++ b/videodb/video.py @@ -194,7 +194,6 @@ def _format_scene_collection(self, scene_collection_data: dict) -> SceneCollecti description=scene.get("description"), id=scene.get("scene_id"), frames=frames, - index_id=scene.get("index_id"), ) scenes.append(scene) @@ -232,7 +231,7 @@ def get_scene_collection(self, collection_id: str): ) return self._format_scene_collection(scenes_data.get("scene_collection")) - def get_scene_collections(self): + def list_scene_collection(self): scene_collections_data = self._connection.get( path=f"{ApiPath.video}/{self.id}/{ApiPath.scenes}" ) @@ -265,18 +264,9 @@ def index_scenes( ) if not scenes_data: return None - return [ - Scene( - video_id=self.id, - start=scene.get("start"), - end=scene.get("end"), - index_id=scene.get("scene_index_id"), - description=scene.get("description"), - ) - for scene in scenes_data.get("scene_index_records", []) - ] + return scenes_data.get("scene_index_records", []) - def get_scene_indexes(self) -> List: + def list_scene_index(self) -> List: index_data = self._connection.get( path=f"{ApiPath.video}/{self.id}/{ApiPath.index}/{ApiPath.scene}" ) @@ -295,7 +285,6 @@ def get_scene_index(self, scene_index_id: str) -> List[Scene] or None: video_id=self.id, start=scene.get("start"), end=scene.get("end"), - index_id=scene.get("scene_index_id"), description=scene.get("description"), ) for scene in index_records From 892b6c97d02eb78ee28a44b610ad932927a94017 Mon Sep 17 00:00:00 2001 From: Ankit raj <113342181+ankit-v2-3@users.noreply.github.com> Date: Thu, 20 Jun 2024 15:02:46 +0530 Subject: [PATCH 033/112] fix: get scene index --- videodb/video.py | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/videodb/video.py b/videodb/video.py index dad299f..0360637 100644 --- a/videodb/video.py +++ b/videodb/video.py @@ -277,18 +277,9 @@ def get_scene_index(self, scene_index_id: str) -> List[Scene] or None: index_data = self._connection.get( path=f"{ApiPath.video}/{self.id}/{ApiPath.index}/{ApiPath.scene}/{scene_index_id}" ) - index_records = index_data.get("scene_index_records", []) - if not index_records: + if not index_data: return None - return [ - Scene( - video_id=self.id, - start=scene.get("start"), - end=scene.get("end"), - description=scene.get("description"), - ) - for scene in index_records - ] + return index_data.get("scene_index_records", []) def delete_scene_index(self, scene_index_id: str) -> None: self._connection.delete( From c0a7f70cecb3f9cbf438552532ac1d71fa64241e Mon Sep 17 00:00:00 2001 From: Ankit raj <113342181+ankit-v2-3@users.noreply.github.com> Date: Thu, 20 Jun 2024 15:58:07 +0530 Subject: [PATCH 034/112] fix: index scenes --- videodb/video.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/videodb/video.py b/videodb/video.py index 0360637..0b277c7 100644 --- a/videodb/video.py +++ b/videodb/video.py @@ -245,20 +245,26 @@ def delete_scene_collection(self, collection_id: str) -> None: def index_scenes( self, extraction_type: SceneExtractionType = SceneExtractionType.scene_based, - extraction_config: dict = {}, - prompt: str = None, - scenes: List[Scene] = [], - force: bool = False, - callback_url: str = None, - ) -> List[Scene] or None: + extraction_config: Dict = {}, + prompt: Optional[str] = None, + model: Optional[str] = None, + model_config: Optional[Dict] = None, + name: Optional[str] = None, + scenes: Optional[List[Scene]] = None, + force: Optional[bool] = False, + callback_url: Optional[str] = None, + ) -> Optional[List]: scenes_data = self._connection.post( path=f"{ApiPath.video}/{self.id}/{ApiPath.index}/{ApiPath.scene}", data={ - "scenes": [scene.to_json() for scene in scenes], "extraction_type": extraction_type, "extraction_config": extraction_config, "prompt": prompt, + "model": model, + "model_config": model_config, + "name": name, "force": force, + "scenes": [scene.to_json() for scene in scenes] if scenes else None, "callback_url": callback_url, }, ) @@ -273,7 +279,7 @@ def list_scene_index(self) -> List: return index_data.get("scene_indexes", []) - def get_scene_index(self, scene_index_id: str) -> List[Scene] or None: + def get_scene_index(self, scene_index_id: str) -> Optional[List]: index_data = self._connection.get( path=f"{ApiPath.video}/{self.id}/{ApiPath.index}/{ApiPath.scene}/{scene_index_id}" ) From cff83a2e5db6c08d963d95645cda4a1c6297559c Mon Sep 17 00:00:00 2001 From: Ankit raj <113342181+ankit-v2-3@users.noreply.github.com> Date: Fri, 21 Jun 2024 15:34:13 +0530 Subject: [PATCH 035/112] build: add client header --- setup.py | 22 +++++++++++----------- videodb/__about__.py | 8 ++++++++ videodb/__init__.py | 1 + videodb/_utils/_http_client.py | 8 +++++++- videodb/client.py | 4 ++-- videodb/video.py | 1 - 6 files changed, 29 insertions(+), 15 deletions(-) create mode 100644 videodb/__about__.py diff --git a/setup.py b/setup.py index bdb8b05..b97aad2 100644 --- a/setup.py +++ b/setup.py @@ -2,16 +2,16 @@ import os from setuptools import setup, find_packages -ROOT = os.path.dirname(__file__) +ROOT = os.path.dirname(os.path.abspath(__file__)) # Read in the package version per recommendations from: # https://packaging.python.org/guides/single-sourcing-package-version/ -def get_version(): - with open(os.path.join(ROOT, "videodb", "__init__.py")) as f: - for line in f.readlines(): - if line.startswith("__version__"): - return line.split("=")[1].strip().strip('''"''') + +about_path = os.path.join(ROOT, "videodb", "__about__.py") +about = {} +with open(about_path) as fp: + exec(fp.read(), about) # read the contents of README file @@ -19,14 +19,14 @@ def get_version(): setup( - name="videodb", - version=get_version(), - author="videodb", - author_email="contact@videodb.io", + name=about["__title__"], + version=about["__version__"], + author=about["__author__"], + author_email=about["__email__"], description="VideoDB Python SDK", long_description=long_description, long_description_content_type="text/markdown", - url="https://github.com/video-db/videodb-python", + url=about["__url__"], packages=find_packages(exclude=["tests", "tests.*"]), python_requires=">=3.8", install_requires=[ diff --git a/videodb/__about__.py b/videodb/__about__.py new file mode 100644 index 0000000..d71b052 --- /dev/null +++ b/videodb/__about__.py @@ -0,0 +1,8 @@ +""" About information for videodb sdk""" + + +__version__ = "0.2.0" +__title__ = "videodb" +__author__ = "videodb" +__email__ = "contact@videodb.io" +__url__ = "https://github.com/video-db/videodb-python" diff --git a/videodb/__init__.py b/videodb/__init__.py index 22fbad6..82e2500 100644 --- a/videodb/__init__.py +++ b/videodb/__init__.py @@ -4,6 +4,7 @@ import logging from typing import Optional +from videodb.__about__ import __version__ from videodb._utils._video import play_stream from videodb._constants import ( VIDEO_DB_API, diff --git a/videodb/_utils/_http_client.py b/videodb/_utils/_http_client.py index 0b4ae80..8633ebb 100644 --- a/videodb/_utils/_http_client.py +++ b/videodb/_utils/_http_client.py @@ -32,6 +32,7 @@ def __init__( self, api_key: str, base_url: str, + version: str, max_retries: Optional[int] = HttpClientDefaultValues.max_retries, ) -> None: """Create a new http client instance @@ -50,8 +51,13 @@ def __init__( adapter = HTTPAdapter(max_retries=retries) self.session.mount("http://", adapter) self.session.mount("https://", adapter) + self.version = version self.session.headers.update( - {"x-access-token": api_key, "Content-Type": "application/json"} + { + "x-access-token": api_key, + "x-videodb-client": f"videodb-python/{self.version}", + "Content-Type": "application/json", + } ) self.base_url = base_url self.show_progress = False diff --git a/videodb/client.py b/videodb/client.py index 39bf38b..19d9c1d 100644 --- a/videodb/client.py +++ b/videodb/client.py @@ -5,7 +5,7 @@ Union, List, ) - +from videodb import __version__ from videodb._constants import ( ApiPath, ) @@ -28,7 +28,7 @@ def __init__(self, api_key: str, base_url: str) -> None: self.api_key = api_key self.base_url = base_url self.collection_id = "default" - super().__init__(api_key, base_url) + super().__init__(api_key=api_key, base_url=base_url, version=__version__) def get_collection(self, collection_id: Optional[str] = "default") -> Collection: collection_data = self.get(path=f"{ApiPath.collection}/{collection_id}") diff --git a/videodb/video.py b/videodb/video.py index 0b277c7..845e32d 100644 --- a/videodb/video.py +++ b/videodb/video.py @@ -276,7 +276,6 @@ def list_scene_index(self) -> List: index_data = self._connection.get( path=f"{ApiPath.video}/{self.id}/{ApiPath.index}/{ApiPath.scene}" ) - return index_data.get("scene_indexes", []) def get_scene_index(self, scene_index_id: str) -> Optional[List]: From c23d3ca831853c2af224663506d9ea6bc9804bff Mon Sep 17 00:00:00 2001 From: Ankit raj <113342181+ankit-v2-3@users.noreply.github.com> Date: Fri, 21 Jun 2024 15:56:42 +0530 Subject: [PATCH 036/112] fix: linter --- videodb/__init__.py | 3 --- videodb/client.py | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/videodb/__init__.py b/videodb/__init__.py index 82e2500..b96558d 100644 --- a/videodb/__init__.py +++ b/videodb/__init__.py @@ -4,7 +4,6 @@ import logging from typing import Optional -from videodb.__about__ import __version__ from videodb._utils._video import play_stream from videodb._constants import ( VIDEO_DB_API, @@ -26,8 +25,6 @@ logger: logging.Logger = logging.getLogger("videodb") -__version__ = "0.1.2" -__author__ = "videodb" __all__ = [ "VideodbError", diff --git a/videodb/client.py b/videodb/client.py index 19d9c1d..a118f57 100644 --- a/videodb/client.py +++ b/videodb/client.py @@ -5,7 +5,7 @@ Union, List, ) -from videodb import __version__ +from videodb.__about__ import __version__ from videodb._constants import ( ApiPath, ) 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 037/112] 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 038/112] 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 039/112] 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 040/112] 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 041/112] 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 042/112] 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 From a94693a2569ffa7b8df9a7b25cff14d279812a50 Mon Sep 17 00:00:00 2001 From: Ankit raj <113342181+ankit-v2-3@users.noreply.github.com> Date: Thu, 27 Jun 2024 20:53:49 +0530 Subject: [PATCH 043/112] fix: remove force --- videodb/video.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/videodb/video.py b/videodb/video.py index 5a0e291..5098cbf 100644 --- a/videodb/video.py +++ b/videodb/video.py @@ -261,7 +261,6 @@ def index_scenes( model_config: Optional[Dict] = None, name: Optional[str] = None, scenes: Optional[List[Scene]] = None, - force: Optional[bool] = False, callback_url: Optional[str] = None, ) -> Optional[str]: scenes_data = self._connection.post( @@ -273,7 +272,6 @@ def index_scenes( "model_name": model_name, "model_config": model_config, "name": name, - "force": force, "scenes": [scene.to_json() for scene in scenes] if scenes else None, "callback_url": callback_url, }, From 30a88286bdda3f18a3dcddffc7b0f23097c62c19 Mon Sep 17 00:00:00 2001 From: Ankit raj <113342181+ankit-v2-3@users.noreply.github.com> Date: Fri, 19 Jul 2024 16:54:01 +0530 Subject: [PATCH 044/112] fix: keyword search --- videodb/search.py | 1 + 1 file changed, 1 insertion(+) diff --git a/videodb/search.py b/videodb/search.py index 168d10f..bef8fe2 100644 --- a/videodb/search.py +++ b/videodb/search.py @@ -184,6 +184,7 @@ def search_inside_video( "query": query, "score_threshold": score_threshold, "result_threshold": result_threshold, + **kwargs, }, ) return SearchResult(self._connection, **search_data) From 4ecb8ef8355a87021460de71170ce36a9421e206 Mon Sep 17 00:00:00 2001 From: Ankit raj <113342181+ankit-v2-3@users.noreply.github.com> Date: Fri, 19 Jul 2024 17:46:48 +0530 Subject: [PATCH 045/112] build: update version --- videodb/__about__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/videodb/__about__.py b/videodb/__about__.py index aa144bc..a264669 100644 --- a/videodb/__about__.py +++ b/videodb/__about__.py @@ -1,7 +1,7 @@ """ About information for videodb sdk""" -__version__ = "0.2.1" +__version__ = "0.2.2" __title__ = "videodb" __author__ = "videodb" __email__ = "contact@videodb.io" From c8ecceccc2503d34abd2177299fca6e48479e9da Mon Sep 17 00:00:00 2001 From: Ankit raj <113342181+ankit-v2-3@users.noreply.github.com> Date: Wed, 24 Jul 2024 18:44:39 +0530 Subject: [PATCH 046/112] docs: add docstrings --- videodb/audio.py | 6 ++ videodb/client.py | 57 +++++++++++++++++++ videodb/collection.py | 74 ++++++++++++++++++++++++- videodb/image.py | 13 +++++ videodb/scene.py | 13 +++++ videodb/search.py | 4 +- videodb/shot.py | 4 +- videodb/timeline.py | 19 +++++++ videodb/video.py | 124 ++++++++++++++++++++++++++++++++++++++++-- 9 files changed, 303 insertions(+), 11 deletions(-) diff --git a/videodb/audio.py b/videodb/audio.py index 7cab2b2..aabf03f 100644 --- a/videodb/audio.py +++ b/videodb/audio.py @@ -21,4 +21,10 @@ def __repr__(self) -> str: ) def delete(self) -> None: + """Delete the audio. + + :raises InvalidRequestError: If the delete fails + :return: None if the delete is successful + :rtype: None + """ self._connection.delete(f"{ApiPath.audio}/{self.id}") diff --git a/videodb/client.py b/videodb/client.py index a118f57..4df3c5e 100644 --- a/videodb/client.py +++ b/videodb/client.py @@ -24,13 +24,29 @@ class Connection(HttpClient): + """Connection class to interact with the VideoDB""" + def __init__(self, api_key: str, base_url: str) -> None: + """Initializes a new instance of the Connection class with specified API credentials. + + :param api_key: API key for authentication + :param str base_url: (optional) Base URL of the VideoDB API + :raise ValueError: If the API key is not provided + :return: connection object + :rtype: str + """ self.api_key = api_key self.base_url = base_url self.collection_id = "default" super().__init__(api_key=api_key, base_url=base_url, version=__version__) def get_collection(self, collection_id: Optional[str] = "default") -> Collection: + """Get a collection object by its ID. + + :param collection_id: ID of the collection + :return: :class:`Collection ` object + :rtype: :class:`videodb.collection.Collection` + """ collection_data = self.get(path=f"{ApiPath.collection}/{collection_id}") self.collection_id = collection_data.get("id", "default") return Collection( @@ -41,6 +57,11 @@ def get_collection(self, collection_id: Optional[str] = "default") -> Collection ) def get_collections(self) -> List[Collection]: + """Get a list of all collections. + + :return: List of :class:`Collection ` objects + :rtype: list[:class:`videodb.collection.Collection`] + """ collections_data = self.get(path=ApiPath.collection) return [ Collection( @@ -53,6 +74,13 @@ def get_collections(self) -> List[Collection]: ] def create_collection(self, name: str, description: str) -> Collection: + """Create a new collection. + + :param name: Name of the collection + :param description: Description of the collection + :return: :class:`Collection ` object + :rtype: :class:`videodb.collection.Collection` + """ collection_data = self.post( path=ApiPath.collection, data={ @@ -69,6 +97,14 @@ def create_collection(self, name: str, description: str) -> Collection: ) def update_collection(self, id: str, name: str, description: str) -> Collection: + """Update an existing collection. + + :param str id: ID of the collection + :param name: Name of the collection + :param description: Description of the collection + :return: :class:`Collection ` object + :rtype: :class:`videodb.collection.Collection` + """ collection_data = self.patch( path=f"{ApiPath.collection}/{id}", data={ @@ -85,9 +121,19 @@ def update_collection(self, id: str, name: str, description: str) -> Collection: ) def check_usage(self) -> dict: + """Check the usage. + + :return: Usage data + :rtype: dict + """ return self.get(path=f"{ApiPath.billing}/{ApiPath.usage}") def get_invoices(self) -> List[dict]: + """Get a list of all invoices. + + :return: List of invoices + :rtype: list of dict + """ return self.get(path=f"{ApiPath.billing}/{ApiPath.invoices}") def upload( @@ -99,6 +145,17 @@ def upload( description: Optional[str] = None, callback_url: Optional[str] = None, ) -> Union[Video, Audio, Image, None]: + """Upload a file. + + :param file_path: Path to the file to upload + :param url: URL of the file to upload + :param MediaType media_type:(optional):class:`MediaType ` object + :param name:(optional) Name of the file + :param description:(optional) Description of the file + :param callback_url:(optional) URL to receive the callback + :return: :class:`Video