hbmartin commited on
Commit
eab63b5
·
1 Parent(s): 9b83536

CaptionQuery and StreamQuery implement Sequence

Browse files
pytube/cli.py CHANGED
@@ -413,12 +413,12 @@ def display_streams(youtube: YouTube) -> None:
413
  A valid YouTube watch URL.
414
 
415
  """
416
- for stream in youtube.streams.all():
417
  print(stream)
418
 
419
 
420
  def _print_available_captions(captions: CaptionQuery) -> None:
421
- print(f"Available caption codes are: {', '.join(c.code for c in captions.all())}")
422
 
423
 
424
  def download_caption(
 
413
  A valid YouTube watch URL.
414
 
415
  """
416
+ for stream in youtube.streams:
417
  print(stream)
418
 
419
 
420
  def _print_available_captions(captions: CaptionQuery) -> None:
421
+ print(f"Available caption codes are: {', '.join(c.code for c in captions)}")
422
 
423
 
424
  def download_caption(
pytube/contrib/playlist.py CHANGED
@@ -143,7 +143,7 @@ class Playlist(Sequence):
143
  """
144
  yield from (YouTube(url) for url in self.video_urls)
145
 
146
- def __getitem__(self, i: Union[slice, int]):
147
  return self.video_urls[i]
148
 
149
  def __len__(self) -> int:
 
143
  """
144
  yield from (YouTube(url) for url in self.video_urls)
145
 
146
+ def __getitem__(self, i: Union[slice, int]) -> Union[str, List[str]]:
147
  return self.video_urls[i]
148
 
149
  def __len__(self) -> int:
pytube/query.py CHANGED
@@ -1,12 +1,14 @@
1
  # -*- coding: utf-8 -*-
2
 
3
  """This module provides a query interface for media streams and captions."""
4
- from typing import List, Optional
 
5
 
6
  from pytube import Stream, Caption
 
7
 
8
 
9
- class StreamQuery:
10
  """Interface for querying the available media streams."""
11
 
12
  def __init__(self, fmt_streams):
@@ -313,14 +315,16 @@ class StreamQuery:
313
  except IndexError:
314
  pass
315
 
 
316
  def count(self) -> int:
317
  """Get the count the query would return.
318
 
319
  :rtype: int
320
 
321
  """
322
- return len(self.fmt_streams)
323
 
 
324
  def all(self) -> List[Stream]:
325
  """Get all the results represented by this query as a list.
326
 
@@ -329,8 +333,14 @@ class StreamQuery:
329
  """
330
  return self.fmt_streams
331
 
 
 
 
 
 
 
332
 
333
- class CaptionQuery:
334
  """Interface for querying the available captions."""
335
 
336
  def __init__(self, captions: List[Caption]):
@@ -355,6 +365,7 @@ class CaptionQuery:
355
  """
356
  return self.lang_code_index.get(lang_code)
357
 
 
358
  def all(self) -> List[Caption]:
359
  """Get all the results represented by this query as a list.
360
 
@@ -362,3 +373,9 @@ class CaptionQuery:
362
 
363
  """
364
  return self.captions
 
 
 
 
 
 
 
1
  # -*- coding: utf-8 -*-
2
 
3
  """This module provides a query interface for media streams and captions."""
4
+ from typing import List, Optional, Union
5
+ from collections.abc import Sequence
6
 
7
  from pytube import Stream, Caption
8
+ from pytube.helpers import deprecated
9
 
10
 
11
+ class StreamQuery(Sequence):
12
  """Interface for querying the available media streams."""
13
 
14
  def __init__(self, fmt_streams):
 
315
  except IndexError:
316
  pass
317
 
318
+ @deprecated("Get the size of this list directly using len()")
319
  def count(self) -> int:
320
  """Get the count the query would return.
321
 
322
  :rtype: int
323
 
324
  """
325
+ return len(self)
326
 
327
+ @deprecated("This object can be treated as a list, all() is useless")
328
  def all(self) -> List[Stream]:
329
  """Get all the results represented by this query as a list.
330
 
 
333
  """
334
  return self.fmt_streams
335
 
336
+ def __getitem__(self, i: Union[slice, int]):
337
+ return self.fmt_streams[i]
338
+
339
+ def __len__(self) -> int:
340
+ return len(self.fmt_streams)
341
+
342
 
343
+ class CaptionQuery(Sequence):
344
  """Interface for querying the available captions."""
345
 
346
  def __init__(self, captions: List[Caption]):
 
365
  """
366
  return self.lang_code_index.get(lang_code)
367
 
368
+ @deprecated("This object can be treated as a list, all() is useless")
369
  def all(self) -> List[Caption]:
370
  """Get all the results represented by this query as a list.
371
 
 
373
 
374
  """
375
  return self.captions
376
+
377
+ def __getitem__(self, i: Union[slice, int]):
378
+ return self.captions[i]
379
+
380
+ def __len__(self) -> int:
381
+ return len(self.captions)
tests/contrib/test_playlist.py CHANGED
@@ -90,6 +90,7 @@ def test_sequence(request_get, playlist_html):
90
  assert playlist[0] == "https://www.youtube.com/watch?v=ujTCoH21GlA"
91
  assert len(playlist) == 12
92
 
 
93
  @mock.patch("pytube.contrib.playlist.request.get")
94
  @mock.patch("pytube.cli.YouTube.__init__", return_value=None)
95
  def test_videos(youtube, request_get, playlist_html):
 
90
  assert playlist[0] == "https://www.youtube.com/watch?v=ujTCoH21GlA"
91
  assert len(playlist) == 12
92
 
93
+
94
  @mock.patch("pytube.contrib.playlist.request.get")
95
  @mock.patch("pytube.cli.YouTube.__init__", return_value=None)
96
  def test_videos(youtube, request_get, playlist_html):
tests/test_captions.py CHANGED
@@ -12,7 +12,7 @@ def test_float_to_srt_time_format():
12
  assert caption1.float_to_srt_time_format(3.89) == "00:00:03,890"
13
 
14
 
15
- def test_caption_query_all():
16
  caption1 = Caption(
17
  {"url": "url1", "name": {"simpleText": "name1"}, "languageCode": "en"}
18
  )
@@ -21,6 +21,8 @@ def test_caption_query_all():
21
  )
