Taylor Fox Dahlin commited on
Commit
f5188a1
·
unverified ·
1 Parent(s): 8883b15

Improvement/better exceptions (#896)

Browse files

* Reworked the different unavailable video exceptions so they can be caught by a generic exception.

* Added handling for videos being unavailable due to error.

pytube/__main__.py CHANGED
@@ -135,6 +135,9 @@ class YouTube:
135
  'Please sign in to verify that you may see it.'
136
  ):
137
  raise VideoPrivate(video_id=self.video_id)
 
 
 
138
 
139
  def descramble(self) -> None:
140
  """Descramble the stream data and build Stream instances.
 
135
  'Please sign in to verify that you may see it.'
136
  ):
137
  raise VideoPrivate(video_id=self.video_id)
138
+ elif status == 'ERROR':
139
+ if reason == 'Video unavailable':
140
+ raise VideoUnavailable(video_id=self.video_id)
141
 
142
  def descramble(self) -> None:
143
  """Descramble the stream data and build Stream instances.
pytube/exceptions.py CHANGED
@@ -5,7 +5,7 @@ from typing import Union
5
 
6
 
7
  class PytubeError(Exception):
8
- """Base pytube exception that all others inherent.
9
 
10
  This is done to not pollute the built-in exceptions, which *could* result
11
  in unintended errors being unexpectedly and incorrectly handled within
@@ -13,6 +13,10 @@ class PytubeError(Exception):
13
  """
14
 
15
 
 
 
 
 
16
  class ExtractError(PytubeError):
17
  """Data extraction based exception."""
18
 
@@ -32,55 +36,66 @@ class RegexMatchError(ExtractError):
32
  self.pattern = pattern
33
 
34
 
35
- class LiveStreamError(ExtractError):
36
- """Video is a live stream."""
37
-
38
  def __init__(self, video_id: str):
39
  """
40
  :param str video_id:
41
  A YouTube video identifier.
42
  """
43
- super().__init__(f"{video_id} is streaming live and cannot be loaded")
44
-
45
  self.video_id = video_id
 
46
 
 
 
 
47
 
48
- class VideoUnavailable(PytubeError):
49
- """Video is unavailable."""
 
50
 
51
  def __init__(self, video_id: str):
52
  """
53
  :param str video_id:
54
  A YouTube video identifier.
55
  """
56
- super().__init__(f"{video_id} is unavailable")
57
-
58
  self.video_id = video_id
 
 
 
 
 
59
 
60
 
61
- class VideoPrivate(ExtractError):
62
  def __init__(self, video_id: str):
63
  """
64
  :param str video_id:
65
  A YouTube video identifier.
66
  """
67
- super().__init__('%s is a private video' % video_id)
68
  self.video_id = video_id
 
69
 
 
 
 
70
 
71
- class RecordingUnavailable(ExtractError):
 
72
  def __init__(self, video_id: str):
73
  """
74
  :param str video_id:
75
  A YouTube video identifier.
76
  """
77
- super().__init__(
78
- '%s does not have a live stream recording available' % video_id
79
- )
80
  self.video_id = video_id
 
 
 
 
 
81
 
82
 
83
- class MembersOnly(PytubeError):
84
  """Video is members-only.
85
 
86
  YouTube has special videos that are only viewable to users who have
@@ -92,21 +107,23 @@ class MembersOnly(PytubeError):
92
  :param str video_id:
93
  A YouTube video identifier.
94
  """
95
- super().__init__('%s is a members-only video' % video_id)
96
  self.video_id = video_id
 
97
 
 
 
 
98
 
99
- class VideoRegionBlocked(ExtractError):
 
100
  def __init__(self, video_id: str):
101
  """
102
  :param str video_id:
103
  A YouTube video identifier.
104
  """
105
- super().__init__(
106
- '%s is not available in your region' % video_id
107
- )
108
  self.video_id = video_id
 
109
 
110
-
111
- class HTMLParseError(PytubeError):
112
- """HTML could not be parsed"""
 
5
 
6
 
7
  class PytubeError(Exception):
8
+ """Base pytube exception that all others inherit.
9
 
10
  This is done to not pollute the built-in exceptions, which *could* result
11
  in unintended errors being unexpectedly and incorrectly handled within
 
13
  """
14
 
15
 
16
+ class HTMLParseError(PytubeError):
17
+ """HTML could not be parsed"""
18
+
19
+
20
  class ExtractError(PytubeError):
21
  """Data extraction based exception."""
22
 
 
36
  self.pattern = pattern
37
 
38
 
39
+ class VideoUnavailable(PytubeError):
40
+ """Base video unavailable error."""
 
41
  def __init__(self, video_id: str):
42
  """
43
  :param str video_id:
44
  A YouTube video identifier.
45
  """
 
 
46
  self.video_id = video_id
47
+ super().__init__(self.error_string)
48
 
