jonathanagustin commited on
Commit
8f53b45
1 Parent(s): 65da0c4

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +183 -163
app.py CHANGED
@@ -1,5 +1,5 @@
1
  """
2
- This module integrates real-time object detection into live YouTube streams using the YOLO model and provides an interactive user interface through Gradio. It allows users to search for live YouTube streams and apply object detection to these streams in real time.
3
 
4
  Main Features:
5
  - Search for live YouTube streams using specific queries.
@@ -8,18 +8,17 @@ Main Features:
8
  - Display the live stream and object detection results through a Gradio interface.
9
 
10
  The module comprises several key components:
 
11
  - `SearchService`: A service class to search for YouTube videos and retrieve live stream URLs.
12
  - `LiveYouTubeObjectDetector`: The main class integrating the YOLO model and Gradio UI, handling the entire workflow of searching, streaming, and object detection.
13
 
14
  Dependencies:
15
- - OpenCV (`cv2`): Used for image processing tasks.
16
  - Gradio: Provides the interactive web-based user interface.
17
- - Streamlink: Used for retrieving live stream data.
18
- - NumPy: Utilized for numerical operations on image data.
19
- - Pillow (`PIL`): A Python Imaging Library for opening, manipulating, and saving images.
20
- - Ultralytics YOLO: The YOLO model implementation for object detection.
21
- - `innertube`: Used for interacting with YouTube's internal API.
22
- - `imageio`: For reading frames from live streams using FFmpeg.
23
 
24
  Usage:
25
  Run this file to launch the Gradio interface, which allows users to input search queries for YouTube live streams, select a stream, and perform object detection on the selected live stream.
@@ -27,88 +26,137 @@ Run this file to launch the Gradio interface, which allows users to input search
27
  """
28
 
29
  import logging
 
 
 
30
  from typing import Any, Dict, List, Optional, Tuple
31
 
32
- import asyncio
33
  import cv2
34
  import gradio as gr
 
35
  import numpy as np
36
- from ultralytics import YOLO
37
  import streamlink
38
  from PIL import Image
39
- import innertube
40
- import imageio.v3 as iio
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
 
42
- logging.basicConfig(level=logging.DEBUG)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
 
44
 
45
  class SearchService:
46
  """
47
  SearchService provides functionality to search for YouTube videos using the
48
- `innertube` library and retrieve live stream URLs using the Streamlink library.
49
 
50
- Methods:
51
- search: Searches YouTube for videos matching a query and live filter.
52
- parse: Parses raw search response data into a list of video details.
53
- get_youtube_url: Constructs a YouTube URL for a given video ID.
54
- get_stream: Retrieves the stream URL for a given YouTube video URL.
55
  """
56
 
57
  @staticmethod
58
- def search(query: str, live: bool = False) -> List[Dict[str, Any]]:
59
- """
60
- Searches YouTube for videos matching the given query and live filter.
61
 
62
  :param query: The search query.
63
- :type query: str
64
- :param live: Whether to filter for live videos.
65
- :type live: bool
66
  :return: A list of search results, each a dictionary with video details.
67
  :rtype: List[Dict[str, Any]]
68
  """
69
- client = innertube.InnerTube()
70
- params = "EgJAAQ%3D%3D" if live else None # 'Live' filter code
71
-
72
- response = client.search(query=query, params=params)
73
  results = SearchService.parse(response)
74
  return results
75
 
76
  @staticmethod
77
- def parse(response: Dict[str, Any]) -> List[Dict[str, Any]]:
78
- """
79
- Parses the raw search response data into a list of video details.
80
 
81
- :param response: The raw search response data from YouTube.
82
- :type response: Dict[str, Any]
83
  :return: A list of parsed video details.
84
- :rtype: List[Dict[str, Any]]
85
  """
86
  results = []
87
  try:
88
- contents = response["contents"]["twoColumnSearchResultsRenderer"]["primaryContents"]["sectionListRenderer"]["contents"]
89
  for content in contents:
90
  items = content.get("itemSectionRenderer", {}).get("contents", [])
