2121from pydantic import Field , BaseModel
2222from youtube_transcript_api import YouTubeTranscriptApi
2323from youtube_transcript_api .proxies import WebshareProxyConfig , GenericProxyConfig , ProxyConfig
24+ from yt_dlp import YoutubeDL
25+ from yt_dlp .extractor .youtube import YoutubeIE
2426
2527
2628@dataclass (frozen = True )
2729class AppContext :
2830 http_client : requests .Session
2931 ytt_api : YouTubeTranscriptApi
32+ dlp : YoutubeDL
3033
3134
3235@asynccontextmanager
3336async def _app_lifespan (_server : FastMCP , proxy_config : ProxyConfig | None ) -> AsyncIterator [AppContext ]:
34- with requests .Session () as http_client :
37+ with requests .Session () as http_client , YoutubeDL ( params = { "quiet" : True }, auto_init = False ) as dlp :
3538 ytt_api = YouTubeTranscriptApi (http_client = http_client , proxy_config = proxy_config )
36- yield AppContext (http_client = http_client , ytt_api = ytt_api )
39+ dlp .add_info_extractor (YoutubeIE ())
40+ yield AppContext (http_client = http_client , ytt_api = ytt_api , dlp = dlp )
41+
42+
43+ class Transcript (BaseModel ):
44+ """Transcript of a YouTube video."""
45+
46+ title : str = Field (description = "Title of the video" )
47+ transcript : str = Field (description = "Transcript of the video" )
48+ next_cursor : str | None = Field (description = "Cursor to retrieve the next page of the transcript" , default = None )
49+
50+
51+ class VideoInfo (BaseModel ):
52+ """Video information."""
53+
54+ title : str = Field (description = "Title of the video" )
55+ description : str = Field (description = "Description of the video" )
56+ uploader : str = Field (description = "Uploader of the video" )
3757
3858
3959@lru_cache
@@ -54,12 +74,10 @@ def _get_transcript(ctx: AppContext, video_id: str, lang: str) -> Tuple[str, lis
5474 return title , [item .text for item in transcripts ]
5575
5676
57- class Transcript (BaseModel ):
58- """Transcript of a YouTube video."""
59-
60- title : str = Field (description = "Title of the video" )
61- transcript : str = Field (description = "Transcript of the video" )
62- next_cursor : str | None = Field (description = "Cursor to retrieve the next page of the transcript" , default = None )
77+ @lru_cache
78+ def _get_video_info (ctx : AppContext , video_url : str ) -> VideoInfo :
79+ res = ctx .dlp .extract_info (video_url , download = False )
80+ return VideoInfo (title = res ["title" ], description = res ["description" ], uploader = res ["uploader" ])
6381
6482
6583def server (
@@ -111,7 +129,15 @@ async def get_transcript(
111129
112130 return Transcript (title = title , transcript = res [:- 1 ], next_cursor = cursor )
113131
132+ @mcp .tool ()
133+ def get_video_info (
134+ ctx : Context [ServerSession , AppContext ],
135+ url : str = Field (description = "The URL of the YouTube video" ),
136+ ) -> VideoInfo :
137+ """Retrieves the video information."""
138+ return _get_video_info (ctx .request_context .lifespan_context , url )
139+
114140 return mcp
115141
116142
117- __all__ : Final = ["server" , "Transcript" ]
143+ __all__ : Final = ["server" , "Transcript" , "VideoInfo" ]
0 commit comments