Skip to content

Commit e41cca0

Browse files
authored
Merge pull request #12 from video-db/v0.1.0/scene-index
V0.1.0/scene index
2 parents e0b3eee + dc108c4 commit e41cca0

File tree

5 files changed

+91
-16
lines changed

5 files changed

+91
-16
lines changed

videodb/_constants.py

+3
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@ class MediaType:
1414
class SearchType:
1515
semantic = "semantic"
1616
keyword = "keyword"
17+
scene = "scene"
1718

1819

1920
class IndexType:
2021
semantic = "semantic"
22+
scene = "scene"
2123

2224

2325
class Workflows:
@@ -44,6 +46,7 @@ class ApiPath:
4446
compile = "compile"
4547
workflow = "workflow"
4648
timeline = "timeline"
49+
delete = "delete"
4750

4851

4952
class Status:

videodb/_utils/_http_client.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ def _get_output(self, url: str):
133133
response_json.get("status") == Status.in_progress
134134
or response_json.get("status") == Status.processing
135135
):
136-
percentage = response_json.get("data").get("percentage")
136+
percentage = response_json.get("data", {}).get("percentage")
137137
if percentage and self.show_progress and self.progress_bar:
138138
self.progress_bar.n = int(percentage)
139139
self.progress_bar.update(0)
@@ -169,7 +169,7 @@ def _parse_response(self, response: requests.Response):
169169
bar_format="{l_bar}{bar:100}{r_bar}{bar:-100b}",
170170
)
171171
response_json = self._get_output(
172-
response_json.get("data").get("output_url")
172+
response_json.get("data", {}).get("output_url")
173173
)
174174
if response_json.get("success"):
175175
return response_json.get("data")

videodb/collection.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,11 @@ def search(
7777
) -> SearchResult:
7878
search = SearchFactory(self._connection).get_search(search_type)
7979
return search.search_inside_collection(
80-
self.id,
81-
query,
82-
result_threshold,
83-
score_threshold,
84-
dynamic_score_percentage,
80+
collection_id=self.id,
81+
query=query,
82+
result_threshold=result_threshold,
83+
score_threshold=score_threshold,
84+
dynamic_score_percentage=dynamic_score_percentage,
8585
)
8686

8787
def upload(

videodb/search.py

+35-1
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ def search_inside_video(
112112
result_threshold: Optional[int] = None,
113113
score_threshold: Optional[int] = None,
114114
dynamic_score_percentage: Optional[int] = None,
115+
**kwargs,
115116
):
116117
search_data = self._connection.post(
117118
path=f"{ApiPath.video}/{video_id}/{ApiPath.search}",
@@ -133,6 +134,7 @@ def search_inside_collection(
133134
result_threshold: Optional[int] = None,
134135
score_threshold: Optional[int] = None,
135136
dynamic_score_percentage: Optional[int] = None,
137+
**kwargs,
136138
):
137139
search_data = self._connection.post(
138140
path=f"{ApiPath.collection}/{collection_id}/{ApiPath.search}",
@@ -176,7 +178,39 @@ def search_inside_collection(self, **kwargs):
176178
raise NotImplementedError("Keyword search will be implemented in the future")
177179

178180

179-
search_type = {SearchType.semantic: SemanticSearch, SearchType.keyword: KeywordSearch}
181+
class SceneSearch(Search):
182+
def __init__(self, _connection):
183+
self._connection = _connection
184+
185+
def search_inside_video(
186+
self,
187+
video_id: str,
188+
query: str,
189+
result_threshold: Optional[int] = None,
190+
score_threshold: Optional[int] = None,
191+
dynamic_score_percentage: Optional[int] = None,
192+
**kwargs,
193+
):
194+
search_data = self._connection.post(
195+
path=f"{ApiPath.video}/{video_id}/{ApiPath.search}",
196+
data={
197+
"index_type": SearchType.scene,
198+
"query": query,
199+
"score_threshold": score_threshold,
200+
"result_threshold": result_threshold,
201+
},
202+
)
203+
return SearchResult(self._connection, **search_data)
204+
205+
def search_inside_collection(self, **kwargs):
206+
raise NotImplementedError("Scene search will be implemented in the future")
207+
208+
209+
search_type = {
210+
SearchType.semantic: SemanticSearch,
211+
SearchType.keyword: KeywordSearch,
212+
SearchType.scene: SceneSearch,
213+
}
180214

181215

182216
class SearchFactory:

videodb/video.py

+46-8
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
from typing import Optional, List, Dict, Tuple
1+
from typing import Optional, Union, List, Dict, Tuple
22
from videodb._utils._video import play_stream
33
from videodb._constants import (
44
ApiPath,
5-
SearchType,
65
IndexType,
7-
Workflows,
6+
SearchType,
87
SubtitleStyle,
8+
Workflows,
99
)
1010
from videodb.search import SearchFactory, SearchResult
1111
from videodb.shot import Shot
@@ -24,6 +24,7 @@ def __init__(self, _connection, id: str, collection_id: str, **kwargs) -> None:
2424
self.length = float(kwargs.get("length", 0.0))
2525
self.transcript = kwargs.get("transcript", None)
2626
self.transcript_text = kwargs.get("transcript_text", None)
27+
self.scenes = kwargs.get("scenes", None)
2728

2829
def __repr__(self) -> str:
2930
return (
@@ -51,11 +52,11 @@ def search(
5152
) -> SearchResult:
5253
search = SearchFactory(self._connection).get_search(search_type)
5354
return search.search_inside_video(
54-
self.id,
55-
query,
56-
result_threshold,
57-
score_threshold,
58-
dynamic_score_percentage,
55+
video_id=self.id,
56+
query=query,
57+
result_threshold=result_threshold,
58+
score_threshold=score_threshold,
59+
dynamic_score_percentage=dynamic_score_percentage,
5960
)
6061

6162
def delete(self) -> None:
@@ -130,6 +131,43 @@ def index_spoken_words(self) -> None:
130131
},
131132
)
132133

134+
def index_scenes(
135+
self,
136+
force: bool = False,
137+
prompt: str = None,
138+
callback_url: str = None,
139+
) -> None:
140+
self._connection.post(
141+
path=f"{ApiPath.video}/{self.id}/{ApiPath.index}",
142+
data={
143+
"index_type": IndexType.scene,
144+
"force": force,
145+
"prompt": prompt,
146+
"callback_url": callback_url,
147+
},
148+
)
149+
150+
def get_scenes(self) -> Union[list, None]:
151+
if self.scenes:
152+
return self.scenes
153+
scene_data = self._connection.get(
154+
path=f"{ApiPath.video}/{self.id}/{ApiPath.index}",
155+
params={
156+
"index_type": IndexType.scene,
157+
},
158+
)
159+
self.scenes = scene_data
160+
return scene_data if scene_data else None
161+
162+
def delete_scene_index(self) -> None:
163+
self._connection.post(
164+
path=f"{ApiPath.video}/{self.id}/{ApiPath.index}/{ApiPath.delete}",
165+
data={
166+
"index_type": IndexType.scene,
167+
},
168+
)
169+
self.scenes = None
170+
133171
def add_subtitle(self, style: SubtitleStyle = SubtitleStyle()) -> str:
134172
if not isinstance(style, SubtitleStyle):
135173
raise ValueError("style must be of type SubtitleStyle")

0 commit comments

Comments
 (0)