91
  for item in items:
92
- video_renderer = item.get("videoRenderer")
93
- if video_renderer:
94
- video_id = video_renderer.get("videoId")
95
- thumbnails = video_renderer.get("thumbnail", {}).get("thumbnails", [])
96
- title_runs = video_renderer.get("title", {}).get("runs", [])
97
- title = "".join([run.get("text", "") for run in title_runs])
98
- thumbnail_url = thumbnails[-1].get("url") if thumbnails else ""
99
- results.append({
100
- 'video_id': video_id,
101
- 'thumbnail_url': thumbnail_url,
102
- 'title': title,
103
- })
 
 
104
  except Exception as e:
105
  logging.error(f"Error parsing search results: {e}")
106
  return results
107
 
108
  @staticmethod
109
- def get_youtube_url(video_id: str) -> str:
 
 
 
 
 
 
 
 
110
  """
111
- Constructs a YouTube URL for the given video ID.
 
 
 
 
 
 
112
 
113
  :param video_id: The ID of the YouTube video.
114
  :type video_id: str
@@ -119,8 +167,7 @@ class SearchService:
119
 
120
  @staticmethod
121
  def get_stream(youtube_url: str) -> Optional[str]:
122
- """
123
- Retrieves the stream URL for a given YouTube video URL.
124
 
125
  :param youtube_url: The URL of the YouTube video.
126
  :type youtube_url: str
@@ -141,30 +188,41 @@ class SearchService:
141
  return None
142
 
143
 
 
 
 
144
  class LiveYouTubeObjectDetector:
145
  """
146
  LiveYouTubeObjectDetector is a class that integrates object detection into live YouTube streams.
147
- It uses the YOLO (You Only Look Once) model to detect objects in video frames captured from live streams.
148
  The class also provides a Gradio interface for users to interact with the object detection system,
149
  allowing them to search for live streams, view them, and detect objects in real-time.
150
-
151
- Methods:
152
- detect_objects: Detects objects in a live YouTube stream given its URL.
153
- get_frame: Captures a frame from a live stream URL.
154
- annotate: Annotates a frame with detected objects.
155
- create_black_image: Creates a black placeholder image.
156
- get_live_streams: Searches for live streams based on a query.
157
- render: Sets up and launches the Gradio interface.
158
  """
159
 
160
  def __init__(self):
161
  """Initializes the LiveYouTubeObjectDetector with YOLO model and UI components."""
162
  logging.getLogger().setLevel(logging.DEBUG)
163
- self.model = YOLO("yolo11n.pt")
164
- self.model.fuse()
165
- self.streams = self.get_live_streams("world live cams")
166
-
167
- async def detect_objects(self, url: str) -> Tuple[Image.Image, List[Tuple[Tuple[int, int, int, int], str]]]:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
168
  """
169
  Detects objects in the given live YouTube stream URL.
170
 
@@ -177,13 +235,13 @@ class LiveYouTubeObjectDetector:
177
  if not stream_url:
178
  logging.error(f"Unable to find a stream for: {url}")
179
  return self.create_black_image()
180
- frame = await self.get_frame(stream_url)
181
  if frame is None:
182
  logging.error(f"Unable to capture frame for: {url}")
183
  return self.create_black_image()
184
  return self.annotate(frame)
185
 
186
- async def get_frame(self, stream_url: str) -> Optional[np.ndarray]:
187
  """
188
  Captures a frame from the given live stream URL.
189
 
@@ -195,17 +253,14 @@ class LiveYouTubeObjectDetector:
195
  if not stream_url:
196
  return None
197
  try:
198
- reader = iio.imiter(stream_url, plugin='pyav', thread_type='AUTO')
199
- loop = asyncio.get_event_loop()
200
- frame = await loop.run_in_executor(None, next, reader, None)
201
- if frame is None:
 
 
 
202
  return None
203
- # Resize frame if needed
204
- frame = cv2.resize(frame, (1280, 720))
205
- return frame
206
- except StopIteration:
207
- logging.warning("Could not read frame from stream.")
208
- return None
209
  except Exception as e:
