J3 commited on
Commit
b3bb0f6
·
verified ·
1 Parent(s): 2defbd3

Upload 6 files

Browse files
Tools/code_reader.py ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from smolagents import tool
2
+
3
+ @tool
4
+ def code_reader(python_file_path:str) -> str:
5
+ """
6
+ This tool reads python file and returns the code as a string.
7
+
8
+ Args:
9
+ python_file_path: Path of a local python file that needs to be accessed.
10
+ """
11
+
12
+ try:
13
+ with open(python_file_path) as f:
14
+ code = f.read()
15
+ return code
16
+ except Exception as e:
17
+ return f'An unexpected error occurred: {str(e)}'
Tools/mywebpagevisit.py ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from smolagents import VisitWebpageTool
2
+
3
+
4
+ class CustomVisitWebpageTool(VisitWebpageTool):
5
+
6
+ def forward(self, url: str) -> str:
7
+ try:
8
+ import re
9
+ import requests
10
+ from markdownify import markdownify
11
+ from requests.exceptions import RequestException
12
+ except ImportError as e:
13
+ raise ImportError(
14
+ "You must install packages `markdownify` and `requests` to run this tool: for instance run `pip install markdownify requests`."
15
+ ) from e
16
+ try:
17
+ headers = {
18
+ "User-Agent": (
19
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
20
+ "AppleWebKit/537.36 (KHTML, like Gecko) "
21
+ "Chrome/122.0.0.0 Safari/537.36"
22
+ ),
23
+ "Accept-Language": "en-US,en;q=0.9",
24
+ "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
25
+ }
26
+ # Send a GET request to the URL with a 20-second timeout
27
+ response = requests.get(url, timeout=20, headers=headers)
28
+ response.raise_for_status() # Raise an exception for bad status codes
29
+
30
+ # Convert the HTML content to Markdown
31
+ markdown_content = markdownify(response.text).strip()
32
+
33
+ # Remove multiple line breaks
34
+ #markdown_content = re.sub(r"\n{3,}", "\n\n", markdown_content)
35
+ return self._truncate_content(markdown_content, self.max_output_length)
36
+
37
+ except requests.exceptions.Timeout:
38
+ return "The request timed out. Please try again later or check the URL."
39
+ except RequestException as e:
40
+ return f"Error fetching the webpage: {str(e)}"
41
+ except Exception as e:
42
+ return f"An unexpected error occurred: {str(e)}"
Tools/searchtool.py ADDED
@@ -0,0 +1,230 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ##not implemented include_images and include_image_descriptions attributes
2
+
3
+ # import os
4
+
5
+ # from tavily import TavilyClient
6
+ # from smolagents import Tool
7
+ # #from pydantic import BaseModel, Field
8
+ # from typing import Optional,List, Dict, Any, Literal
9
+
10
+
11
+ # # class TavilySearchInput(BaseModel):
12
+ # # """Input for Tavily Search."""
13
+ # # query : str = Field(description="The search query string.")
14
+ # # max_results : Optional[int] = Field(default = 5,
15
+ # # description="Maximum number of search results to return.")
16
+ # # topic : Optional[str] = Field(default = "general",
17
+ # # description="Category of the search. Can be 'general', 'news', or 'finance'.")
18
+ # # include_answer : Optional[bool] = Field(default = False,
19
+ # # description="Whether to include an answer to original query in results.")
20
+ # # include_raw_content : Optional[bool] = Field(default = False,
21
+ # # description="Whether to include cleaned and parsed HTML of each search result.")
22
+ # # search_depth: Optional[str] = Field(default = "basic",
23
+ # # description="Depth of the search, either 'basic' or 'advanced'.")
24
+ # # time_range: Optional[str] = Field(default = None,
25
+ # # description="The time range back from the current date to filter results - 'day', 'week', 'month', or 'year'.")
26
+ # # include_domains: Optional[List[str]] = Field(default = None,
27
+ # # description="List of domains to specifically include.")
28
+ # # exclude_domains: Optional[List[str]] = Field(default = None,
29
+ # # description="List of domains to specifically exclude.")
30
+
31
+
32
+ # # class TavilySearchTool(Tool):
33
+ # # name: str = "tavily_search"
34
+ # # description = """
35
+ # # A tool to perform comprehensive web searches using the Tavily Search API.
36
+ # # It can retrieve real-time, accurate, and factual information from across the web.
37
+ # # Useful for answering questions, gathering information, and reducing hallucinations."""
38
+
39
+ # # inputs = inputs
40
+
41
+ # # output_type = "list"
42
+
43
+ # # def __init__(self, api_key:Optional[str] = None, **kwargs):
44
+ # # super().__init__(**kwargs)
45
+ # # if api_key is None:
46
+ # # api_key = os.getenv("TRAVILY_SECRET_KEY")
47
+ # # if not api_key:
48
+ # # raise ValueError(
49
+ # # "Tavily API key not found. Please set the TAVILY_API_KEY environment variable "
50
+ # # "or pass it directly to TavilySearchTool(api_key='YOUR_API_KEY')."
51
+ # # )
52
+ # # self.tavily_client = TavilyClient(api_key=api_key)
53
+ # # print('TavilyClient is initiated')
54
+
55
+ # # def forward(self, query: str,
56
+ # # max_results: int = 5,
57
+ # # include_answer: bool = False,
58
+ # # include_raw_content: bool = False,
59
+ # # search_depth: str = "basic",
60
+ # # topic: str = "general",
61
+ # # time_range: Optional[str] = None,
62
+ # # include_domains: Optional[List[str]] = None,
63
+ # # exclude_domains: Optional[List[str]] = None) -> List[Dict[str, Any]]:
64
+ # # """
65
+ # # Executes a search query using the Tavily API.
66
+
67
+ # # Args:
68
+ # # query (str): The search query.
69
+ # # max_results (int): Maximum number of search results to return.
70
+ # # include_answer (bool): Whether to include a direct answer synthesized from the search results.
71
+ # # include_raw_content (bool): Whether to include the raw HTML content of the searched pages.
72
+ # # search_depth (str): Depth of the search, either 'basic' or 'advanced'.
73
+ # # topic (str): Category of the search. Can be 'general', 'news', or 'finance'.
74
+ # # time_range (Optional[str]): The time range back from the current date to filter results - 'day', 'week', 'month', or 'year'.
75
+ # # include_domains (Optional[List[str]]): List of domains to specifically include.
76
+ # # exclude_domains (Optional[List[str]]): List of domains to specifically exclude.
77
+
78
+ # # Returns:
79
+ # # List[Dict[str, Any]]: A list of dictionaries, where each dictionary represents a search result.
80
+ # # Each result typically contains 'title', 'url', and 'content'.
81
+ # # If include_answer is True, it will also include the 'answer' field
82
+ # # in the top-level response.
83
+ # # """
84
+ # # try:
85
+ # # response = self.tavily_client.search(
86
+ # # query=query,
87
+ # # max_results=max_results,
88
+ # # include_answer=include_answer,
89
+ # # include_raw_content=include_raw_content,
90
+ # # search_depth=search_depth,
91
+ # # topic=topic,
92
+ # # time_range=time_range,
93
+ # # include_domains=include_domains,
94
+ # # exclude_domains=exclude_domains
95
+ # # )
96
+
97
+ # # return response
98
+
99
+ # # except Exception as e:
100
+ # # return [{"error": f"Tavily Search Error: {e}"}]
101
+
102
+
103
+
104
+ # # class TavilySearchTool(Tool):
105
+ # # name = "tavily_search"
106
+ # # description = """
107
+ # # A tool to perform comprehensive web searches using the Tavily Search API.
108
+ # # It can retrieve real-time, accurate, and factual information from across the web.
109
+ # # Useful for answering questions, gathering information, and reducing hallucinations."""
110
+
111
+ # # inputs = {
112
+ # # "query": {"type": "string", "description": "The search query string."},
113
+ # # "max_results": {"type": "integer", "description": "Maximum number of results.", "default": 5, "nullable": False},
114
+ # # "topic": {"type": "string", "description": "Search category: 'general', 'news', or 'finance'.", "default": "general", "nullable": False},
115
+ # # "include_answer": {"type": "boolean", "description": "Whether to include answer in results.", "default": False, "nullable": False},
116
+ # # "include_raw_content": {"type": "boolean", "description": "Whether to include HTML content.", "default": False, "nullable": False},
117
+ # # "search_depth": {"type": "string", "description": "Search depth: 'basic' or 'advanced'.", "default": "basic", "nullable": False},
118
+ # # "time_range": {"type": "string", "description": "Filter by time range.", "default": None, "nullable": True},
119
+ # # "include_domains": {"type": "array", "description": "List of domains to include.", "default": None, "nullable": True},
120
+ # # "exclude_domains": {"type": "array", "description": "List of domains to exclude.", "default": None, "nullable": True},
121
+ # # }
122
+
123
+ # # output_type = "any"
124
+
125
+ # # def __init__(self, api_key:Optional[str] = None, **kwargs):
126
+ # # super().__init__(**kwargs)
127
+ # # if api_key is None:
128
+ # # api_key = os.getenv("TRAVILY_SECRET_KEY")
129
+ # # if not api_key:
130
+ # # raise ValueError(
131
+ # # "Tavily API key not found. Please set the TAVILY_API_KEY environment variable "
132
+ # # "or pass it directly to TavilySearchTool(api_key='YOUR_API_KEY')."
133
+ # # )
134
+ # # self.tavily_client = TavilyClient(api_key=api_key)
135
+ # # print('TavilyClient is initiated')
136
+
137
+ # # def forward(self, query: str,
138
+ # # max_results: int = 5,
139
+ # # include_answer: bool = False,
140
+ # # include_raw_content: bool = False,
141
+ # # search_depth: str = "basic",
142
+ # # topic: str = "general",
143
+ # # time_range: Optional[str] = None,
144
+ # # include_domains: Optional[List[str]] = None,
145
+ # # exclude_domains: Optional[List[str]] = None) -> List[Dict[str, Any]]:
146
+ # # """
147
+ # # Executes a search query using the Tavily API.
148
+
149
+ # # Args:
150
+ # # query (str): The search query.
151
+ # # max_results (int): Maximum number of search results to return.
152
+ # # include_answer (bool): Whether to include a direct answer synthesized from the search results.
153
+ # # include_raw_content (bool): Whether to include the raw HTML content of the searched pages.
154
+ # # search_depth (str): Depth of the search, either 'basic' or 'advanced'.
155
+ # # topic (str): Category of the search. Can be 'general', 'news', or 'finance'.
156
+ # # time_range (Optional[str]): The time range back from the current date to filter results - 'day', 'week', 'month', or 'year'.
157
+ # # include_domains (Optional[List[str]]): List of domains to specifically include.
158
+ # # exclude_domains (Optional[List[str]]): List of domains to specifically exclude.
159
+
160
+ # # Returns:
161
+ # # List[Dict[str, Any]]: A list of dictionaries, where each dictionary represents a search result.
162
+ # # Each result typically contains 'title', 'url', and 'content'.
163
+ # # If include_answer is True, it will also include the 'answer' field
164
+ # # in the top-level response.
165
+ # # """
166
+ # # try:
167
+ # # response = self.tavily_client.search(
168
+ # # query=query,
169
+ # # max_results=max_results,
170
+ # # include_answer=include_answer,
171
+ # # include_raw_content=include_raw_content,
172
+ # # search_depth=search_depth,
173
+ # # topic=topic,
174
+ # # time_range=time_range,
175
+ # # include_domains=include_domains,
176
+ # # exclude_domains=exclude_domains
177
+ # # )
178
+
179
+ # # return response
180
+
181
+ # # except Exception as e:
182
+ # # return [{"error": f"Tavily Search Error: {e}"}]
183
+
184
+
185
+
186
+ # class WebSearchTool(Tool):
187
+ # name = "web_search"
188
+ # description = """
189
+ # A tool to perform comprehensive web searches using the Tavily Search API.
190
+ # It can retrieve real-time, accurate, and factual information from across the web.
191
+ # Useful for answering questions, gathering information, and reducing hallucinations."""
192
+
193
+ # inputs = {
194
+ # "query": {"type": "string",
195
+ # "description": "The search query string."
196
+ # }
197
+ # }
198
+
199
+ # output_type = "any"
200
+
201
+ # def __init__(self, api_key:Optional[str] = None, **kwargs):
202
+ # super().__init__(**kwargs)
203
+ # if api_key is None:
204
+ # api_key = os.getenv("TRAVILY_SECRET_KEY")
205
+ # if not api_key:
206
+ # raise ValueError(
207
+ # "Tavily API key not found. Please set the TAVILY_API_KEY environment variable "
208
+ # "or pass it directly to TavilySearchTool(api_key='YOUR_API_KEY')."
209
+ # )
210
+ # self.tavily_client = TavilyClient(api_key=api_key)
211
+ # print('TavilyClient is initiated')
212
+
213
+ # def forward(self,
214
+ # query: str) -> Any:
215
+ # try:
216
+ # print(f'trying travily search...')
217
+ # response = self.tavily_client.search(
218
+ # query=query,
219
+ # max_results=5,
220
+ # search_depth="advanced",
221
+ # topic='general',
222
+ # )
223
+ # #print(response)
224
+ # if "results" in response:
225
+ # return response.get("results", [])
226
+ # else:
227
+ # return('try shorter query or different approach.')
228
+
229
+ # except Exception as e:
230
+ # return [{"error": f"Tavily Search Error: {e}"}]
Tools/transcriber.py ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from smolagents import tool
2
+ import torch
3
+ from transformers import AutoModelForSpeechSeq2Seq, AutoProcessor, pipeline
4
+
5
+ @tool
6
+ def transcriber(audio_file_path:str) -> str:
7
+ """
8
+ This tool transcribes an audio file and returns the generated transcription.
9
+
10
+ Args:
11
+ audio_file_path: Path of a local audio file that needs to be transcribed.
12
+ """
13
+
14
+ try:
15
+ device = "cuda:0" if torch.cuda.is_available() else "cpu"
16
+ torch_dtype = torch.float16 if torch.cuda.is_available() else torch.float32
17
+
18
+ model_id = "openai/whisper-small"
19
+
20
+ model = AutoModelForSpeechSeq2Seq.from_pretrained(
21
+ model_id, torch_dtype=torch_dtype, low_cpu_mem_usage=True, use_safetensors=True
22
+ )
23
+ model.to(device)
24
+
25
+ processor = AutoProcessor.from_pretrained(model_id)
26
+
27
+ pipe = pipeline(
28
+ "automatic-speech-recognition",
29
+ model=model,
30
+ tokenizer=processor.tokenizer,
31
+ feature_extractor=processor.feature_extractor,
32
+ torch_dtype=torch_dtype,
33
+ device=device,
34
+ return_timestamps=True
35
+ )
36
+
37
+ result = pipe(audio_file_path)
38
+
39
+ import gc
40
+
41
+ # After inference
42
+ del pipe
43
+ del model
44
+ del processor
45
+ gc.collect() # Force Python garbage collection
46
+ torch.cuda.empty_cache() # Clear cached memory
47
+
48
+ return result["text"]
49
+ except Exception as e:
50
+ return f'error occured: {e}'
Tools/visual_reasoner.py ADDED
@@ -0,0 +1,148 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from transformers import Qwen2_5_VLForConditionalGeneration, AutoTokenizer, AutoProcessor
2
+ from qwen_vl_utils import process_vision_info
3
+ from smolagents import tool
4
+ import torch
5
+
6
+
7
+ @tool
8
+ def video_reasoner(file_path : str, query : str) -> str:
9
+ """
10
+ This tool performs requested visual reasoning task on the provided video and returns the generated output.
11
+
12
+ Args:
13
+ file_path: Path of a local video file on which visual reasoning is to be done.
14
+ query: visual reasoning that is to be done.
15
+ """
16
+ try:
17
+ # default: Load the model on the available device(s)
18
+ model = Qwen2_5_VLForConditionalGeneration.from_pretrained(
19
+ "Qwen/Qwen2.5-VL-7B-Instruct", torch_dtype="auto", device_map="auto"
20
+ )
21
+
22
+ # default processer
23
+ processor = AutoProcessor.from_pretrained("Qwen/Qwen2.5-VL-7B-Instruct")
24
+
25
+ messages = [
26
+ {
27
+ "role": "user",
28
+ "content": [
29
+ {
30
+ "type": "video",
31
+ "video": file_path,
32
+ "max_pixels": 360 * 360,
33
+ "fps": 0.3,
34
+ },
35
+ {"type": "text", "text": f"{query}\n\nAdditional instruction: Treat the two types of penguins as distinct species e.g. Adelie and Emperor Penguins are considered two different species of birds."},
36
+ ],
37
+ }
38
+ ]
39
+
40
+ # Preparation for inference
41
+ text = processor.apply_chat_template(
42
+ messages, tokenize=False, add_generation_prompt=True
43
+ )
44
+ image_inputs, video_inputs = process_vision_info(messages)
45
+ inputs = processor(
46
+ text=[text],
47
+ images=image_inputs,
48
+ videos=video_inputs,
49
+ padding=True,
50
+ return_tensors="pt",
51
+ )
52
+ inputs = inputs.to("cuda")
53
+
54
+ # Inference: Generation of the output
55
+ generated_ids = model.generate(**inputs, max_new_tokens=512)
56
+ generated_ids_trimmed = [
57
+ out_ids[len(in_ids) :] for in_ids, out_ids in zip(inputs.input_ids, generated_ids)
58
+ ]
59
+ output_text = processor.batch_decode(
60
+ generated_ids_trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=False
61
+ )
62
+
63
+ import gc
64
+
65
+ # After inference
66
+ del image_inputs
67
+ del video_inputs
68
+ del inputs
69
+ del model
70
+ del processor
71
+ gc.collect() # Force Python garbage collection
72
+ torch.cuda.empty_cache() # Clear cached memory
73
+
74
+ return output_text
75
+
76
+ except Exception as e:
77
+ return f'error occured: {e}'
78
+
79
+
80
+ @tool
81
+ def image_reasoner(file_path : str, query : str) -> str:
82
+ """
83
+ This tool performs requested visual reasoning task on the provided image and returns the generated output.
84
+
85
+ Args:
86
+ file_path: Path of a local image file on which visual reasoning is to be done.
87
+ query: visual reasoning that is to be done.
88
+ """
89
+ try:
90
+ # default: Load the model on the available device(s)
91
+ model = Qwen2_5_VLForConditionalGeneration.from_pretrained(
92
+ "Qwen/Qwen2.5-VL-7B-Instruct", torch_dtype="auto", device_map="auto"
93
+ )
94
+
95
+ # default processer
96
+ processor = AutoProcessor.from_pretrained("Qwen/Qwen2.5-VL-7B-Instruct")
97
+
98
+ messages = [
99
+ {
100
+ "role": "user",
101
+ "content": [
102
+ {
103
+ "type": "image",
104
+ "image": file_path,
105
+ },
106
+ {"type": "text", "text": f"{query}\n\nAdditional instruction: Review your answer for correctness."},
107
+ ],
108
+ }
109
+ ]
110
+
111
+ # Preparation for inference
112
+ text = processor.apply_chat_template(
113
+ messages, tokenize=False, add_generation_prompt=True
114
+ )
115
+ image_inputs, video_inputs = process_vision_info(messages)
116
+ inputs = processor(
117
+ text=[text],
118
+ images=image_inputs,
119
+ videos=video_inputs,
120
+ padding=True,
121
+ return_tensors="pt",
122
+ )
123
+ inputs = inputs.to("cuda")
124
+
125
+ # Inference: Generation of the output
126
+ generated_ids = model.generate(**inputs, max_new_tokens=512)
127
+ generated_ids_trimmed = [
128
+ out_ids[len(in_ids) :] for in_ids, out_ids in zip(inputs.input_ids, generated_ids)
129
+ ]
130
+ output_text = processor.batch_decode(
131
+ generated_ids_trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=False
132
+ )
133
+
134
+ import gc
135
+
136
+ # After inference
137
+ del image_inputs
138
+ del video_inputs
139
+ del inputs
140
+ del model
141
+ del processor
142
+ gc.collect() # Force Python garbage collection
143
+ torch.cuda.empty_cache() # Clear cached memory
144
+
145
+ return output_text
146
+
147
+ except Exception as e:
148
+ return f'error occured: {e}'
Tools/ytdownload.py ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!apt update && apt install -y ffmpeg
2
+ import yt_dlp
3
+ from smolagents import tool
4
+ import os
5
+
6
+ @tool
7
+ def youtube_audio_downloader(video_url:str) ->str :
8
+ """
9
+ This tool downloads the audio of a Youtube video given a Youtube Url and returns the downlaoded path.
10
+
11
+ Args:
12
+ video_url: URL of the Youtube video.
13
+ """
14
+ ydl_opts = {
15
+ 'format': 'bestaudio/best',
16
+ 'outtmpl': './downloads/%(title)s.%(ext)s',
17
+ 'postprocessors': [{
18
+ 'key': 'FFmpegExtractAudio',
19
+ 'preferredcodec': 'mp3',
20
+ 'preferredquality': '192',
21
+ }],
22
+ }
23
+
24
+ try:
25
+ os.makedirs("./downloads", exist_ok=True)
26
+ with yt_dlp.YoutubeDL(ydl_opts) as ydl:
27
+ info = ydl.extract_info(video_url, download=True)
28
+ title = info['title']
29
+ filepath = f"./downloads/{title}.mp3"
30
+ return filepath
31
+ except Exception as e:
32
+ return f'error occured: {e}'
33
+
34
+
35
+ @tool
36
+ def youtube_video_downloader(video_url:str) ->str :
37
+ """
38
+ This tool downloads the Youtube video given a Youtube Url and returns the downlaoded path.
39
+
40
+ Args:
41
+ video_url: URL of the Youtube video.
42
+ """
43
+ ydl_opts = {
44
+ #'format': 'bestvideo+bestaudio/best',
45
+ 'format': 'bestvideo[height<=720][vcodec^=avc1]+bestaudio[acodec^=mp4a]/best[ext=mp4]',
46
+ 'outtmpl': 'downloads/%(title)s.%(ext)s',
47
+ 'merge_output_format': 'mp4',
48
+
49
+ 'writesubtitles': True, # Download user-provided subtitles
50
+ 'subtitleslangs': ['en'], # Use your preferred language(s)
51
+ 'embedsubtitles': True, # Enable embedding into video
52
+
53
+ 'postprocessors': [
54
+ {'key': 'FFmpegEmbedSubtitle'} # Actually does the embedding
55
+ ]
56
+ }
57
+
58
+ try:
59
+ os.makedirs("./downloads", exist_ok=True)
60
+
61
+ with yt_dlp.YoutubeDL(ydl_opts) as ydl:
62
+ info = ydl.extract_info(video_url, download=True)
63
+ title = info.get('title')
64
+ filepath = f"./downloads/{title}.mp4"
65
+ if not os.path.exists(filepath):
66
+ return f"Error: File not found after download: {filepath}"
67
+ return filepath
68
+ except Exception as e:
69
+ return f'error occured while downloading the video: {e}'