import pytest from youtube import extract_youtube_id, InvalidYoutubeUrl @pytest.mark.parametrize("url,expected", [ ("https://www.youtube.com/watch?v=dQw4w9WgXcQ", "dQw4w9WgXcQ"), ("https://youtu.be/dQw4w9WgXcQ", "dQw4w9WgXcQ"), ("https://www.youtube.com/watch?v=dQw4w9WgXcQ&list=PL", "dQw4w9WgXcQ"), ("https://m.youtube.com/watch?v=dQw4w9WgXcQ", "dQw4w9WgXcQ"), ("https://www.youtube.com/shorts/dQw4w9WgXcQ", "dQw4w9WgXcQ"), ]) def test_extract_youtube_id_valid(url, expected): assert extract_youtube_id(url) == expected @pytest.mark.parametrize("url", [ "https://example.com/watch?v=dQw4w9WgXcQ", "not a url", "https://www.youtube.com/", "https://youtu.be/", "", ]) def test_extract_youtube_id_invalid(url): with pytest.raises(InvalidYoutubeUrl): extract_youtube_id(url) from unittest.mock import MagicMock, patch from pathlib import Path from youtube import fetch_metadata, download_audio, YoutubeFetchError, VideoTooLong @patch("youtube.yt_dlp.YoutubeDL") def test_fetch_metadata_returns_normalized_dict(mock_ydl_cls): mock_ydl = MagicMock() mock_ydl_cls.return_value.__enter__.return_value = mock_ydl mock_ydl.extract_info.return_value = { "id": "dQw4w9WgXcQ", "title": "Rick Astley - Never Gonna Give You Up (Official Video)", "uploader": "Rick Astley", "artist": None, "duration": 213, "thumbnail": "https://i.ytimg.com/vi/dQw4w9WgXcQ/hqdefault.jpg", } meta = fetch_metadata("https://youtu.be/dQw4w9WgXcQ") assert meta["youtube_id"] == "dQw4w9WgXcQ" assert meta["artist"] == "Rick Astley" assert meta["title"] == "Never Gonna Give You Up (Official Video)" assert meta["duration_sec"] == 213 assert meta["thumbnail_url"].startswith("https://i.ytimg.com/") @patch("youtube.yt_dlp.YoutubeDL") def test_fetch_metadata_raises_on_too_long(mock_ydl_cls): mock_ydl = MagicMock() mock_ydl_cls.return_value.__enter__.return_value = mock_ydl mock_ydl.extract_info.return_value = { "id": "abc12345678", "title": "x", "uploader": "x", "duration": 99999, "thumbnail": "", } with pytest.raises(VideoTooLong): fetch_metadata("https://youtu.be/abc12345678", max_duration_sec=600) @patch("youtube.yt_dlp.YoutubeDL") def test_fetch_metadata_raises_fetch_error(mock_ydl_cls): from yt_dlp.utils import DownloadError mock_ydl = MagicMock() mock_ydl_cls.return_value.__enter__.return_value = mock_ydl mock_ydl.extract_info.side_effect = DownloadError("Sign in to confirm you're not a bot") with pytest.raises(YoutubeFetchError): fetch_metadata("https://youtu.be/abc12345678") @patch("youtube.yt_dlp.YoutubeDL") def test_download_audio_writes_file(mock_ydl_cls, tmp_path): mock_ydl = MagicMock() mock_ydl_cls.return_value.__enter__.return_value = mock_ydl # Simulate yt-dlp writing the output file: def fake_download(urls): (tmp_path / "dQw4w9WgXcQ.m4a").write_bytes(b"fake audio") mock_ydl.download.side_effect = fake_download out = download_audio("https://youtu.be/dQw4w9WgXcQ", "dQw4w9WgXcQ", tmp_path) assert out.exists() assert out.suffix == ".m4a"