First_agent_template / tools /youtube_transcript.py
Realistvik0's picture
Update tools/youtube_transcript.py
3215ba2 verified
Raw History Blame Contribute Delete
3.77 kB
import re
from smolagents.tools import Tool
class YouTubeTranscriptTool(Tool):
name = "youtube_transcript"
description = (
"Fetches the spoken transcript of a YouTube video. "
"Use this for ANY question about the content of a YouTube video — "
"what is said, shown, demonstrated, or described in it. "
"Pass the full YouTube URL or just the video ID."
)
inputs = {
"url": {
"type": "string",
"description": (
"The YouTube video URL (e.g. https://www.youtube.com/watch?v=XXXXXXXXXXX) "
"or bare video ID (e.g. XXXXXXXXXXX)."
),
}
}
output_type = "string"
def __init__(self, *args, **kwargs):
self.is_initialized = False
def forward(self, url: str) -> str:
try:
from youtube_transcript_api import YouTubeTranscriptApi
from youtube_transcript_api._errors import (
TranscriptsDisabled,
NoTranscriptFound,
VideoUnavailable,
CouldNotRetrieveTranscript,
)
except ImportError:
return (
"youtube-transcript-api is not installed. "
"Add 'youtube-transcript-api' to requirements.txt and rebuild."
)
video_id = self._extract_video_id(url.strip())
if not video_id:
return f"Could not extract a YouTube video ID from: {url}"
api = YouTubeTranscriptApi()
# Try to fetch English first; fall back to any available language.
# v1.x: api.fetch(video_id, languages=[...]) is the simplest path.
# api.list(video_id).find_transcript([...]).fetch() gives language control.
try:
transcript_list = api.list(video_id)
try:
transcript = transcript_list.find_transcript(
["en", "en-US", "en-GB"]
)
except NoTranscriptFound:
# Fall back to whatever is available
transcript = next(iter(transcript_list))
entries = transcript.fetch()
# v1.x entries are FetchedTranscriptSnippet objects with .text attribute
# but also support dict-style access for backwards compat
try:
text = " ".join(e.text for e in entries)
except AttributeError:
text = " ".join(e["text"] for e in entries)
if len(text) > 12000:
text = text[:12000] + "\n\n[Transcript truncated at 12,000 characters]"
return text
except TranscriptsDisabled:
return (
f"Transcripts are disabled for video {video_id}. "
"Try searching for the video title or description instead."
)
except VideoUnavailable:
return f"Video {video_id} is unavailable or does not exist."
except NoTranscriptFound:
return (
f"No transcript found for video {video_id}. "
"The video may not have captions."
)
except CouldNotRetrieveTranscript as e:
return f"Could not retrieve transcript for {video_id}: {e}"
except Exception as e:
return f"Could not retrieve transcript for {video_id}: {e}"
@staticmethod
def _extract_video_id(url: str) -> str:
"""Return the 11-character YouTube video ID from a URL or bare ID."""
patterns = [
r"(?:v=|youtu\.be/|embed/|shorts/)([A-Za-z0-9_-]{11})",
r"^([A-Za-z0-9_-]{11})$",
]
for pattern in patterns:
match = re.search(pattern, url)
if match:
return match.group(1)
return ""