yt-transcript-rs
yt-transcript-rs
is a Rust library for fetching and working with YouTube video transcripts. It allows you to retrieve transcripts in various languages, list available transcripts for a video, and fetch video details.

This project is heavily inspired by the Python module youtube-transcript-api originally developed by Jonas Depoix.
Table of Contents
Features
- Fetch transcripts from YouTube videos in various languages
- List all available transcripts for a video
- Retrieve translations of transcripts
- Get detailed information about YouTube videos
- Support for proxy configuration and cookie authentication
Installation
Add yt-transcript-rs
to your Cargo.toml
:
cargo add yt-transcript-rs
Or manually add it to your Cargo.toml
:
[dependencies]
yt-transcript-rs = "0.1.0"
Usage
Fetch a transcript
use anyhow::Result;
use yt_transcript_rs::api::YouTubeTranscriptApi;
#[tokio::main]
async fn main() -> Result<()> {
let api = YouTubeTranscriptApi::new(None, None, None)?;
let video_id = "5MuIMqhT8DM";
let languages = &["en"];
let preserve_formatting = false;
println!("Fetching transcript for video ID: {}", video_id);
match api.fetch_transcript(video_id, languages, preserve_formatting).await {
Ok(transcript) => {
println!("Successfully fetched transcript!");
println!("Video ID: {}", transcript.video_id);
println!(
"Language: {} ({})",
transcript.language, transcript.language_code
);
println!("Is auto-generated: {}", transcript.is_generated);
println!("Number of snippets: {}", transcript.snippets.len());
println!("\nTranscript content:");
for (_i, snippet) in transcript.snippets.iter().take(5).enumerate() {
println!(
"[{:.1}-{:.1}s] {}",
snippet.start,
snippet.start + snippet.duration,
snippet.text
);
}
println!("... (truncated)");
}
Err(e) => {
println!("Failed to fetch transcript: {:?}", e);
}
}
Ok(())
}
List available transcripts
use anyhow::Result;
use yt_transcript_rs::api::YouTubeTranscriptApi;
#[tokio::main]
async fn main() -> Result<()> {
let api = YouTubeTranscriptApi::new(None, None, None)?;
let video_id = "arj7oStGLkU";
println!("Listing available transcripts for video ID: {}", video_id);
match api.list_transcripts(video_id).await {
Ok(transcript_list) => {
println!("Successfully retrieved transcript list!");
println!("Video ID: {}", transcript_list.video_id);
let mut count = 0;
let mut translatable_count = 0;
println!("\nAvailable transcripts:");
for transcript in &transcript_list {
count += 1;
let translatable = if transcript.is_translatable() {
translatable_count += 1;
"[translatable]"
} else {
""
};
println!(
"{}: {} ({}) {}",
count, transcript.language, transcript.language_code, translatable
);
if transcript.is_translatable() && count == 1 {
println!(" Available translations:");
for (i, lang) in transcript.translation_languages.iter().take(5).enumerate() {
println!(" {}: {} ({})", i + 1, lang.language, lang.language_code);
}
if transcript.translation_languages.len() > 5 {
println!(
" ... and {} more",
transcript.translation_languages.len() - 5
);
}
}
}
println!("\nTotal transcripts: {}", count);
println!("Translatable transcripts: {}", translatable_count);
}
Err(e) => {
println!("Failed to list transcripts: {:?}", e);
}
}
Ok(())
}
Fetch video details
use anyhow::Result;
use yt_transcript_rs::api::YouTubeTranscriptApi;
#[tokio::main]
async fn main() -> Result<()> {
println!("YouTube Video Details Example");
println!("------------------------------");
let api = YouTubeTranscriptApi::new(None, None, None)?;
let video_id = "arj7oStGLkU";
println!("Fetching video details for: {}", video_id);
match api.fetch_video_details(video_id).await {
Ok(details) => {
println!("\nVideo Details:");
println!("-------------");
println!("Video ID: {}", details.video_id);
println!("Title: {}", details.title);
println!("Author: {}", details.author);
println!("Channel ID: {}", details.channel_id);
println!("View Count: {}", details.view_count);
println!("Length: {} seconds", details.length_seconds);
println!("Is Live Content: {}", details.is_live_content);
if let Some(keywords) = details.keywords {
println!("\nKeywords:");
for (i, keyword) in keywords.iter().enumerate().take(10) {
println!(" {}: {}", i + 1, keyword);
}
if keywords.len() > 10 {
println!(" ... and {} more", keywords.len() - 10);
}
}
println!("\nThumbnails: {} available", details.thumbnails.len());
for (i, thumb) in details.thumbnails.iter().enumerate() {
println!(
" {}: {}x{} - {}",
i + 1,
thumb.width,
thumb.height,
thumb.url
);
}
println!("\nDescription:");
let description = if details.short_description.len() > 300 {
format!("{}...", &details.short_description[..300])
} else {
details.short_description.clone()
};
println!("{}", description);
}
Err(e) => {
println!("Failed to fetch video details: {:?}", e);
}
}
Ok(())
}
Requirements
- Rust 1.56 or higher
tokio
for async execution
Advanced Usage
Using Proxies
You can configure the API to use a proxy server:
use anyhow::Result;
use yt_transcript_rs::api::{YouTubeTranscriptApi, ProxyConfig};
#[tokio::main]
async fn main() -> Result<()> {
let proxy = ProxyConfig {
url: "http://your-proxy-server:8080".to_string(),
username: Some("username".to_string()),
password: Some("password".to_string()),
};
let api = YouTubeTranscriptApi::new(Some(proxy), None, None)?;
let video_id = "5MuIMqhT8DM";
let languages = &["en"];
let transcript = api.fetch_transcript(video_id, languages, false).await?;
println!("Fetched transcript via proxy!");
Ok(())
}
Using Cookie Authentication
For videos that require authentication:
use anyhow::Result;
use yt_transcript_rs::api::YouTubeTranscriptApi;
#[tokio::main]
async fn main() -> Result<()> {
let cookies = "CONSENT=YES+; VISITOR_INFO1_LIVE=abcd1234; LOGIN_INFO=AFmmF2swRQIhAI...";
let api = YouTubeTranscriptApi::new(None, Some(cookies.to_string()), None)?;
let video_id = "private_video_id";
let languages = &["en"];
let transcript = api.fetch_transcript(video_id, languages, false).await?;
println!("Successfully authenticated and fetched transcript!");
Ok(())
}
Error Handling
The library uses the anyhow
crate for error handling. Here's an example of more robust error handling:
use anyhow::Result;
use yt_transcript_rs::api::YouTubeTranscriptApi;
use yt_transcript_rs::error::TranscriptError;
#[tokio::main]
async fn main() -> Result<()> {
let api = YouTubeTranscriptApi::new(None, None, None)?;
let video_id = "5MuIMqhT8DM";
match api.fetch_transcript(video_id, &["en"], false).await {
Ok(transcript) => {
println!("Successfully fetched transcript with {} snippets", transcript.snippets.len());
Ok(())
},
Err(e) => {
if let Some(transcript_err) = e.downcast_ref::<TranscriptError>() {
match transcript_err {
TranscriptError::NoTranscriptFound => {
println!("No transcript found for this video");
},
TranscriptError::TranslationLanguageNotAvailable => {
println!("The requested translation language is not available");
},
TranscriptError::NoTranscriptAvailable => {
println!("No transcript is available for this video");
},
_ => println!("Other transcript error: {:?}", transcript_err),
}
} else {
println!("Unknown error: {:?}", e);
}
Err(e)
}
}
}
License
This project is licensed under the MIT License - see the LICENSE file for details.
Contributing
Contributions are welcome! Here's how you can contribute:
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature
)
- Commit your changes (
git commit -m 'Add some amazing feature'
)
- Push to the branch (
git push origin feature/amazing-feature
)
- Open a Pull Request
Development Setup
git clone https://github.com/yourusername/yt-transcript-rs.git
cd yt-transcript-rs
cargo build
cargo test
Acknowledgments