File size: 3,772 Bytes
a9e8b4e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3215ba2
a9e8b4e
 
 
 
 
 
 
 
 
 
 
3215ba2
 
 
 
 
a9e8b4e
3215ba2
a9e8b4e
 
3215ba2
 
 
a9e8b4e
3215ba2
a9e8b4e
 
 
3215ba2
 
 
 
 
 
a9e8b4e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3215ba2
 
a9e8b4e
 
 
 
 
 
 
 
3215ba2
a9e8b4e
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
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 ""