Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrate the youtube-transcript-api library as a tool #10755

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions api/core/tools/provider/builtin/transcript/_assets/icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
81 changes: 81 additions & 0 deletions api/core/tools/provider/builtin/transcript/tools/transcript.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
from typing import Any, Union
from urllib.parse import parse_qs, urlparse

from youtube_transcript_api import YouTubeTranscriptApi

from core.tools.entities.tool_entities import ToolInvokeMessage
from core.tools.tool.builtin_tool import BuiltinTool


class YouTubeTranscriptTool(BuiltinTool):
def _invoke(
self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
"""
Invoke the YouTube transcript tool
"""
try:
# Extract parameters with defaults
video_input = tool_parameters["video_id"]
language = tool_parameters.get("language")
output_format = tool_parameters.get("format", "text")
preserve_formatting = tool_parameters.get("preserve_formatting", False)
proxy = tool_parameters.get("proxy")
cookies = tool_parameters.get("cookies")

# Extract video ID from URL if needed
video_id = self._extract_video_id(video_input)

# Common kwargs for API calls
kwargs = {"proxies": {"https": proxy} if proxy else None, "cookies": cookies}

try:
if language:
transcript_list = YouTubeTranscriptApi.list_transcripts(video_id, **kwargs)
try:
transcript = transcript_list.find_transcript([language])
except:
# If requested language not found, try translating from English
transcript = transcript_list.find_transcript(["en"]).translate(language)
transcript_data = transcript.fetch()
else:
transcript_data = YouTubeTranscriptApi.get_transcript(
video_id, preserve_formatting=preserve_formatting, **kwargs
)

# Format output
formatter_class = {
"json": "JSONFormatter",
"pretty": "PrettyPrintFormatter",
"srt": "SRTFormatter",
"vtt": "WebVTTFormatter",
}.get(output_format)

if formatter_class:
from youtube_transcript_api import formatters

formatter = getattr(formatters, formatter_class)()
formatted_transcript = formatter.format_transcript(transcript_data)
else:
formatted_transcript = " ".join(entry["text"] for entry in transcript_data)

return self.create_text_message(text=formatted_transcript)

except Exception as e:
return self.create_text_message(text=f"Error getting transcript: {str(e)}")

except Exception as e:
return self.create_text_message(text=f"Error processing request: {str(e)}")

def _extract_video_id(self, video_input: str) -> str:
"""
Extract video ID from URL or return as-is if already an ID
"""
if "youtube.com" in video_input or "youtu.be" in video_input:
# Parse URL
parsed_url = urlparse(video_input)
if "youtube.com" in parsed_url.netloc:
return parse_qs(parsed_url.query)["v"][0]
else: # youtu.be
return parsed_url.path[1:]
return video_input # Assume it's already a video ID
102 changes: 102 additions & 0 deletions api/core/tools/provider/builtin/transcript/tools/transcript.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
identity:
name: free_youtube_transcript
author: Tao Wang
label:
en_US: Free YouTube Transcript API
zh_Hans: 免费获取 YouTube 转录
description:
human:
en_US: Get transcript from a YouTube video for free.
zh_Hans: 免费获取 YouTube 视频的转录文案。
llm: A tool for retrieving transcript from YouTube videos.
parameters:
- name: video_id
type: string
required: true
label:
en_US: Video ID/URL
zh_Hans: 视频ID
human_description:
en_US: Used to define the video from which the transcript will be fetched. You can find the id in the video url. For example - https://www.youtube.com/watch?v=video_id.
zh_Hans: 您要哪条视频的转录文案?您可以在视频链接中找到id。例如 - https://www.youtube.com/watch?v=video_id。
llm_description: Used to define the video from which the transcript will be fetched. For example - https://www.youtube.com/watch?v=video_id.
form: llm
- name: language
type: string
required: false
label:
en_US: Language Code
zh_Hans: 语言
human_description:
en_US: Language code (e.g. 'en', 'zh') for the transcript.
zh_Hans: 字幕语言代码(如'en'、'zh')。留空则自动选择。
llm_description: Used to set the language for transcripts.
form: form
- name: format
type: select
required: false
default: text
options:
- value: text
label:
en_US: Plain Text
zh_Hans: 纯文本
- value: json
label:
en_US: JSON Format
zh_Hans: JSON 格式
- value: pretty
label:
en_US: Pretty Print Format
zh_Hans: 美化格式
- value: srt
label:
en_US: SRT Format
zh_Hans: SRT 格式
- value: vtt
label:
en_US: WebVTT Format
zh_Hans: WebVTT 格式
label:
en_US: Output Format
zh_Hans: 输出格式
human_description:
en_US: Format of the transcript output
zh_Hans: 字幕输出格式
llm_description: The format to output the transcript in. Options are text (plain text), json (raw transcript data), srt (SubRip format), or vtt (WebVTT format)
form: form
- name: preserve_formatting
type: boolean
required: false
default: false
label:
en_US: Preserve Formatting
zh_Hans: 保留格式
human_description:
en_US: Keep HTML formatting elements like <i> (italics) and <b> (bold)
zh_Hans: 保留HTML格式元素,如<i>(斜体)和<b>(粗体)
llm_description: Whether to preserve HTML formatting elements in the transcript text
form: form
- name: proxy
type: string
required: false
label:
en_US: HTTPS Proxy
zh_Hans: HTTPS 代理
human_description:
en_US: HTTPS proxy URL (e.g. https://user:pass@domain:port)
zh_Hans: HTTPS 代理地址(如 https://user:pass@domain:port)
llm_description: HTTPS proxy to use for the request. Format should be https://user:pass@domain:port
form: form
- name: cookies
type: string
required: false
label:
en_US: Cookies File Path
zh_Hans: Cookies 文件路径
human_description:
en_US: Path to cookies.txt file for accessing age-restricted videos
zh_Hans: 用于访问年龄限制视频的 cookies.txt 文件路径
llm_description: Path to a cookies.txt file containing YouTube cookies, needed for accessing age-restricted videos
form: form

12 changes: 12 additions & 0 deletions api/core/tools/provider/builtin/transcript/transcript.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from typing import Any, Dict
from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
from core.tools.errors import ToolProviderCredentialValidationError

from core.tools.provider.builtin.transcript.tools.transcript import YouTubeTranscriptTool

class YouTubeTranscriptProvider(BuiltinToolProviderController):
def _validate_credentials(self, credentials: Dict[str, Any]) -> None:
"""
No credentials needed for YouTube Transcript API
"""
pass
14 changes: 14 additions & 0 deletions api/core/tools/provider/builtin/transcript/transcript.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
identity:
author: Tao Wang
name: transcript
label:
en_US: Transcript
zh_Hans: Transcript
description:
en_US: Get transcripts from YouTube videos
zh_Hans: 获取 YouTube 视频的字幕/转录文本
icon: icon.svg
tags:
- videos
credentials_for_provider:
# No credentials needed for this provider as the YouTube Transcript API is free
18 changes: 16 additions & 2 deletions api/poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions api/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ websocket-client = "~1.7.0"
werkzeug = "~3.0.1"
xinference-client = "0.15.2"
yarl = "~1.9.4"
youtube-transcript-api = "~0.6.2"
zhipuai = "~2.1.5"
# Before adding new dependency, consider place it in alphabet order (a-z) and suitable group.

Expand Down
Loading