black formatting for tests
Browse files- .idea/dictionaries/haroldmartin.xml +4 -0
- pytube/captions.py +1 -1
- tests/conftest.py +10 -10
- tests/contrib/test_playlist.py +3 -3
- tests/generate_fixture.py +2 -0
- tests/test_cipher.py +1 -1
- tests/test_cli.py +3 -3
- tests/test_exceptions.py +2 -2
- tests/test_extract.py +10 -10
- tests/test_helpers.py +4 -4
- tests/test_itags.py +2 -2
- tests/test_main.py +2 -2
- tests/test_request.py +10 -10
- tests/test_streams.py +20 -21
.idea/dictionaries/haroldmartin.xml
CHANGED
@@ -1,8 +1,12 @@
|
|
1 |
<component name="ProjectDictionaryState">
|
2 |
<dictionary name="haroldmartin">
|
3 |
<words>
|
|
|
4 |
<w>descrambler</w>
|
|
|
5 |
<w>itag</w>
|
|
|
|
|
6 |
</words>
|
7 |
</dictionary>
|
8 |
</component>
|
|
|
1 |
<component name="ProjectDictionaryState">
|
2 |
<dictionary name="haroldmartin">
|
3 |
<words>
|
4 |
+
<w>descramble</w>
|
5 |
<w>descrambler</w>
|
6 |
+
<w>ficano</w>
|
7 |
<w>itag</w>
|
8 |
+
<w>noqa</w>
|
9 |
+
<w>pytube</w>
|
10 |
</words>
|
11 |
</dictionary>
|
12 |
</component>
|
pytube/captions.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
# -*- coding: utf-8 -*-
|
2 |
-
"""This module
|
3 |
import math
|
4 |
import time
|
5 |
import xml.etree.ElementTree as ElementTree
|
|
|
1 |
# -*- coding: utf-8 -*-
|
2 |
+
"""This module contains a container for caption tracks."""
|
3 |
import math
|
4 |
import time
|
5 |
import xml.etree.ElementTree as ElementTree
|
tests/conftest.py
CHANGED
@@ -14,19 +14,19 @@ def load_playback_file(filename):
|
|
14 |
"""Load a gzip json playback file."""
|
15 |
cur_fp = os.path.realpath(__file__)
|
16 |
cur_dir = os.path.dirname(cur_fp)
|
17 |
-
fp = os.path.join(cur_dir,
|
18 |
-
with gzip.open(fp,
|
19 |
-
content = fh.read().decode(
|
20 |
return json.loads(content)
|
21 |
|
22 |
|
23 |
def load_and_init_from_playback_file(filename):
|
24 |
"""Load a gzip json playback file and create YouTube instance."""
|
25 |
pb = load_playback_file(filename)
|
26 |
-
yt = YouTube(pb[
|
27 |
-
yt.watch_html = pb[
|
28 |
-
yt.js = pb[
|
29 |
-
yt.vid_info = pb[
|
30 |
yt.descramble()
|
31 |
return yt
|
32 |
|
@@ -34,19 +34,19 @@ def load_and_init_from_playback_file(filename):
|
|
34 |
@pytest.fixture
|
35 |
def cipher_signature():
|
36 |
"""Youtube instance initialized with video id 9bZkp7q19f0."""
|
37 |
-
filename =
|
38 |
return load_and_init_from_playback_file(filename)
|
39 |
|
40 |
|
41 |
@pytest.fixture
|
42 |
def presigned_video():
|
43 |
"""Youtube instance initialized with video id QRS8MkLhQmM."""
|
44 |
-
filename =
|
45 |
return load_and_init_from_playback_file(filename)
|
46 |
|
47 |
|
48 |
@pytest.fixture
|
49 |
def age_restricted():
|
50 |
"""Youtube instance initialized with video id zRbsm3e2ltw."""
|
51 |
-
filename =
|
52 |
return load_playback_file(filename)
|
|
|
14 |
"""Load a gzip json playback file."""
|
15 |
cur_fp = os.path.realpath(__file__)
|
16 |
cur_dir = os.path.dirname(cur_fp)
|
17 |
+
fp = os.path.join(cur_dir, "mocks", filename)
|
18 |
+
with gzip.open(fp, "rb") as fh:
|
19 |
+
content = fh.read().decode("utf-8")
|
20 |
return json.loads(content)
|
21 |
|
22 |
|
23 |
def load_and_init_from_playback_file(filename):
|
24 |
"""Load a gzip json playback file and create YouTube instance."""
|
25 |
pb = load_playback_file(filename)
|
26 |
+
yt = YouTube(pb["url"], defer_prefetch_init=True)
|
27 |
+
yt.watch_html = pb["watch_html"]
|
28 |
+
yt.js = pb["js"]
|
29 |
+
yt.vid_info = pb["video_info"]
|
30 |
yt.descramble()
|
31 |
return yt
|
32 |
|
|
|
34 |
@pytest.fixture
|
35 |
def cipher_signature():
|
36 |
"""Youtube instance initialized with video id 9bZkp7q19f0."""
|
37 |
+
filename = "yt-video-9bZkp7q19f0.json.gz"
|
38 |
return load_and_init_from_playback_file(filename)
|
39 |
|
40 |
|
41 |
@pytest.fixture
|
42 |
def presigned_video():
|
43 |
"""Youtube instance initialized with video id QRS8MkLhQmM."""
|
44 |
+
filename = "yt-video-QRS8MkLhQmM.json.gz"
|
45 |
return load_and_init_from_playback_file(filename)
|
46 |
|
47 |
|
48 |
@pytest.fixture
|
49 |
def age_restricted():
|
50 |
"""Youtube instance initialized with video id zRbsm3e2ltw."""
|
51 |
+
filename = "yt-video-zRbsm3e2ltw-1507777044.json.gz"
|
52 |
return load_playback_file(filename)
|
tests/contrib/test_playlist.py
CHANGED
@@ -3,8 +3,8 @@ from pytube import Playlist
|
|
3 |
|
4 |
|
5 |
def test_title():
|
6 |
-
list_key =
|
7 |
-
url =
|
8 |
pl = Playlist(url)
|
9 |
pl_title = pl.title()
|
10 |
-
assert pl_title ==
|
|
|
3 |
|
4 |
|
5 |
def test_title():
|
6 |
+
list_key = "PLsyeobzWxl7poL9JTVyndKe62ieoN-MZ3"
|
7 |
+
url = "https://www.youtube.com/playlist?list=" + list_key
|
8 |
pl = Playlist(url)
|
9 |
pl_title = pl.title()
|
10 |
+
assert pl_title == "Python Tutorial for Beginners"
|
tests/generate_fixture.py
CHANGED
@@ -1,5 +1,7 @@
|
|
1 |
#!/usr/bin/env python3
|
2 |
|
|
|
|
|
3 |
from os import path
|
4 |
import sys
|
5 |
import json
|
|
|
1 |
#!/usr/bin/env python3
|
2 |
|
3 |
+
# flake8: noqa: E402
|
4 |
+
|
5 |
from os import path
|
6 |
import sys
|
7 |
import json
|
tests/test_cipher.py
CHANGED
@@ -7,4 +7,4 @@ from pytube.exceptions import RegexMatchError
|
|
7 |
|
8 |
def test_map_functions():
|
9 |
with pytest.raises(RegexMatchError):
|
10 |
-
cipher.map_functions(
|
|
|
7 |
|
8 |
def test_map_functions():
|
9 |
with pytest.raises(RegexMatchError):
|
10 |
+
cipher.map_functions("asdf")
|
tests/test_cli.py
CHANGED
@@ -4,10 +4,10 @@ from unittest import mock
|
|
4 |
from pytube import cli
|
5 |
|
6 |
|
7 |
-
@mock.patch(
|
8 |
-
@mock.patch(
|
9 |
def test_download(MockYouTube, mock_sys):
|
10 |
instance = MockYouTube.return_value
|
11 |
instance.prefetch_descramble.return_value = None
|
12 |
instance.streams = mock.Mock()
|
13 |
-
cli.download(
|
|
|
4 |
from pytube import cli
|
5 |
|
6 |
|
7 |
+
@mock.patch("pytube.cli.YouTube")
|
8 |
+
@mock.patch("pytube.cli.sys")
|
9 |
def test_download(MockYouTube, mock_sys):
|
10 |
instance = MockYouTube.return_value
|
11 |
instance.prefetch_descramble.return_value = None
|
12 |
instance.streams = mock.Mock()
|
13 |
+
cli.download("asdf", "asdf")
|
tests/test_exceptions.py
CHANGED
@@ -4,6 +4,6 @@ from pytube.exceptions import ExtractError
|
|
4 |
|
5 |
def test_is_expected():
|
6 |
try:
|
7 |
-
raise ExtractError(
|
8 |
except ExtractError as e:
|
9 |
-
assert e.video_id ==
|
|
|
4 |
|
5 |
def test_is_expected():
|
6 |
try:
|
7 |
+
raise ExtractError("ppfff", video_id="YLnZklYFe7E")
|
8 |
except ExtractError as e:
|
9 |
+
assert e.video_id == "YLnZklYFe7E"
|
tests/test_extract.py
CHANGED
@@ -4,15 +4,15 @@ from pytube import extract
|
|
4 |
|
5 |
|
6 |
def test_extract_video_id():
|
7 |
-
url =
|
8 |
video_id = extract.video_id(url)
|
9 |
-
assert video_id ==
|
10 |
|
11 |
|
12 |
def test_extract_watch_url():
|
13 |
-
video_id =
|
14 |
watch_url = extract.watch_url(video_id)
|
15 |
-
assert watch_url ==
|
16 |
|
17 |
|
18 |
def test_info_url(cipher_signature):
|
@@ -20,25 +20,25 @@ def test_info_url(cipher_signature):
|
|
20 |
video_id=cipher_signature.video_id,
|
21 |
watch_url=cipher_signature.watch_url,
|
22 |
watch_html=cipher_signature.watch_html,
|
23 |
-
embed_html=
|
24 |
age_restricted=False,
|
25 |
)
|
26 |
expected = (
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
)
|
31 |
assert video_info_url == expected
|
32 |
|
33 |
|
34 |
def test_js_url(cipher_signature):
|
35 |
-
expected =
|
36 |
result = extract.js_url(cipher_signature.watch_html)
|
37 |
assert expected == result
|
38 |
|
39 |
|
40 |
def test_age_restricted(age_restricted):
|
41 |
-
assert extract.is_age_restricted(age_restricted[
|
42 |
|
43 |
|
44 |
def test_non_age_restricted(cipher_signature):
|
|
|
4 |
|
5 |
|
6 |
def test_extract_video_id():
|
7 |
+
url = "https://www.youtube.com/watch?v=9bZkp7q19f0"
|
8 |
video_id = extract.video_id(url)
|
9 |
+
assert video_id == "9bZkp7q19f0"
|
10 |
|
11 |
|
12 |
def test_extract_watch_url():
|
13 |
+
video_id = "9bZkp7q19f0"
|
14 |
watch_url = extract.watch_url(video_id)
|
15 |
+
assert watch_url == "https://youtube.com/watch?v=9bZkp7q19f0"
|
16 |
|
17 |
|
18 |
def test_info_url(cipher_signature):
|
|
|
20 |
video_id=cipher_signature.video_id,
|
21 |
watch_url=cipher_signature.watch_url,
|
22 |
watch_html=cipher_signature.watch_html,
|
23 |
+
embed_html="",
|
24 |
age_restricted=False,
|
25 |
)
|
26 |
expected = (
|
27 |
+
"https://youtube.com/get_video_info?video_id=9bZkp7q19f0&el=%24el"
|
28 |
+
"&ps=default&eurl=https%253A%2F%2Fyoutube.com%2Fwatch%253Fv%"
|
29 |
+
"253D9bZkp7q19f0&hl=en_US"
|
30 |
)
|
31 |
assert video_info_url == expected
|
32 |
|
33 |
|
34 |
def test_js_url(cipher_signature):
|
35 |
+
expected = "https://youtube.com/yts/jsbin/player-vflOdyxa4/en_US/base.js"
|
36 |
result = extract.js_url(cipher_signature.watch_html)
|
37 |
assert expected == result
|
38 |
|
39 |
|
40 |
def test_age_restricted(age_restricted):
|
41 |
+
assert extract.is_age_restricted(age_restricted["watch_html"])
|
42 |
|
43 |
|
44 |
def test_non_age_restricted(cipher_signature):
|
tests/test_helpers.py
CHANGED
@@ -7,15 +7,15 @@ from pytube.exceptions import RegexMatchError
|
|
7 |
|
8 |
def test_regex_search_no_match():
|
9 |
with pytest.raises(RegexMatchError):
|
10 |
-
helpers.regex_search(
|
11 |
|
12 |
|
13 |
def test_regex_search():
|
14 |
# TODO(nficano): should check isinstance
|
15 |
-
assert helpers.regex_search(
|
16 |
|
17 |
|
18 |
def test_safe_filename():
|
19 |
"""Unsafe characters get stripped from generated filename"""
|
20 |
-
assert helpers.safe_filename(
|
21 |
-
assert helpers.safe_filename(
|
|
|
7 |
|
8 |
def test_regex_search_no_match():
|
9 |
with pytest.raises(RegexMatchError):
|
10 |
+
helpers.regex_search("^a$", "", groups=True)
|
11 |
|
12 |
|
13 |
def test_regex_search():
|
14 |
# TODO(nficano): should check isinstance
|
15 |
+
assert helpers.regex_search("^a$", "a") is not None
|
16 |
|
17 |
|
18 |
def test_safe_filename():
|
19 |
"""Unsafe characters get stripped from generated filename"""
|
20 |
+
assert helpers.safe_filename("abc1245$$") == "abc1245"
|
21 |
+
assert helpers.safe_filename("abc##") == "abc"
|
tests/test_itags.py
CHANGED
@@ -4,9 +4,9 @@ from pytube import itags
|
|
4 |
|
5 |
def test_get_format_profile():
|
6 |
profile = itags.get_format_profile(22)
|
7 |
-
assert profile[
|
8 |
|
9 |
|
10 |
def test_get_format_profile_non_existant():
|
11 |
profile = itags.get_format_profile(2239)
|
12 |
-
assert profile[
|
|
|
4 |
|
5 |
def test_get_format_profile():
|
6 |
profile = itags.get_format_profile(22)
|
7 |
+
assert profile["resolution"] == "720p"
|
8 |
|
9 |
|
10 |
def test_get_format_profile_non_existant():
|
11 |
profile = itags.get_format_profile(2239)
|
12 |
+
assert profile["resolution"] is None
|
tests/test_main.py
CHANGED
@@ -4,9 +4,9 @@ from unittest import mock
|
|
4 |
from pytube import YouTube
|
5 |
|
6 |
|
7 |
-
@mock.patch(
|
8 |
def test_prefetch_deferred(MockYouTube):
|
9 |
instance = MockYouTube.return_value
|
10 |
instance.prefetch_descramble.return_value = None
|
11 |
-
YouTube(
|
12 |
assert not instance.prefetch_descramble.called
|
|
|
4 |
from pytube import YouTube
|
5 |
|
6 |
|
7 |
+
@mock.patch("pytube.__main__.YouTube")
|
8 |
def test_prefetch_deferred(MockYouTube):
|
9 |
instance = MockYouTube.return_value
|
10 |
instance.prefetch_descramble.return_value = None
|
11 |
+
YouTube("https://www.youtube.com/watch?v=9bZkp7q19f0", True)
|
12 |
assert not instance.prefetch_descramble.called
|
tests/test_request.py
CHANGED
@@ -6,7 +6,7 @@ from unittest import mock
|
|
6 |
from pytube import request
|
7 |
|
8 |
|
9 |
-
@mock.patch(
|
10 |
def test_get_streaming(mock_urlopen):
|
11 |
fake_stream_binary = [
|
12 |
iter(os.urandom(8 * 1024)),
|
@@ -17,26 +17,26 @@ def test_get_streaming(mock_urlopen):
|
|
17 |
response = mock.Mock()
|
18 |
response.read.side_effect = fake_stream_binary
|
19 |
mock_urlopen.return_value = response
|
20 |
-
response = request.get(
|
21 |
call_count = 0
|
22 |
for i in response:
|
23 |
call_count += 1
|
24 |
assert call_count == 3
|
25 |
|
26 |
|
27 |
-
@mock.patch(
|
28 |
def test_get_headers(mock_urlopen):
|
29 |
response = mock.Mock()
|
30 |
-
response.info.return_value = {
|
31 |
mock_urlopen.return_value = response
|
32 |
-
response = request.get(
|
33 |
-
assert response == {
|
34 |
|
35 |
|
36 |
-
@mock.patch(
|
37 |
def test_get(mock_urlopen):
|
38 |
response = mock.Mock()
|
39 |
-
response.read.return_value =
|
40 |
mock_urlopen.return_value = response
|
41 |
-
response = request.get(
|
42 |
-
assert response ==
|
|
|
6 |
from pytube import request
|
7 |
|
8 |
|
9 |
+
@mock.patch("pytube.request.urlopen")
|
10 |
def test_get_streaming(mock_urlopen):
|
11 |
fake_stream_binary = [
|
12 |
iter(os.urandom(8 * 1024)),
|
|
|
17 |
response = mock.Mock()
|
18 |
response.read.side_effect = fake_stream_binary
|
19 |
mock_urlopen.return_value = response
|
20 |
+
response = request.get("http://fakeassurl.gov", streaming=True)
|
21 |
call_count = 0
|
22 |
for i in response:
|
23 |
call_count += 1
|
24 |
assert call_count == 3
|
25 |
|
26 |
|
27 |
+
@mock.patch("pytube.request.urlopen")
|
28 |
def test_get_headers(mock_urlopen):
|
29 |
response = mock.Mock()
|
30 |
+
response.info.return_value = {"content-length": "16384"}
|
31 |
mock_urlopen.return_value = response
|
32 |
+
response = request.get("http://fakeassurl.gov", headers=True)
|
33 |
+
assert response == {"content-length": "16384"}
|
34 |
|
35 |
|
36 |
+
@mock.patch("pytube.request.urlopen")
|
37 |
def test_get(mock_urlopen):
|
38 |
response = mock.Mock()
|
39 |
+
response.read.return_value = "<html></html>".encode("utf-8")
|
40 |
mock_urlopen.return_value = response
|
41 |
+
response = request.get("http://fakeassurl.gov")
|
42 |
+
assert response == "<html></html>"
|
tests/test_streams.py
CHANGED
@@ -8,41 +8,41 @@ from pytube import Stream
|
|
8 |
|
9 |
|
10 |
def test_filesize(cipher_signature, mocker):
|
11 |
-
mocker.patch.object(request,
|
12 |
-
request.get.return_value = {
|
13 |
assert cipher_signature.streams.first().filesize == 6796391
|
14 |
|
15 |
|
16 |
def test_default_filename(cipher_signature):
|
17 |
-
expected =
|
18 |
stream = cipher_signature.streams.first()
|
19 |
assert stream.default_filename == expected
|
20 |
|
21 |
|
22 |
def test_title(cipher_signature):
|
23 |
-
expected =
|
24 |
stream = cipher_signature.streams.first()
|
25 |
assert stream.title == expected
|
26 |
|
27 |
-
expected =
|
28 |
stream.player_config_args = {
|
29 |
-
|
30 |
}
|
31 |
assert stream.title == expected
|
32 |
|
33 |
-
expected =
|
34 |
stream.player_config_args = {}
|
35 |
assert stream.title == expected
|
36 |
|
37 |
|
38 |
def test_download(cipher_signature, mocker):
|
39 |
-
mocker.patch.object(request,
|
40 |
request.get.side_effect = [
|
41 |
-
{
|
42 |
-
{
|
43 |
iter([str(random.getrandbits(8 * 1024))]),
|
44 |
]
|
45 |
-
with mock.patch(
|
46 |
stream = cipher_signature.streams.first()
|
47 |
stream.download()
|
48 |
|
@@ -61,13 +61,13 @@ def test_on_progress_hook(cipher_signature, mocker):
|
|
61 |
callback_fn = mock.MagicMock()
|
62 |
cipher_signature.register_on_progress_callback(callback_fn)
|
63 |
|
64 |
-
mocker.patch.object(request,
|
65 |
request.get.side_effect = [
|
66 |
-
{
|
67 |
-
{
|
68 |
iter([str(random.getrandbits(8 * 1024))]),
|
69 |
]
|
70 |
-
with mock.patch(
|
71 |
stream = cipher_signature.streams.first()
|
72 |
stream.download()
|
73 |
assert callback_fn.called
|
@@ -81,13 +81,13 @@ def test_on_complete_hook(cipher_signature, mocker):
|
|
81 |
callback_fn = mock.MagicMock()
|
82 |
cipher_signature.register_on_complete_callback(callback_fn)
|
83 |
|
84 |
-
mocker.patch.object(request,
|
85 |
request.get.side_effect = [
|
86 |
-
{
|
87 |
-
{
|
88 |
iter([str(random.getrandbits(8 * 1024))]),
|
89 |
]
|
90 |
-
with mock.patch(
|
91 |
stream = cipher_signature.streams.first()
|
92 |
stream.download()
|
93 |
assert callback_fn.called
|
@@ -96,8 +96,7 @@ def test_on_complete_hook(cipher_signature, mocker):
|
|
96 |
def test_repr_for_audio_streams(cipher_signature):
|
97 |
stream = str(cipher_signature.streams.filter(only_audio=True).first())
|
98 |
expected = (
|
99 |
-
'<Stream: itag="140" mime_type="audio/mp4" abr="128kbps" '
|
100 |
-
'acodec="mp4a.40.2">'
|
101 |
)
|
102 |
assert stream == expected
|
103 |
|
|
|
8 |
|
9 |
|
10 |
def test_filesize(cipher_signature, mocker):
|
11 |
+
mocker.patch.object(request, "get")
|
12 |
+
request.get.return_value = {"content-length": "6796391"}
|
13 |
assert cipher_signature.streams.first().filesize == 6796391
|
14 |
|
15 |
|
16 |
def test_default_filename(cipher_signature):
|
17 |
+
expected = "PSY - GANGNAM STYLE(๊ฐ๋จ์คํ์ผ) MV.mp4"
|
18 |
stream = cipher_signature.streams.first()
|
19 |
assert stream.default_filename == expected
|
20 |
|
21 |
|
22 |
def test_title(cipher_signature):
|
23 |
+
expected = "PSY - GANGNAM STYLE(๊ฐ๋จ์คํ์ผ) M/V"
|
24 |
stream = cipher_signature.streams.first()
|
25 |
assert stream.title == expected
|
26 |
|
27 |
+
expected = "PSY - GANGNAM STYLE(๊ฐ๋จ์คํ์ผ)"
|
28 |
stream.player_config_args = {
|
29 |
+
"player_response": {"videoDetails": {"title": expected}},
|
30 |
}
|
31 |
assert stream.title == expected
|
32 |
|
33 |
+
expected = "Unknown YouTube Video Title"
|
34 |
stream.player_config_args = {}
|
35 |
assert stream.title == expected
|
36 |
|
37 |
|
38 |
def test_download(cipher_signature, mocker):
|
39 |
+
mocker.patch.object(request, "get")
|
40 |
request.get.side_effect = [
|
41 |
+
{"content-length": "16384"},
|
42 |
+
{"content-length": "16384"},
|
43 |
iter([str(random.getrandbits(8 * 1024))]),
|
44 |
]
|
45 |
+
with mock.patch("pytube.streams.open", mock.mock_open(), create=True):
|
46 |
stream = cipher_signature.streams.first()
|
47 |
stream.download()
|
48 |
|
|
|
61 |
callback_fn = mock.MagicMock()
|
62 |
cipher_signature.register_on_progress_callback(callback_fn)
|
63 |
|
64 |
+
mocker.patch.object(request, "get")
|
65 |
request.get.side_effect = [
|
66 |
+
{"content-length": "16384"},
|
67 |
+
{"content-length": "16384"},
|
68 |
iter([str(random.getrandbits(8 * 1024))]),
|
69 |
]
|
70 |
+
with mock.patch("pytube.streams.open", mock.mock_open(), create=True):
|
71 |
stream = cipher_signature.streams.first()
|
72 |
stream.download()
|
73 |
assert callback_fn.called
|
|
|
81 |
callback_fn = mock.MagicMock()
|
82 |
cipher_signature.register_on_complete_callback(callback_fn)
|
83 |
|
84 |
+
mocker.patch.object(request, "get")
|
85 |
request.get.side_effect = [
|
86 |
+
{"content-length": "16384"},
|
87 |
+
{"content-length": "16384"},
|
88 |
iter([str(random.getrandbits(8 * 1024))]),
|
89 |
]
|
90 |
+
with mock.patch("pytube.streams.open", mock.mock_open(), create=True):
|
91 |
stream = cipher_signature.streams.first()
|
92 |
stream.download()
|
93 |
assert callback_fn.called
|
|
|
96 |
def test_repr_for_audio_streams(cipher_signature):
|
97 |
stream = str(cipher_signature.streams.filter(only_audio=True).first())
|
98 |
expected = (
|
99 |
+
'<Stream: itag="140" mime_type="audio/mp4" abr="128kbps" ' 'acodec="mp4a.40.2">'
|
|
|
100 |
)
|
101 |
assert stream == expected
|
102 |
|