210
  logging.warning(f"An error occurred while capturing the frame: {e}")
211
  return None
@@ -219,125 +274,90 @@ class LiveYouTubeObjectDetector:
219
  :return: A tuple of the annotated PIL image and list of annotations.
220
  :rtype: Tuple[Image.Image, List[Tuple[Tuple[int, int, int, int], str]]]
221
  """
222
- results = self.model(frame)[0]
 
223
  annotations = []
224
- boxes = results.boxes
 
 
225
  for box in boxes:
226
  x1, y1, x2, y2 = box.xyxy[0].tolist()
227
  class_id = int(box.cls[0])
228
  class_name = self.model.names[class_id]
229
  bbox_coords = (int(x1), int(y1), int(x2), int(y2))
230
  annotations.append((bbox_coords, class_name))
231
- pil_image = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
232
- return pil_image, annotations
233
 
234
  @staticmethod
235
  def create_black_image() -> Tuple[Image.Image, List]:
236
  """
237
  Creates a black image of fixed dimensions.
238
 
239
- :return: A black image as a PIL Image and an empty list of annotations.
240
  :rtype: Tuple[Image.Image, List]
241
  """
242
- black_image = np.zeros((720, 1280, 3), dtype=np.uint8)
243
  pil_black_image = Image.fromarray(black_image)
244
  return pil_black_image, []
245
 
246
- def get_live_streams(self, query: str = "") -> List[Dict[str, Any]]:
 
247
  """
248
  Searches for live streams on YouTube based on the given query.
249
 
250
- :param query: The search query for live streams.
251
  :type query: str
252
  :return: A list of dictionaries containing information about each live stream.
253
  :rtype: List[Dict[str, str]]
254
  """
255
- return SearchService.search(query if query else "world live cams", live=True)
256
 
257
  def render(self):
258
  """
259
  Sets up and launches the Gradio interface for the application.
260
 
261
- This method creates the Gradio UI elements and defines the behavior of the application.
262
- It includes the setup of interactive widgets like galleries, textboxes, and buttons,
263
- and defines the actions triggered by user interactions with these widgets.
264
-
265
  The Gradio interface allows users to search for live YouTube streams, select a stream,
266
  and run object detection on the selected live stream.