49
+ @property
50
+ def error_string(self):
51
+ return f'{self.video_id} is unavailable'
52
 
53
+
54
+ class LiveStreamError(VideoUnavailable):
55
+ """Video is a live stream."""
56
 
57
  def __init__(self, video_id: str):
58
  """
59
  :param str video_id:
60
  A YouTube video identifier.
61
  """
 
 
62
  self.video_id = video_id
63
+ super().__init__(self.video_id)
64
+
65
+ @property
66
+ def error_string(self):
67
+ return f'{self.video_id} is streaming live and cannot be loaded'
68
 
69
 
70
+ class VideoPrivate(VideoUnavailable):
71
  def __init__(self, video_id: str):
72
  """
73
  :param str video_id:
74
  A YouTube video identifier.
75
  """
 
76
  self.video_id = video_id
77
+ super().__init__(self.video_id)
78
 
79
+ @property
80
+ def error_string(self):
81
+ return f'{self.video_id} is a private video'
82
 
83
+
84
+ class RecordingUnavailable(VideoUnavailable):
85
  def __init__(self, video_id: str):
86
  """
87
  :param str video_id:
88
  A YouTube video identifier.
89
  """
 
 
 
90
  self.video_id = video_id
91
+ super().__init__(self.video_id)
92
+
93
+ @property
94
+ def error_string(self):
95
+ return f'{self.video_id} does not have a live stream recording available'
96
 
97
 
98
+ class MembersOnly(VideoUnavailable):
99
  """Video is members-only.
100
 
101
  YouTube has special videos that are only viewable to users who have
 
107
  :param str video_id:
108
  A YouTube video identifier.
109
  """
 
110
  self.video_id = video_id
111
+ super().__init__(self.video_id)
112
 
113
+ @property
114
+ def error_string(self):
115
+ return f'{self.video_id} is a members-only video'
116
 
117
+
118
+ class VideoRegionBlocked(VideoUnavailable):
119
  def __init__(self, video_id: str):
120
  """
121
  :param str video_id:
122
  A YouTube video identifier.
123
  """
 
 
 
124
  self.video_id = video_id
125
+ super().__init__(self.video_id)
126
 
127
+ @property
128
+ def error_string(self):
129
+ return f'{self.video_id} is not available in your region'
tests/test_exceptions.py CHANGED
@@ -27,6 +27,9 @@ def test_regex_match_error():
27
 
28
 
29
  def test_live_stream_error():
 
 
 
30
  try:
31
  raise LiveStreamError(video_id='YLnZklYFe7E')
32
  except LiveStreamError as e:
@@ -35,6 +38,9 @@ def test_live_stream_error():
35
 
36
 
37
  def test_recording_unavailable_error():
 
 
 
38
  try:
39
  raise RecordingUnavailable(video_id='5YceQ8YqYMc')
40
  except RecordingUnavailable as e:
@@ -43,6 +49,9 @@ def test_recording_unavailable_error():
43
 
44
 
45
  def test_private_error():
 
 
 
46
  try:
47
  raise VideoPrivate('m8uHb5jIGN8')
48
  except VideoPrivate as e:
@@ -51,6 +60,9 @@ def test_private_error():
51
 
52
 
53
  def test_region_locked_error():
 
 
 
54
  try:
55
  raise VideoRegionBlocked('hZpzr8TbF08')
56
  except VideoRegionBlocked as e:
 
27
 
28
 
29
  def test_live_stream_error():
30
+ # Ensure this can be caught as generic VideoUnavailable exception
31
+ with pytest.raises(VideoUnavailable):
32
+ raise LiveStreamError(video_id='YLnZklYFe7E')
33
  try:
34
  raise LiveStreamError(video_id='YLnZklYFe7E')
35
  except LiveStreamError as e:
 
38
 
39
 
40
  def test_recording_unavailable_error():
41
+ # Ensure this can be caught as generic VideoUnavailable exception
42
+ with pytest.raises(VideoUnavailable):
43
+ raise RecordingUnavailable(video_id='5YceQ8YqYMc')
44
  try:
45
  raise RecordingUnavailable(video_id='5YceQ8YqYMc')
46
  except RecordingUnavailable as e:
 
49
 
50
 
51
  def test_private_error():
52
+ # Ensure this can be caught as generic VideoUnavailable exception
53
+ with pytest.raises(VideoUnavailable):
54
+ raise VideoPrivate('m8uHb5jIGN8')
55
  try:
56
  raise VideoPrivate('m8uHb5jIGN8')
57
  except VideoPrivate as e:
 
60
 
61
 
62
  def test_region_locked_error():
63
+ # Ensure this can be caught as generic VideoUnavailable exception
64
+ with pytest.raises(VideoUnavailable):
65
+ raise VideoRegionBlocked('hZpzr8TbF08')
66
  try:
67
  raise VideoRegionBlocked('hZpzr8TbF08')
68
  except VideoRegionBlocked as e: