Skip to content

Commit 9b0cf5c

Browse files
authored
Merge pull request #15 from video-db/ankit/add-collection-crud
Ankit/add collection crud
2 parents c0ed0e1 + bda5702 commit 9b0cf5c

File tree

7 files changed

+116
-17
lines changed

7 files changed

+116
-17
lines changed

videodb/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424

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

27-
__version__ = "0.1.0"
27+
__version__ = "0.1.1"
2828
__author__ = "videodb"
2929

3030
__all__ = [

videodb/_constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ class ApiPath:
3939
image = "image"
4040
stream = "stream"
4141
thumbnail = "thumbnail"
42+
thumbnails = "thumbnails"
4243
upload_url = "upload_url"
4344
transcription = "transcription"
4445
index = "index"

videodb/_utils/_http_client.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ def _make_request(
8787

8888
def _handle_request_error(self, e: requests.exceptions.RequestException) -> None:
8989
"""Handle request errors"""
90-
90+
self.show_progress = False
9191
if isinstance(e, requests.exceptions.HTTPError):
9292
try:
9393
error_message = e.response.json().get("message", "Unknown error")
@@ -198,8 +198,11 @@ def get(
198198
self.show_progress = show_progress
199199
return self._make_request(method=self.session.get, path=path, **kwargs)
200200

201-
def post(self, path: str, data=None, **kwargs) -> requests.Response:
201+
def post(
202+
self, path: str, data=None, show_progress: Optional[bool] = False, **kwargs
203+
) -> requests.Response:
202204
"""Make a post request"""
205+
self.show_progress = show_progress
203206
return self._make_request(self.session.post, path, json=data, **kwargs)
204207

205208
def put(self, path: str, data=None, **kwargs) -> requests.Response:

videodb/client.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from typing import (
44
Optional,
55
Union,
6+
List,
67
)
78

89
from videodb._constants import (
@@ -39,6 +40,50 @@ def get_collection(self, collection_id: Optional[str] = "default") -> Collection
3940
collection_data.get("description"),
4041
)
4142

43+
def get_collections(self) -> List[Collection]:
44+
collections_data = self.get(path=ApiPath.collection)
45+
return [
46+
Collection(
47+
self,
48+
collection.get("id"),
49+
collection.get("name"),
50+
collection.get("description"),
51+
)
52+
for collection in collections_data.get("collections")
53+
]
54+
55+
def create_collection(self, name: str, description: str) -> Collection:
56+
collection_data = self.post(
57+
path=ApiPath.collection,
58+
data={
59+
"name": name,
60+
"description": description,
61+
},
62+
)
63+
self.collection_id = collection_data.get("id", "default")
64+
return Collection(
65+
self,
66+
collection_data.get("id"),
67+
collection_data.get("name"),
68+
collection_data.get("description"),
69+
)
70+
71+
def update_collection(self, id: str, name: str, description: str) -> Collection:
72+
collection_data = self.patch(
73+
path=f"{ApiPath.collection}/{id}",
74+
data={
75+
"name": name,
76+
"description": description,
77+
},
78+
)
79+
self.collection_id = collection_data.get("id", "default")
80+
return Collection(
81+
self,
82+
collection_data.get("id"),
83+
collection_data.get("name"),
84+
collection_data.get("description"),
85+
)
86+
4287
def upload(
4388
self,
4489
file_path: str = None,

videodb/collection.py

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,25 @@ def __init__(self, _connection, id: str, name: str = None, description: str = No
2727
self.name = name
2828
self.description = description
2929

30+
def __repr__(self) -> str:
31+
return (
32+
f"Collection("
33+
f"id={self.id}, "
34+
f"name={self.name}, "
35+
f"description={self.description})"
36+
)
37+
3038
def get_videos(self) -> List[Video]:
31-
videos_data = self._connection.get(path=f"{ApiPath.video}")
39+
videos_data = self._connection.get(
40+
path=f"{ApiPath.video}",
41+
params={"collection_id": self.id},
42+
)
3243
return [Video(self._connection, **video) for video in videos_data.get("videos")]
3344

3445
def get_video(self, video_id: str) -> Video:
35-
video_data = self._connection.get(path=f"{ApiPath.video}/{video_id}")
46+
video_data = self._connection.get(
47+
path=f"{ApiPath.video}/{video_id}", params={"collection_id": self.id}
48+
)
3649
return Video(self._connection, **video_data)
3750

3851
def delete_video(self, video_id: str) -> None:
@@ -43,29 +56,45 @@ def delete_video(self, video_id: str) -> None:
4356
:return: None if the delete is successful
4457
:rtype: None
4558
"""
46-
return self._connection.delete(path=f"{ApiPath.video}/{video_id}")
59+
return self._connection.delete(
60+
path=f"{ApiPath.video}/{video_id}", params={"collection_id": self.id}
61+
)
4762

4863
def get_audios(self) -> List[Audio]:
49-
audios_data = self._connection.get(path=f"{ApiPath.audio}")
64+
audios_data = self._connection.get(
65+
path=f"{ApiPath.audio}",
66+
params={"collection_id": self.id},
67+
)
5068
return [Audio(self._connection, **audio) for audio in audios_data.get("audios")]
5169

5270
def get_audio(self, audio_id: str) -> Audio:
53-
audio_data = self._connection.get(path=f"{ApiPath.audio}/{audio_id}")
71+
audio_data = self._connection.get(
72+
path=f"{ApiPath.audio}/{audio_id}", params={"collection_id": self.id}
73+
)
5474
return Audio(self._connection, **audio_data)
5575

5676
def delete_audio(self, audio_id: str) -> None:
57-
return self._connection.delete(path=f"{ApiPath.audio}/{audio_id}")
77+
return self._connection.delete(
78+
path=f"{ApiPath.audio}/{audio_id}", params={"collection_id": self.id}
79+
)
5880

5981
def get_images(self) -> List[Image]:
60-
images_data = self._connection.get(path=f"{ApiPath.image}")
82+
images_data = self._connection.get(
83+
path=f"{ApiPath.image}",
84+
params={"collection_id": self.id},
85+
)
6186
return [Image(self._connection, **image) for image in images_data.get("images")]
6287

6388
def get_image(self, image_id: str) -> Image:
64-
image_data = self._connection.get(path=f"{ApiPath.image}/{image_id}")
89+
image_data = self._connection.get(
90+
path=f"{ApiPath.image}/{image_id}", params={"collection_id": self.id}
91+
)
6592
return Image(self._connection, **image_data)
6693

6794
def delete_image(self, image_id: str) -> None:
68-
return self._connection.delete(path=f"{ApiPath.image}/{image_id}")
95+
return self._connection.delete(
96+
path=f"{ApiPath.image}/{image_id}", params={"collection_id": self.id}
97+
)
6998

7099
def search(
71100
self,

videodb/image.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,15 @@ def __init__(self, _connection, id: str, collection_id: str, **kwargs) -> None:
99
self.id = id
1010
self.collection_id = collection_id
1111
self.name = kwargs.get("name", None)
12+
self.url = kwargs.get("url", None)
1213

1314
def __repr__(self) -> str:
1415
return (
1516
f"Image("
1617
f"id={self.id}, "
1718
f"collection_id={self.collection_id}, "
18-
f"name={self.name})"
19+
f"name={self.name}, "
20+
f"url={self.url})"
1921
)
2022

2123
def delete(self) -> None:

videodb/video.py

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
SubtitleStyle,
88
Workflows,
99
)
10+
from videodb.image import Image
1011
from videodb.search import SearchFactory, SearchResult
1112
from videodb.shot import Shot
1213

@@ -88,15 +89,31 @@ def generate_stream(self, timeline: Optional[List[Tuple[int, int]]] = None) -> s
8889
)
8990
return stream_data.get("stream_url", None)
9091

91-
def generate_thumbnail(self):
92-
if self.thumbnail_url:
92+
def generate_thumbnail(self, time: Optional[float] = None) -> Union[str, Image]:
93+
if self.thumbnail_url and not time:
9394
return self.thumbnail_url
95+
96+
if time:
97+
thumbnail_data = self._connection.post(
98+
path=f"{ApiPath.video}/{self.id}/{ApiPath.thumbnail}",
99+
data={
100+
"time": time,
101+
},
102+
)
103+
return Image(self._connection, **thumbnail_data)
104+
94105
thumbnail_data = self._connection.get(
95106
path=f"{ApiPath.video}/{self.id}/{ApiPath.thumbnail}"
96107
)
97108
self.thumbnail_url = thumbnail_data.get("thumbnail_url")
98109
return self.thumbnail_url
99110

111+
def get_thumbnails(self) -> List[Image]:
112+
thumbnails_data = self._connection.get(
113+
path=f"{ApiPath.video}/{self.id}/{ApiPath.thumbnails}"
114+
)
115+
return [Image(self._connection, **thumbnail) for thumbnail in thumbnails_data]
116+
100117
def _fetch_transcript(self, force: bool = False) -> None:
101118
if self.transcript and not force:
102119
return
@@ -116,19 +133,21 @@ def get_transcript_text(self, force: bool = False) -> str:
116133
self._fetch_transcript(force)
117134
return self.transcript_text
118135

119-
def index_spoken_words(self) -> None:
136+
def index_spoken_words(self, force: bool = False, callback_url: str = None) -> None:
120137
"""Semantic indexing of spoken words in the video
121138
122139
:raises InvalidRequestError: If the video is already indexed
123140
:return: None if the indexing is successful
124141
:rtype: None
125142
"""
126-
self._fetch_transcript()
127143
self._connection.post(
128144
path=f"{ApiPath.video}/{self.id}/{ApiPath.index}",
129145
data={
130146
"index_type": IndexType.semantic,
147+
"force": force,
148+
"callback_url": callback_url,
131149
},
150+
show_progress=True,
132151
)
133152

134153
def index_scenes(

0 commit comments

Comments
 (0)