File size: 13,696 Bytes
4a4777a 97f04a9 39acdce a92dd5e cd0bfbf 272841b 39acdce 97f04a9 a92dd5e 97f04a9 c39cd97 272841b fc9aec5 3cabdbd 97f04a9 3cabdbd 97f04a9 272841b c16d7a9 f3408d8 fc9aec5 c227094 fc9aec5 c227094 328e22b fc9aec5 c16d7a9 97f04a9 f9962fa 67e6144 c4dc4b2 78b647b f9962fa 81f1abc 79befd6 81f1abc e276f3d eab63b5 e276f3d c3ecb1d d68bda8 c3ecb1d 79befd6 c3ecb1d d68bda8 c3ecb1d d68bda8 c3ecb1d 272841b 8f73fed 272841b 9a86686 272841b 82321d6 c16d7a9 97f04a9 272841b 8f73fed 272841b 9a86686 272841b 737064b c16d7a9 737064b eec6f64 fc9aec5 eec6f64 737064b 272841b 8f73fed 272841b 9a86686 272841b 4a4777a c16d7a9 4a4777a eec6f64 8f73fed eec6f64 4a4777a 272841b 8f73fed 272841b 9a86686 272841b 4a4777a c16d7a9 272841b 4a4777a eec6f64 fc9aec5 eec6f64 4a4777a 272841b 8f73fed 272841b 9a86686 272841b 4a4777a c16d7a9 272841b 4a4777a eec6f64 fc9aec5 eec6f64 4a4777a 328e22b c16d7a9 97f04a9 328e22b c16d7a9 97f04a9 24c3a19 272841b 8f73fed 272841b 9a86686 272841b 24c3a19 328e22b 24c3a19 82321d6 c16d7a9 24c3a19 f3408d8 24c3a19 272841b 8f73fed 272841b 9a86686 272841b 24c3a19 328e22b 24c3a19 82321d6 c16d7a9 24c3a19 b2aa04f fc9aec5 b2aa04f cc30577 fc9aec5 b22e0c8 f9dad3c cc30577 328e22b fc9aec5 25de36d 24c3a19 328e22b c16d7a9 25de36d bdaa6bc 25de36d 24c3a19 328e22b fc9aec5 25de36d bdaa6bc 272841b 25de36d fc9aec5 24c3a19 328e22b c16d7a9 25de36d bdaa6bc 25de36d 24c3a19 39acdce e573655 39acdce 9a86686 e573655 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 | import os
import random
import pytest
from datetime import datetime
from unittest import mock
from unittest.mock import MagicMock, Mock
from urllib.error import HTTPError
from pytube import request, Stream
@mock.patch("pytube.streams.request")
def test_stream_to_buffer(mock_request, cipher_signature):
# Given
stream_bytes = iter(
[
bytes(os.urandom(8 * 1024)),
bytes(os.urandom(8 * 1024)),
bytes(os.urandom(8 * 1024)),
]
)
mock_request.stream.return_value = stream_bytes
buffer = MagicMock()
# When
cipher_signature.streams[0].stream_to_buffer(buffer)
# Then
assert buffer.write.call_count == 3
def test_filesize(cipher_signature):
assert cipher_signature.streams[0].filesize == 3399554
def test_filesize_kb(cipher_signature):
assert cipher_signature.streams[0].filesize_kb == float(3319.877)
def test_filesize_mb(cipher_signature):
assert cipher_signature.streams[0].filesize_mb == float(3.243)
def test_filesize_gb(cipher_signature):
assert cipher_signature.streams[0].filesize_gb == float(0.004)
def test_filesize_approx(cipher_signature):
stream = cipher_signature.streams[0]
assert stream.filesize_approx == 3403320
stream.bitrate = None
assert stream.filesize_approx == 3399554
def test_default_filename(cipher_signature):
expected = "YouTube Rewind 2019 For the Record YouTubeRewind.3gpp"
stream = cipher_signature.streams[0]
assert stream.default_filename == expected
def test_title(cipher_signature):
expected = "YouTube Rewind 2019: For the Record | #YouTubeRewind"
assert cipher_signature.title == expected
def test_expiration(cipher_signature):
assert cipher_signature.streams[0].expiration >= datetime(2020, 10, 30, 5, 39, 41)
def test_caption_tracks(presigned_video):
assert len(presigned_video.caption_tracks) == 13
def test_captions(presigned_video):
assert len(presigned_video.captions) == 13
def test_description(cipher_signature):
expected = (
"In 2018, we made something you didn’t like. "
"For Rewind 2019, let’s see what you DID like.\n\n"
"Celebrating the creators, music and moments "
"that mattered most to you in 2019. \n\n"
"To learn how the top lists in Rewind were generated: "
"https://rewind.youtube/about\n\n"
"Top lists featured the following channels:\n\n"
"@1MILLION Dance Studio \n@A4 \n@Anaysa \n"
"@Andymation \n@Ariana Grande \n@Awez Darbar \n"
"@AzzyLand \n@Billie Eilish \n@Black Gryph0n \n"
"@BLACKPINK \n@ChapkisDanceUSA \n@Daddy Yankee \n"
"@David Dobrik \n@Dude Perfect \n@Felipe Neto \n"
"@Fischer's-フィッシャーズ- \n@Galen Hooks \n@ibighit \n"
"@James Charles \n@jeffreestar \n@Jelly \n@Kylie Jenner \n"
"@LazarBeam \n@Lil Dicky \n@Lil Nas X \n@LOUD \n@LOUD Babi \n"
"@LOUD Coringa \n@Magnet World \n@MrBeast \n"
"@Nilson Izaias Papinho Oficial \n@Noah Schnapp\n"
"@백종원의 요리비책 Paik's Cuisine \n@Pencilmation \n@PewDiePie \n"
"@SethEverman \n@shane \n@Shawn Mendes \n@Team Naach \n"
"@whinderssonnunes \n@워크맨-Workman \n@하루한끼 one meal a day \n\n"
"To see the full list of featured channels in Rewind 2019, "
"visit: https://rewind.youtube/about"
)
assert cipher_signature.description == expected
def test_rating(cipher_signature):
"""Test the rating value of a YouTube object.
This changes each time we rebuild the json files, so we want to use
an estimate of where it will be. The two values seen to make this
estimate were 2.073431 and 2.0860765. This represents a range of
~0.007 below and ~0.006 above 2.08. Allowing for up to 0.02 in either
direction should provide a steady indicator of correctness.
"""
assert abs(cipher_signature.rating - 2.08) < 0.02
def test_length(cipher_signature):
assert cipher_signature.length == 337
def test_views(cipher_signature):
assert cipher_signature.views >= 108531745
@mock.patch(
"pytube.request.head", MagicMock(return_value={"content-length": "6796391"})
)
@mock.patch(
"pytube.request.stream",
MagicMock(return_value=iter([str(random.getrandbits(8 * 1024))])),
)
def test_download(cipher_signature):
with mock.patch("pytube.streams.open", mock.mock_open(), create=True):
stream = cipher_signature.streams[0]
stream.download()
@mock.patch(
"pytube.request.head", MagicMock(return_value={"content-length": "16384"})
)
@mock.patch(
"pytube.request.stream",
MagicMock(return_value=iter([str(random.getrandbits(8 * 1024))])),
)
@mock.patch("pytube.streams.target_directory", MagicMock(return_value="/target"))
def test_download_with_prefix(cipher_signature):
with mock.patch("pytube.streams.open", mock.mock_open(), create=True):
stream = cipher_signature.streams[0]
file_path = stream.download(filename_prefix="prefix")
assert file_path == os.path.join(
"/target",
"prefixYouTube Rewind 2019 For the Record YouTubeRewind.3gpp"
)
@mock.patch(
"pytube.request.head", MagicMock(return_value={"content-length": "16384"})
)
@mock.patch(
"pytube.request.stream",
MagicMock(return_value=iter([str(random.getrandbits(8 * 1024))])),
)
@mock.patch("pytube.streams.target_directory", MagicMock(return_value="/target"))
def test_download_with_filename(cipher_signature):
with mock.patch("pytube.streams.open", mock.mock_open(), create=True):
stream = cipher_signature.streams[0]
file_path = stream.download(filename="cool name bro")
assert file_path == os.path.join(
"/target",
"cool name bro"
)
@mock.patch(
"pytube.request.head", MagicMock(return_value={"content-length": "16384"})
)
@mock.patch(
"pytube.request.stream",
MagicMock(return_value=iter([str(random.getrandbits(8 * 1024))])),
)
@mock.patch("pytube.streams.target_directory", MagicMock(return_value="/target"))
@mock.patch("os.path.isfile", MagicMock(return_value=True))
def test_download_with_existing(cipher_signature):
with mock.patch("pytube.streams.open", mock.mock_open(), create=True):
stream = cipher_signature.streams[0]
os.path.getsize = Mock(return_value=stream.filesize)
file_path = stream.download()
assert file_path == os.path.join(
"/target",
"YouTube Rewind 2019 For the Record YouTubeRewind.3gpp"
)
assert not request.stream.called
@mock.patch(
"pytube.request.head", MagicMock(return_value={"content-length": "16384"})
)
@mock.patch(
"pytube.request.stream",
MagicMock(return_value=iter([str(random.getrandbits(8 * 1024))])),
)
@mock.patch("pytube.streams.target_directory", MagicMock(return_value="/target"))
@mock.patch("os.path.isfile", MagicMock(return_value=True))
def test_download_with_existing_no_skip(cipher_signature):
with mock.patch("pytube.streams.open", mock.mock_open(), create=True):
stream = cipher_signature.streams[0]
os.path.getsize = Mock(return_value=stream.filesize)
file_path = stream.download(skip_existing=False)
assert file_path == os.path.join(
"/target",
"YouTube Rewind 2019 For the Record YouTubeRewind.3gpp"
)
assert request.stream.called
def test_progressive_streams_return_includes_audio_track(cipher_signature):
stream = cipher_signature.streams.filter(progressive=True)[0]
assert stream.includes_audio_track
def test_progressive_streams_return_includes_video_track(cipher_signature):
stream = cipher_signature.streams.filter(progressive=True)[0]
assert stream.includes_video_track
@mock.patch(
"pytube.request.head", MagicMock(return_value={"content-length": "16384"})
)
@mock.patch(
"pytube.request.stream",
MagicMock(return_value=iter([str(random.getrandbits(8 * 1024))])),
)
def test_on_progress_hook(cipher_signature):
callback_fn = mock.MagicMock()
cipher_signature.register_on_progress_callback(callback_fn)
with mock.patch("pytube.streams.open", mock.mock_open(), create=True):
stream = cipher_signature.streams[0]
stream.download()
assert callback_fn.called
args, _ = callback_fn.call_args
assert len(args) == 3
stream, _, _ = args
assert isinstance(stream, Stream)
@mock.patch(
"pytube.request.head", MagicMock(return_value={"content-length": "16384"})
)
@mock.patch(
"pytube.request.stream",
MagicMock(return_value=iter([str(random.getrandbits(8 * 1024))])),
)
def test_on_complete_hook(cipher_signature):
callback_fn = mock.MagicMock()
cipher_signature.register_on_complete_callback(callback_fn)
with mock.patch("pytube.streams.open", mock.mock_open(), create=True):
stream = cipher_signature.streams[0]
stream.download()
assert callback_fn.called
def test_author(cipher_signature):
assert cipher_signature.author == 'YouTube'
def test_thumbnail_when_in_details(cipher_signature):
expected = f"https://i.ytimg.com/vi/{cipher_signature.video_id}/sddefault.jpg"
cipher_signature._player_response = {
"videoDetails": {"thumbnail": {"thumbnails": [{"url": expected}]}}
}
assert cipher_signature.thumbnail_url == expected
def test_repr_for_audio_streams(cipher_signature):
stream = str(cipher_signature.streams.filter(only_audio=True)[1])
expected = (
'<Stream: itag="140" mime_type="audio/mp4" abr="128kbps" '
'acodec="mp4a.40.2" progressive="False" type="audio">'
)
assert stream == expected
def test_repr_for_video_streams(cipher_signature):
stream = str(cipher_signature.streams.filter(only_video=True)[0])
expected = (
'<Stream: itag="137" mime_type="video/mp4" res="1080p" fps="24fps" '
'vcodec="avc1.640028" progressive="False" type="video">'
)
assert stream == expected
def test_repr_for_progressive_streams(cipher_signature):
stream_reprs = [
str(s)
for s in cipher_signature.streams.filter(progressive=True)
]
expected = (
'<Stream: itag="18" mime_type="video/mp4" res="360p" fps="24fps" '
'vcodec="avc1.42001E" acodec="mp4a.40.2" progressive="True" '
'type="video">'
)
assert expected in stream_reprs
def test_repr_for_adaptive_streams(cipher_signature):
stream = str(cipher_signature.streams.filter(adaptive=True)[0])
expected = (
'<Stream: itag="137" mime_type="video/mp4" res="1080p" fps="24fps" '
'vcodec="avc1.640028" progressive="False" type="video">'
)
assert stream == expected
def test_segmented_stream_on_404(cipher_signature):
stream = cipher_signature.streams.filter(adaptive=True)[0]
with mock.patch('pytube.request.head') as mock_head:
with mock.patch('pytube.request.urlopen') as mock_url_open:
# Mock the responses to YouTube
mock_url_open_object = mock.Mock()
# These are our 4 "segments" of a dash stream
# The first explains how many pieces there are, and
# the rest are those pieces
responses = [
b'Raw_data\r\nSegment-Count: 3',
b'a',
b'b',
b'c',
]
joined_responses = b''.join(responses)
# We create response headers to match the segments
response_headers = [
{
'content-length': len(r),
'Content-Range': '0-%s/%s' % (str(len(r)), str(len(r)))
}
for r in responses
]
# Request order for stream:
# 1. get(url&sn=0)
# 2. head(url&sn=[1,2,3])
# 3. info(url) -> 404
# 4. get(url&sn=0)
# 5. get(url&sn=[1,2,3])
# Handle filesize requests
mock_head.side_effect = [
HTTPError('', 404, 'Not Found', '', ''),
*response_headers[1:],
]
# Each response must be followed by None, to break iteration
# in the stream() function
mock_url_open_object.read.side_effect = [
responses[0], None,
responses[1], None,
responses[2], None,
responses[3], None,
]
# This handles the HEAD requests to get content-length
mock_url_open_object.info.side_effect = [
HTTPError('', 404, 'Not Found', '', ''),
*response_headers
]
mock_url_open.return_value = mock_url_open_object
with mock.patch('builtins.open', new_callable=mock.mock_open) as mock_open:
file_handle = mock_open.return_value.__enter__.return_value
fp = stream.download()
full_content = b''
for call in file_handle.write.call_args_list:
args, kwargs = call
full_content += b''.join(args)
assert full_content == joined_responses
mock_open.assert_called_once_with(fp, 'wb')
def test_segmented_only_catches_404(cipher_signature):
stream = cipher_signature.streams.filter(adaptive=True)[0]
with mock.patch('pytube.request.stream') as mock_stream:
mock_stream.side_effect = HTTPError('', 403, 'Forbidden', '', '')
with mock.patch("pytube.streams.open", mock.mock_open(), create=True):
with pytest.raises(HTTPError):
stream.download()
|