267
  """
268
  with gr.Blocks(title="Object Detection in Live YouTube Streams",
269
- css=".gradio-container {background-color: #f5f5f5}", theme=gr.themes.Soft()) as app:
270
- gr.HTML("<h1 style='text-align: center; color: #1E88E5;'>Object Detection in Live YouTube Streams</h1>")
271
- with gr.Tabs():
272
- with gr.TabItem("Live Stream Detection"):
273
  with gr.Row():
274
- stream_input = gr.Textbox(label="URL of Live YouTube Video",
275
- placeholder="Enter YouTube live stream URL...", interactive=True)
276
- submit_button = gr.Button("Detect Objects", variant="primary")
277
- annotated_image = gr.AnnotatedImage(label="Detection Result", height=480)
278
- status_text = gr.Markdown(value="Status: Ready", visible=False)
279
-
280
- async def detect_objects_from_url(url):
281
- status_text.update(value="Status: Processing...", visible=True)
282
- try:
283
- result = await self.detect_objects(url)
284
- status_text.update(value="Status: Done", visible=True)
285
- return result
286
- except Exception as e:
287
- logging.error(f"An error occurred: {e}")
288
- status_text.update(value=f"Status: Error - {e}", visible=True)
289
- return self.create_black_image()
290
-
291
- submit_button.click(fn=detect_objects_from_url, inputs=[stream_input], outputs=[annotated_image], api_name="detect_objects")
292
-
293
- with gr.TabItem("Search Live Streams"):
294
- with gr.Row():
295
- search_input = gr.Textbox(label="Search for Live YouTube Streams",
296
- placeholder="Enter search query...", interactive=True)
297
- search_button = gr.Button("Search", variant="secondary")
298
- gallery = gr.Gallery(label="Live YouTube Streams", show_label=False).style(grid=[4], height="auto")
299
- gallery.style(item_height=150)
300
- status_text_search = gr.Markdown(value="", visible=False)
301
-
302
- async def search_live_streams(query):
303
- status_text_search.update(value="Searching...", visible=True)
304
- self.streams = self.get_live_streams(query)
305
- gallery_items = []
306
- for stream in self.streams:
307
- thumb_url = stream["thumbnail_url"]
308
- title = stream["title"]
309
- video_id = stream["video_id"]
310
- gallery_items.append((thumb_url, title, video_id))
311
- status_text_search.update(value="Search Results:", visible=True)
312
- return gr.update(value=gallery_items)
313
-
314
- search_button.click(fn=search_live_streams, inputs=[search_input], outputs=[gallery], api_name="search_streams")
315
-
316
- async def detect_objects_from_gallery_item(evt: gr.SelectData):
317
- index = evt.index
318
- if index is not None and index < len(self.streams):
319
- selected_stream = self.streams[index]
320
- stream_url = SearchService.get_youtube_url(selected_stream["video_id"])
321
- stream_input.value = stream_url
322
- result = await self.detect_objects(stream_url)
323
- annotated_image.update(value=result[0], annotations=result[1])
324
- with gr.Row():
325
- annotated_image.render()
326
- return result
327
-
328
- gallery.select(fn=detect_objects_from_gallery_item, inputs=None, outputs=None)
329
-
330
- gr.Markdown(
331
- """
332
- **Instructions:**
333
- - **Live Stream Detection Tab:** Enter a YouTube live stream URL and click 'Detect Objects' to view the real-time object detection.
334
- - **Search Live Streams Tab:** Search for live streams on YouTube, select one from the gallery, and view object detection results.
335
- """
336
- )
337
-
338
- gr.HTML("<footer style='text-align: center; color: gray;'>Developed using Gradio and YOLO</footer>")
339
-
340
- app.queue(concurrency_count=3).launch()
341
 
342
  if __name__ == "__main__":
343
- LiveYouTubeObjectDetector().render()
 
1
  """
2
+ This module integrates real-time object detection into live YouTube streams using the YOLO (You Only Look Once) model, and provides an interactive user interface through Gradio. It is designed to allow users to search for live YouTube streams and apply object detection to these streams in real time.
3
 
4
  Main Features:
5
  - Search for live YouTube streams using specific queries.
 
8
  - Display the live stream and object detection results through a Gradio interface.
9
 
10
  The module comprises several key components:
11
+ - `SearchFilter`: An enumeration for YouTube search filters.
12
  - `SearchService`: A service class to search for YouTube videos and retrieve live stream URLs.
13
  - `LiveYouTubeObjectDetector`: The main class integrating the YOLO model and Gradio UI, handling the entire workflow of searching, streaming, and object detection.
14
 
15
  Dependencies:
16
+ - cv2 (OpenCV): Used for image processing tasks.
17
  - Gradio: Provides the interactive web-based user interface.
18
+ - innertube, streamlink: Used for interacting with YouTube and retrieving live stream data.
19
+ - numpy: Utilized for numerical operations on image data.
20
+ - PIL (Pillow): A Python Imaging Library for opening, manipulating, and saving images.
21
+ - ultralytics YOLO: The YOLO model implementation for object detection.
 
 
22
 
23
  Usage:
24
  Run this file to launch the Gradio interface, which allows users to input search queries for YouTube live streams, select a stream, and perform object detection on the selected live stream.
 
