feat: use yt-dlp as primary transcript fetcher, fallback to youtube-transcript-api
- Add _ytdlp_transcript() — fetch transcripts via yt-dlp Python API (supports json3/srv3/vtt/srt subtitle formats, cookies.txt auth) - Add _parse_subtitles() — parse subtitle formats into plain text - Keep _yttranscript_api_transcript() as fallback - download_transcript() tries yt-dlp first, then youtube-transcript-api - Add yt-dlp to requirements.txt - Update tests to mock both paths (all 23 pass) - Document Google Data API v3 caption limitations in architecture.md (captions.list works, captions.download=403 for non-owned videos)
This commit is contained in:
@@ -43,7 +43,8 @@ async def test_download_transcript_success():
|
||||
mock_trans = MockTranscript()
|
||||
mock_list = MockTranscriptList(mock_trans)
|
||||
|
||||
with patch("youtube_transcript_api.YouTubeTranscriptApi.list", return_value=mock_list):
|
||||
with patch.object(summarizer, "_ytdlp_transcript", return_value=None), \
|
||||
patch("youtube_transcript_api.YouTubeTranscriptApi.list", return_value=mock_list):
|
||||
text = summarizer.download_transcript("test_vid")
|
||||
assert text == "Welcome to this video tutorial. Today we are learning about Svelte 5 and runes."
|
||||
|
||||
@@ -60,7 +61,8 @@ async def test_summarize_video_success(test_db):
|
||||
mock_list = MockTranscriptList(mock_trans)
|
||||
mock_response = _mock_deepseek_response("**Mock Core Hook**\n- Takeaway 1\n- Takeaway 2\nVerdict line.")
|
||||
|
||||
with patch("youtube_transcript_api.YouTubeTranscriptApi.list", return_value=mock_list), \
|
||||
with patch.object(summarizer, "_ytdlp_transcript", return_value=None), \
|
||||
patch("youtube_transcript_api.YouTubeTranscriptApi.list", return_value=mock_list), \
|
||||
patch("httpx.AsyncClient.post", new_callable=AsyncMock, return_value=mock_response) as mock_post:
|
||||
|
||||
summary = await summarizer.summarize_video("summarize_vid")
|
||||
@@ -106,7 +108,8 @@ async def test_summarize_video_deepseek_error_leaves_null(test_db):
|
||||
"429 Rate Limited", request=MagicMock(), response=mock_response
|
||||
)
|
||||
|
||||
with patch("youtube_transcript_api.YouTubeTranscriptApi.list", return_value=mock_list), \
|
||||
with patch.object(summarizer, "_ytdlp_transcript", return_value=None), \
|
||||
patch("youtube_transcript_api.YouTubeTranscriptApi.list", return_value=mock_list), \
|
||||
patch("httpx.AsyncClient.post", new_callable=AsyncMock, return_value=mock_response):
|
||||
|
||||
result = await summarizer.summarize_video("err_vid")
|
||||
@@ -133,7 +136,8 @@ async def test_summarize_all_pending(test_db):
|
||||
mock_list = MockTranscriptList(mock_trans)
|
||||
mock_response = _mock_deepseek_response("Fresh summary")
|
||||
|
||||
with patch("youtube_transcript_api.YouTubeTranscriptApi.list", return_value=mock_list), \
|
||||
with patch.object(summarizer, "_ytdlp_transcript", return_value=None), \
|
||||
patch("youtube_transcript_api.YouTubeTranscriptApi.list", return_value=mock_list), \
|
||||
patch("httpx.AsyncClient.post", new_callable=AsyncMock, return_value=mock_response) as mock_post:
|
||||
|
||||
await summarizer.summarize_all_pending()
|
||||
|
||||
Reference in New Issue
Block a user