22
  caption_query = CaptionQuery(captions=[caption1, caption2])
23
  assert caption_query.captions == [caption1, caption2]
 
 
24
 
25
 
26
  def test_caption_query_get_by_language_code_when_exists():
 
12
  assert caption1.float_to_srt_time_format(3.89) == "00:00:03,890"
13
 
14
 
15
+ def test_caption_query_sequence():
16
  caption1 = Caption(
17
  {"url": "url1", "name": {"simpleText": "name1"}, "languageCode": "en"}
18
  )
 
21
  )
22
  caption_query = CaptionQuery(captions=[caption1, caption2])
23
  assert caption_query.captions == [caption1, caption2]
24
+ assert len(caption_query) == 2
25
+ assert caption_query[0] == caption1
26
 
27
 
28
  def test_caption_query_get_by_language_code_when_exists():
tests/test_query.py CHANGED
@@ -3,11 +3,6 @@
3
  import pytest
4
 
5
 
6
- def test_count(cipher_signature):
7
- """Ensure :meth:`~pytube.StreamQuery.count` returns an accurate amount."""
8
- assert cipher_signature.streams.count() == 22
9
-
10
-
11
  @pytest.mark.parametrize(
12
  ("test_input", "expected"),
13
  [
@@ -30,7 +25,7 @@ def test_count(cipher_signature):
30
  )
31
  def test_filters(test_input, expected, cipher_signature):
32
  """Ensure filters produce the expected results."""
33
- result = [s.itag for s in cipher_signature.streams.filter(**test_input).all()]
34
  assert result == expected
35
 
36
 
@@ -64,8 +59,7 @@ def test_order_by(cipher_signature):
64
  :class:`Stream <Stream>` instances in the expected order.
65
  """
66
  itags = [
67
- s.itag
68
- for s in cipher_signature.streams.filter(type="audio").order_by("itag").all()
69
  ]
70
  assert itags == [140, 249, 250, 251]
71
 
@@ -77,10 +71,7 @@ def test_order_by_descending(cipher_signature):
77
  # numerical values
78
  itags = [
79
  s.itag
80
- for s in cipher_signature.streams.filter(type="audio")
81
- .order_by("itag")
82
- .desc()
83
- .all()
84
  ]
85
  assert itags == [251, 250, 249, 140]
86
 
@@ -91,7 +82,6 @@ def test_order_by_non_numerical(cipher_signature):
91
  for s in cipher_signature.streams.filter(res="360p")
92
  .order_by("mime_type")
93
  .desc()
94
- .all()
95
  ]
96
  assert mime_types == ["video/webm", "video/mp4", "video/mp4"]
97
 
@@ -103,10 +93,7 @@ def test_order_by_ascending(cipher_signature):
103
  # numerical values
104
  itags = [
105
  s.itag
106
- for s in cipher_signature.streams.filter(type="audio")
107
- .order_by("itag")
108
- .asc()
109
- .all()
110
  ]
111
  assert itags == [140, 249, 250, 251]
112
 
@@ -114,16 +101,13 @@ def test_order_by_ascending(cipher_signature):
114
  def test_order_by_non_numerical_ascending(cipher_signature):
115
  mime_types = [
116
  s.mime_type
117
- for s in cipher_signature.streams.filter(res="360p")
118
- .order_by("mime_type")
119
- .asc()
120
- .all()
121
  ]
122
  assert mime_types == ["video/mp4", "video/mp4", "video/webm"]
123
 
124
 
125
  def test_order_by_with_none_values(cipher_signature):
126
- abrs = [s.abr for s in cipher_signature.streams.order_by("abr").asc().all()]
127
  assert abrs == ["50kbps", "70kbps", "96kbps", "128kbps", "160kbps"]
128
 
129
 
@@ -151,7 +135,7 @@ def test_get_highest_resolution(cipher_signature):
151
 
152
 
153
  def test_filter_is_dash(cipher_signature):
154
- streams = cipher_signature.streams.filter(is_dash=False).all()
155
  itags = [s.itag for s in streams]
156
  assert itags == [18, 398, 397, 396, 395, 394]
157
 
@@ -162,3 +146,8 @@ def test_get_audio_only(cipher_signature):
162
 
163
  def test_get_audio_only_with_subtype(cipher_signature):
164
  assert cipher_signature.streams.get_audio_only(subtype="webm").itag == 251
 
 
 
 
 
 
3
  import pytest
4
 
5
 
 
 
 
 
 
6
  @pytest.mark.parametrize(
7
  ("test_input", "expected"),
8
  [
 
25
  )
26
  def test_filters(test_input, expected, cipher_signature):
27
  """Ensure filters produce the expected results."""
28
+ result = [s.itag for s in cipher_signature.streams.filter(**test_input)]
29
  assert result == expected
30
 
31
 
 
59
  :class:`Stream <Stream>` instances in the expected order.
60
  """
61
  itags = [
62
+ s.itag for s in cipher_signature.streams.filter(type="audio").order_by("itag")
 
63
  ]
64
  assert itags == [140, 249, 250, 251]
65
 
 
71
  # numerical values
72
  itags = [
73
  s.itag
74
+ for s in cipher_signature.streams.filter(type="audio").order_by("itag").desc()
 
 
 
75
  ]
76
  assert itags == [251, 250, 249, 140]
77
 
 
82
  for s in cipher_signature.streams.filter(res="360p")
83
  .order_by("mime_type")
84
  .desc()
 
85
  ]