26
  """
27
 
28
  import logging
29
+ import os
30
+ import sys
31
+ from enum import Enum
32
  from typing import Any, Dict, List, Optional, Tuple
33
 
 
34
  import cv2
35
  import gradio as gr
36
+ import innertube
37
  import numpy as np
 
38
  import streamlink
39
  from PIL import Image
40
+ from ultralytics import YOLO
41
+
42
+ # Set up logging
43
+ logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
44
+
45
+
46
+ class SearchFilter(Enum):
47
+ """
48
+ An enumeration for specifying different types of YouTube search filters.
49
+
50
+ This Enum class is used to define filters for categorizing YouTube search
51
+ results into either live or regular video content. It is utilized in
52
+ conjunction with the `SearchService` class to refine YouTube searches
53
+ based on the type of content being sought.
54
+
55
+ Attributes:
56
+ LIVE (str): Represents the filter code for live video content on YouTube.
57
+ VIDEO (str): Represents the filter code for regular, non-live video content on YouTube.
58
+ """
59
 
60
+ LIVE = ("EgJAAQ%3D%3D", "Live")
61
+ VIDEO = ("EgIQAQ%3D%3D", "Video")
62
+
63
+ def __init__(self, code, human_readable):
64
+ """Initializes the SearchFilter with a code and a human-readable string.
65
+
66
+ :param code: The filter code used in YouTube search queries.
67
+ :type code: str
68
+ :param human_readable: A human-readable representation of the filter.
69
+ :type human_readable: str
70
+ """
71
+ self.code = code
72
+ self.human_readable = human_readable
73
+
74
+ def __str__(self):
75
+ """Returns the human-readable representation of the filter.
76
+
77
+ :return: The human-readable representation of the filter.
78
+ :rtype: str
79
+ """
80
+ return self.human_readable
81
 
82
 
83
  class SearchService:
84
  """
85
  SearchService provides functionality to search for YouTube videos using the
86
+ InnerTube API and retrieve live stream URLs using the Streamlink library.
87
 
88
+ This service allows filtering search results to either live or regular video
89
+ content and parsing the search response to extract relevant video information.
90
+ It also constructs YouTube URLs for given video IDs and retrieves the best
91
+ available stream URL for live YouTube videos.
 
92
  """
93
 
94
  @staticmethod
95
+ def search(query: Optional[str], filter: SearchFilter = SearchFilter.VIDEO):
96
+ """Searches YouTube for videos matching the given query and filter.
 
97
 
98
  :param query: The search query.
99
+ :type query: Optional[str]
100
+ :param filter: The search filter to apply.
101
+ :type filter: SearchFilter
102
  :return: A list of search results, each a dictionary with video details.
103
  :rtype: List[Dict[str, Any]]
104
  """
105
+ response = SearchService._search(query, filter)
 
 
 
106
  results = SearchService.parse(response)
107
  return results
108
 
109
  @staticmethod
110
+ def parse(data: Dict[str, Any]) -> List[Dict[str, str]]:
111
+ """Parses the raw search response data into a list of video details.
 
112
 
113
+ :param data: The raw search response data from YouTube.
114
+ :type data: Dict[str, Any]
115
  :return: A list of parsed video details.
116
+ :rtype: List[Dict[str, str]]
117
  """
118
  results = []
119
  try:
120
+ contents = data["contents"]["twoColumnSearchResultsRenderer"]["primaryContents"]["sectionListRenderer"]["contents"]
121
  for content in contents:
122
  items = content.get("itemSectionRenderer", {}).get("contents", [])
123
  for item in items:
124
+ if "videoRenderer" in item:
125
+ renderer = item["videoRenderer"]
126
+ video_id = renderer.get("videoId", "")
127
+ thumbnails = renderer.get("thumbnail", {}).get("thumbnails", [])
128
+ thumbnail_url = thumbnails[-1]["url"] if thumbnails else ""
129
+ title_runs = renderer.get("title", {}).get("runs", [])
130
+ title = "".join(run.get("text", "") for run in title_runs)
131
+ results.append(
132
+ {
133
+ "video_id": video_id,
134
+ "thumbnail_url": thumbnail_url,
135
+ "title": title,
136
+ }
137
+ )
138
  except Exception as e:
139
  logging.error(f"Error parsing search results: {e}")
140
  return results
141
 
142
  @staticmethod
143
+ def _search(query: Optional[str] = None, filter: SearchFilter = SearchFilter.VIDEO) -> Dict[str, Any]:
144
+ """Performs a YouTube search with the given query and filter.
145
+
146
+ :param query: The search query.
147
+ :type query: Optional[str]
148
+ :param filter: The search filter to apply.
149
+ :type filter: SearchFilter
150
+ :return: The raw search response data from YouTube.
151
+ :rtype: Dict[str, Any]
152
  """
153
+ client = innertube.InnerTube(client_name="WEB", client_version="2.20230920.00.00")
154
+ response = client.search(query=query, params=filter.code if filter else None)
155
+ return response
156
+
157
+ @staticmethod
158
+ def get_youtube_url(video_id: str) -> str:
159
+ """Constructs a YouTube URL for the given video ID.
160
 
