Skip to content

Commit

Permalink
Merge pull request #15 from video-db/ankit/add-collection-crud
Browse files Browse the repository at this point in the history
Ankit/add collection crud
  • Loading branch information
ashish-spext authored Mar 26, 2024
2 parents c0ed0e1 + bda5702 commit 9b0cf5c
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 17 deletions.
2 changes: 1 addition & 1 deletion videodb/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

logger: logging.Logger = logging.getLogger("videodb")

__version__ = "0.1.0"
__version__ = "0.1.1"
__author__ = "videodb"

__all__ = [
Expand Down
1 change: 1 addition & 0 deletions videodb/_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class ApiPath:
image = "image"
stream = "stream"
thumbnail = "thumbnail"
thumbnails = "thumbnails"
upload_url = "upload_url"
transcription = "transcription"
index = "index"
Expand Down
7 changes: 5 additions & 2 deletions videodb/_utils/_http_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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:
Expand Down
45 changes: 45 additions & 0 deletions videodb/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from typing import (
Optional,
Union,
List,
)

from videodb._constants import (
Expand Down Expand Up @@ -39,6 +40,50 @@ 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,
},
)
self.collection_id = collection_data.get("id", "default")
return Collection(
self,
collection_data.get("id"),
collection_data.get("name"),
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,
Expand Down
47 changes: 38 additions & 9 deletions videodb/collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,25 @@ 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}")
videos_data = self._connection.get(
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:
Expand All @@ -43,29 +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.audio}")
audios_data = self._connection.get(
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.image}")
images_data = self._connection.get(
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,
Expand Down
4 changes: 3 additions & 1 deletion videodb/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
27 changes: 23 additions & 4 deletions videodb/video.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
SubtitleStyle,
Workflows,
)
from videodb.image import Image
from videodb.search import SearchFactory, SearchResult
from videodb.shot import Shot

Expand Down Expand Up @@ -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
Expand All @@ -116,19 +133,21 @@ 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, callback_url: str = None) -> 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,
"callback_url": callback_url,
},
show_progress=True,
)

def index_scenes(
Expand Down

0 comments on commit 9b0cf5c

Please sign in to comment.