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 01/10] 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 02/10] 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 03/10] 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 04/10] 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 05/10] 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 06/10] 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 07/10] 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 08/10] 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 09/10] 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 10/10] 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__ = [