161
  :param video_id: The ID of the YouTube video.
162
  :type video_id: str
 
167
 
168
  @staticmethod
169
  def get_stream(youtube_url: str) -> Optional[str]:
170
+ """Retrieves the stream URL for a given YouTube video URL.
 
171
 
172
  :param youtube_url: The URL of the YouTube video.
173
  :type youtube_url: str
 
188
  return None
189
 
190
 
191
+ INITIAL_STREAMS = SearchService.search("world live cams", SearchFilter.LIVE)
192
+
193
+
194
  class LiveYouTubeObjectDetector:
195
  """
196
  LiveYouTubeObjectDetector is a class that integrates object detection into live YouTube streams.
197
+ It uses the YOLO model to detect objects in video frames captured from live streams.
198
  The class also provides a Gradio interface for users to interact with the object detection system,
199
  allowing them to search for live streams, view them, and detect objects in real-time.
 
 
 
 
 
 
 
 
200
  """
201
 
202
  def __init__(self):
203
  """Initializes the LiveYouTubeObjectDetector with YOLO model and UI components."""
204
  logging.getLogger().setLevel(logging.DEBUG)
205
+ self.model = YOLO("yolo11n.pt") # Using yolo11n.pt as per your request
206
+ self.streams = INITIAL_STREAMS
207
+
208
+ # Gradio UI
209
+ initial_gallery_items = [(stream["thumbnail_url"], stream["title"]) for stream in self.streams]
210
+ self.gallery = gr.Gallery(label="Live YouTube Videos",
211
+ value=initial_gallery_items,
212
+ show_label=True,
213
+ columns=[4],
214
+ rows=[5],
215
+ object_fit="contain",
216
+ height="auto",
217
+ allow_preview=False)
218
+ self.search_input = gr.Textbox(label="Search Live YouTube Videos")
219
+ self.stream_input = gr.Textbox(label="URL of Live YouTube Video")
220
+ self.annotated_image = gr.AnnotatedImage(show_label=False)
221
+ self.search_button = gr.Button("Search", size="lg")
222
+ self.submit_button = gr.Button("Detect Objects", variant="primary", size="lg")
223
+ self.page_title = gr.HTML("<center><h1><b>Object Detection in Live YouTube Streams</b></h1></center>")
224
+
225
+ def detect_objects(self, url: str) -> Tuple[Image.Image, List[Tuple[Tuple[int, int, int, int], str]]]:
226
  """
227
  Detects objects in the given live YouTube stream URL.
228
 
 
235
  if not stream_url:
236
  logging.error(f"Unable to find a stream for: {url}")
237
  return self.create_black_image()
238
+ frame = self.get_frame(stream_url)
239
  if frame is None:
240
  logging.error(f"Unable to capture frame for: {url}")
241
  return self.create_black_image()
242
  return self.annotate(frame)
243
 
244
+ def get_frame(self, stream_url: str) -> Optional[np.ndarray]:
245
  """
246
  Captures a frame from the given live stream URL.
247
 
 
253
  if not stream_url:
254
  return None
255
  try:
256
+ cap = cv2.VideoCapture(stream_url)
257
+ ret, frame = cap.read()
258
+ cap.release()
259
+ if ret and frame is not None:
260
+ return cv2.resize(frame, (1920, 1080))
261
+ else:
262
+ logging.warning("Unable to process the HLS stream with cv2.VideoCapture.")
263
  return None
 
 
 
 
 
 
264
  except Exception as e:
265
  logging.warning(f"An error occurred while capturing the frame: {e}")
266
  return None
 
274
  :return: A tuple of the annotated PIL image and list of annotations.
275
  :rtype: Tuple[Image.Image, List[Tuple[Tuple[int, int, int, int], str]]]
276
  """
