Skip to content

Commit 2afa1de

Browse files
author
TubeAgentKit
committed
v0.5.0: Client.get_transcript_languages for GET /transcript/languages (free)
1 parent 6cd8af8 commit 2afa1de

7 files changed

Lines changed: 73 additions & 4 deletions

File tree

‎README.md‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,17 @@ Every transcript also says where it came from:
7777
| `cached` | `True` when served from the stored copy rather than fetched from YouTube just now |
7878
| `fetched_at` | ISO 8601 time it was fetched from YouTube |
7979

80+
Not sure which languages a video has? `get_transcript_languages` lists them for free, before you spend a credit:
81+
82+
```python
83+
langs = client.get_transcript_languages("kJQP7kiw5Fk")
84+
print(langs["default_language_code"]) # what get_transcript returns with no language
85+
for lang in langs["languages"]:
86+
print(lang["language_code"], lang["caption_type"], lang["name"]) # "es manual Spanish"
87+
```
88+
89+
An empty `languages` list means the video has captions turned off.
90+
8091
### Batch: many videos at once
8192

8293
Queue up to 100 videos in one call; transcripts are fetched in the background. Submitting is free, each video that returns a transcript costs 1 credit, and failed videos are never charged (10 videos where 2 have no captions = 8 credits).

‎pyproject.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "getyoutubetranscript"
7-
version = "0.4.0"
7+
version = "0.5.0"
88
description = "YouTube transcript API for Python: get YouTube video transcripts, captions and subtitles with timestamps, search YouTube, and list channel and playlist videos. No proxies or headless browser."
99
readme = "README.md"
1010
requires-python = ">=3.9"

‎src/getyoutubetranscript/__init__.py‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@
1111
from .client import Client, signup, verify_signup
1212
from .exceptions import GetYouTubeTranscriptError
1313
from .formatters import format_transcript, to_json, to_srt, to_text, to_timed_text, to_vtt
14-
from .types import BatchData, BatchItem, Segment, TranscriptData
14+
from .types import BatchData, BatchItem, Segment, TranscriptData, TranscriptLanguage, TranscriptLanguagesData
1515
from .webhooks import verify_webhook_signature
1616

17-
__version__ = "0.4.0"
17+
__version__ = "0.5.0"
1818

1919
__all__ = [
2020
"BatchData",
@@ -23,6 +23,8 @@
2323
"GetYouTubeTranscriptError",
2424
"Segment",
2525
"TranscriptData",
26+
"TranscriptLanguage",
27+
"TranscriptLanguagesData",
2628
"format_transcript",
2729
"signup",
2830
"to_json",

‎src/getyoutubetranscript/client.py‎

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
import requests
1313

1414
from .exceptions import GetYouTubeTranscriptError
15-
from .types import BatchData, BatchItem, TranscriptData
15+
from .types import BatchData, BatchItem, TranscriptData, TranscriptLanguagesData
1616

1717
DEFAULT_BASE_URL = "https://getyoutubetranscript.com/api/v1"
1818
DEFAULT_TIMEOUT = 30.0
@@ -183,6 +183,27 @@ def get_transcript(
183183
)
184184
return payload["data"]
185185

186+
def get_transcript_languages(self, video: str) -> TranscriptLanguagesData:
187+
"""List the caption languages a video offers, before fetching one. Free.
188+
189+
Args:
190+
video: Full or short YouTube video URL, or an 11-character video ID.
191+
192+
Returns:
193+
dict with ``video_id``, ``default_language_code`` (what
194+
:meth:`get_transcript` returns with no ``language``; ``None``
195+
without captions) and ``languages``: one
196+
``{"language_code", "name", "caption_type"}`` per language and
197+
caption type (``"manual"`` or ``"auto"``). Empty when the video has
198+
captions turned off.
199+
200+
Raises:
201+
GetYouTubeTranscriptError: e.g. ``code="VIDEO_UNAVAILABLE"`` (HTTP
202+
404) for a private or removed video.
203+
"""
204+
payload = self._get("/transcript/languages", {"v": video})
205+
return payload["data"]
206+
186207
# -- batch --------------------------------------------------------------
187208

188209
def create_batch(

‎src/getyoutubetranscript/types.py‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,22 @@ class TranscriptData(_TranscriptFields, total=False):
4040
cached: bool # True when served from the stored copy rather than fetched now
4141

4242

43+
class TranscriptLanguage(TypedDict):
44+
"""One caption language a video offers."""
45+
46+
language_code: str
47+
name: str # YouTube's display name, e.g. "English (auto-generated)"
48+
caption_type: Literal["manual", "auto"]
49+
50+
51+
class TranscriptLanguagesData(TypedDict):
52+
"""Result of :meth:`Client.get_transcript_languages`."""
53+
54+
video_id: str
55+
default_language_code: Optional[str] # what get_transcript returns with no language; None without captions
56+
languages: List[TranscriptLanguage] # empty when the video has captions turned off
57+
58+
4359
BatchStatus = Literal["queued", "processing", "completed"]
4460

4561

‎tests/live/test_live_api.py‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,15 @@ def test_get_transcript_live(client: Client):
6868
assert result["fetched_at"]
6969

7070

71+
def test_get_transcript_languages_live(client: Client):
72+
"""Free endpoint."""
73+
result = client.get_transcript_languages(KNOWN_VIDEO_ID)
74+
assert result["video_id"] == KNOWN_VIDEO_ID
75+
assert result["languages"]
76+
assert all(lang["caption_type"] in ("manual", "auto") for lang in result["languages"])
77+
assert result["default_language_code"] in {lang["language_code"] for lang in result["languages"]}
78+
79+
7180
def test_batch_live(client: Client):
7281
"""1 credit for the one real video; the invalid one fails and is never charged."""
7382
batch = client.create_batch([KNOWN_VIDEO_ID, "aaaaaaaaaaa"])

‎tests/test_batch.py‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,16 @@ def _batch(status: str = "completed", **extra):
2222
return {"success": True, "data": {"batch_id": BATCH_ID, "status": status, "total": 3, "pending": 0, **extra}}
2323

2424

25+
@responses.activate
26+
def test_get_transcript_languages_sends_video_param(client: Client):
27+
data = {"video_id": "kJQP7kiw5Fk", "default_language_code": "en",
28+
"languages": [{"language_code": "en", "name": "English", "caption_type": "manual"}]}
29+
responses.add(responses.GET, f"{BASE_URL}/transcript/languages", json={"success": True, "data": data}, status=200)
30+
31+
assert client.get_transcript_languages("https://youtu.be/kJQP7kiw5Fk") == data
32+
assert responses.calls[0].request.url == f"{BASE_URL}/transcript/languages?v=https%3A%2F%2Fyoutu.be%2FkJQP7kiw5Fk"
33+
34+
2535
@responses.activate
2636
def test_create_batch_posts_json_body_and_idempotency_header(client: Client):
2737
responses.add(responses.POST, f"{BASE_URL}/batch", json=_batch("queued", webhook_secret="whsec_x"), status=202)

0 commit comments

Comments
 (0)