86
  assert mime_types == ["video/webm", "video/mp4", "video/mp4"]
87
 
 
93
  # numerical values
94
  itags = [
95
  s.itag
96
+ for s in cipher_signature.streams.filter(type="audio").order_by("itag").asc()
 
 
 
97
  ]
98
  assert itags == [140, 249, 250, 251]
99
 
 
101
  def test_order_by_non_numerical_ascending(cipher_signature):
102
  mime_types = [
103
  s.mime_type
104
+ for s in cipher_signature.streams.filter(res="360p").order_by("mime_type").asc()
 
 
 
105
  ]
106
  assert mime_types == ["video/mp4", "video/mp4", "video/webm"]
107
 
108
 
109
  def test_order_by_with_none_values(cipher_signature):
110
+ abrs = [s.abr for s in cipher_signature.streams.order_by("abr").asc()]
111
  assert abrs == ["50kbps", "70kbps", "96kbps", "128kbps", "160kbps"]
112
 
113
 
 
135
 
136
 
137
  def test_filter_is_dash(cipher_signature):
138
+ streams = cipher_signature.streams.filter(is_dash=False)
139
  itags = [s.itag for s in streams]
140
  assert itags == [18, 398, 397, 396, 395, 394]
141
 
 
146
 
147
  def test_get_audio_only_with_subtype(cipher_signature):
148
  assert cipher_signature.streams.get_audio_only(subtype="webm").itag == 251
149
+
150
+
151
+ def test_sequence(cipher_signature):
152
+ assert len(cipher_signature.streams) == 22
153
+ assert cipher_signature.streams[0] is not None
tests/test_streams.py CHANGED
@@ -41,7 +41,7 @@ def test_caption_tracks(presigned_video):
41
 
42
 
43
  def test_captions(presigned_video):
44
- assert len(presigned_video.captions.all()) == 13
45
 
46
 
47
  def test_description(cipher_signature):
 
41
 
42
 
43
  def test_captions(presigned_video):
44
+ assert len(presigned_video.captions) == 13
45
 
46
 
47
  def test_description(cipher_signature):