277
+ frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
278
+ predictions = self.model.predict(frame_rgb)
279
  annotations = []
280
+ result = predictions[0]
281
+
282
+ boxes = result.boxes
283
  for box in boxes:
284
  x1, y1, x2, y2 = box.xyxy[0].tolist()
285
  class_id = int(box.cls[0])
286
  class_name = self.model.names[class_id]
287
  bbox_coords = (int(x1), int(y1), int(x2), int(y2))
288
  annotations.append((bbox_coords, class_name))
289
+
290
+ return Image.fromarray(frame_rgb), annotations
291
 
292
  @staticmethod
293
  def create_black_image() -> Tuple[Image.Image, List]:
294
  """
295
  Creates a black image of fixed dimensions.
296
 
297
+ :return: A black image as a PIL image and an empty list of annotations.
298
  :rtype: Tuple[Image.Image, List]
299
  """
300
+ black_image = np.zeros((1080, 1920, 3), dtype=np.uint8)
301
  pil_black_image = Image.fromarray(black_image)
302
  return pil_black_image, []
303
 
304
+ @staticmethod
305
+ def get_live_streams(query=""):
306
  """
307
  Searches for live streams on YouTube based on the given query.
308
 
309
+ :param query: The search query for live streams, defaults to 'world live cams'.
310
  :type query: str
311
  :return: A list of dictionaries containing information about each live stream.
312
  :rtype: List[Dict[str, str]]
313
  """
314
+ return SearchService.search(query if query else "world live cams", SearchFilter.LIVE)
315
 
316
  def render(self):
317
  """
318
  Sets up and launches the Gradio interface for the application.
319
 
 
 
 
 
320
  The Gradio interface allows users to search for live YouTube streams, select a stream,
321
  and run object detection on the selected live stream.
322
  """
323
  with gr.Blocks(title="Object Detection in Live YouTube Streams",
324
+ css="footer {visibility: hidden}", analytics_enabled=False) as app:
325
+ self.page_title.render()
326
+ with gr.Column():
327
+ with gr.Group():
328
  with gr.Row():
329
+ self.stream_input.render()
330
+ self.submit_button.render()
331
+ self.annotated_image.render()
332
+ with gr.Group():
333
+ with gr.Row():
334
+ self.search_input.render()
335
+ self.search_button.render()
336
+ with gr.Row():
337
+ self.gallery.render()
338
+
339
+ @self.gallery.select(inputs=None, outputs=[self.annotated_image, self.stream_input], scroll_to_output=True)
340
+ def detect_objects_from_gallery_item(evt: gr.SelectData):
341
+ if evt.index is not None and evt.index < len(self.streams):
342
+ selected_stream = self.streams[evt.index]
343
+ stream_url = SearchService.get_youtube_url(selected_stream["video_id"])
344
+ annotated_image, annotations = self.detect_objects(stream_url)
345
+ self.stream_input.value = stream_url
346
+ return annotated_image, annotations, stream_url
347
+ return None, "", ""
348
+
349
+ @self.search_button.click(inputs=[self.search_input], outputs=[self.gallery])
350
+ def search_live_streams(query):
351
+ self.streams = self.get_live_streams(query)
352
+ gallery_items = [(stream["thumbnail_url"], stream["title"]) for stream in self.streams]
353
+ return gallery_items
354
+
355
+ @self.submit_button.click(inputs=[self.stream_input], outputs=[self.annotated_image])
356
+ def detect_objects_from_url(url):
357
+ return self.detect_objects(url)
358
+
359
+ app.queue().launch(show_api=False, debug=True)
360
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
361
 
362
  if __name__ == "__main__":
363
+ LiveYouTubeObjectDetector().render()