8
8
from cloudbot .util import colors , timeformat
9
9
from cloudbot .util .formatting import pluralize_auto
10
10
11
- youtube_re = re .compile (r'(?:youtube.*?(?:v=|/v/)|youtu\.be/|yooouuutuuube.*?id=)([-_a-zA-Z0-9]+)' , re .I )
11
+ youtube_re = re .compile (
12
+ r'(?:youtube.*?(?:v=|/v/)|youtu\.be/|yooouuutuuube.*?id=)([-_a-zA-Z0-9]+)' , re .I
13
+ )
12
14
13
15
base_url = 'https://www.googleapis.com/youtube/v3/'
14
16
api_url = base_url + 'videos?part=contentDetails%2C+snippet%2C+statistics&id={}&key={}'
15
17
search_api_url = base_url + 'search?part=id&maxResults=1'
16
18
playlist_api_url = base_url + 'playlists?part=snippet%2CcontentDetails%2Cstatus'
17
19
video_url = "http://youtu.be/%s"
18
- err_no_api = "The YouTube API is off in the Google Developers Console."
19
20
20
21
21
22
class APIError (Exception ):
@@ -25,16 +26,41 @@ def __init__(self, message, response=None):
25
26
self .response = response
26
27
27
28
29
+ class NoApiKeyError (APIError ):
30
+ def __init__ (self ):
31
+ super ().__init__ ("Missing API key" )
32
+
33
+
34
+ class NoResultsError (APIError ):
35
+ def __init__ (self ):
36
+ super ().__init__ ("No results" )
37
+
38
+
39
+ def handle_api_errors (response ):
40
+ try :
41
+ response .raise_for_status ()
42
+ except requests .RequestException as e :
43
+ try :
44
+ data = response .json ()
45
+ except ValueError :
46
+ raise e
47
+
48
+ errors = data .get ('errors' )
49
+ if not errors :
50
+ return
51
+
52
+ first_error = errors [0 ]
53
+ domain = first_error ['domain' ]
54
+ reason = first_error ['reason' ]
55
+ raise APIError ("API Error ({}/{})" .format (domain , reason ), data ) from e
56
+
57
+
28
58
def get_video_description (video_id ):
29
59
dev_key = bot .config .get_api_key ("google_dev_key" )
30
60
request = requests .get (api_url .format (video_id , dev_key ))
31
61
json = request .json ()
32
62
33
- if json .get ('error' ):
34
- if json ['error' ]['code' ] == 403 :
35
- raise APIError (err_no_api , json )
36
-
37
- raise APIError ("Unknown error" , json )
63
+ handle_api_errors (request )
38
64
39
65
data = json ['items' ]
40
66
if not data :
@@ -50,7 +76,9 @@ def get_video_description(video_id):
50
76
return out
51
77
52
78
length = isodate .parse_duration (content_details ['duration' ])
53
- out += ' - length \x02 {}\x02 ' .format (timeformat .format_time (int (length .total_seconds ()), simple = True ))
79
+ out += ' - length \x02 {}\x02 ' .format (
80
+ timeformat .format_time (int (length .total_seconds ()), simple = True )
81
+ )
54
82
try :
55
83
total_votes = float (statistics ['likeCount' ]) + float (statistics ['dislikeCount' ])
56
84
except (LookupError , ValueError ):
@@ -62,18 +90,18 @@ def get_video_description(video_id):
62
90
dislikes = pluralize_auto (int (statistics ['dislikeCount' ]), "dislike" )
63
91
64
92
percent = 100 * float (statistics ['likeCount' ]) / total_votes
65
- out += ' - {}, {} (\x02 {:.1f}\x02 %)' .format (likes ,
66
- dislikes , percent )
93
+ out += ' - {}, {} (\x02 {:.1f}\x02 %)' .format (likes , dislikes , percent )
67
94
68
95
if 'viewCount' in statistics :
69
96
views = int (statistics ['viewCount' ])
70
- out += ' - \x02 {:,}\x02 view{}' .format (views , "s" [views == 1 :])
97
+ out += ' - \x02 {:,}\x02 view{}' .format (views , "s" [views == 1 :])
71
98
72
99
uploader = snippet ['channelTitle' ]
73
100
74
101
upload_time = isodate .parse_datetime (snippet ['publishedAt' ])
75
- out += ' - \x02 {}\x02 on \x02 {}\x02 ' .format (uploader ,
76
- upload_time .strftime ("%Y.%m.%d" ))
102
+ out += ' - \x02 {}\x02 on \x02 {}\x02 ' .format (
103
+ uploader , upload_time .strftime ("%Y.%m.%d" )
104
+ )
77
105
78
106
try :
79
107
yt_rating = content_details ['contentRating' ]['ytRating' ]
@@ -86,31 +114,27 @@ def get_video_description(video_id):
86
114
return out
87
115
88
116
89
- def get_video_id (reply , text ):
117
+ def get_video_id (text ):
90
118
dev_key = bot .config .get_api_key ('google_dev_key' )
91
119
if not dev_key :
92
- return None , "This command requires a Google Developers Console API key."
120
+ raise NoApiKeyError ()
93
121
94
122
try :
95
- request = requests .get (search_api_url , params = { 'q' : text , 'key' : dev_key , 'type' : 'video' })
96
- request . raise_for_status ()
97
- except Exception :
98
- reply ( "Error performing search." )
99
- raise
123
+ request = requests .get (
124
+ search_api_url , params = { 'q' : text , 'key' : dev_key , 'type' : 'video' }
125
+ )
126
+ except requests . RequestException as e :
127
+ raise APIError ( "Unable to connect to API" ) from e
100
128
101
129
json = request .json ()
102
130
103
- if json .get ('error' ):
104
- if json ['error' ]['code' ] == 403 :
105
- return None , err_no_api
106
-
107
- return None , "Error performing search."
131
+ handle_api_errors (request )
108
132
109
133
if not json .get ('items' ):
110
- return None , "No results found."
134
+ raise NoResultsError ()
111
135
112
136
video_id = json ['items' ][0 ]['id' ]['videoId' ]
113
- return video_id , None
137
+ return video_id
114
138
115
139
116
140
@hook .regex (youtube_re )
@@ -121,11 +145,8 @@ def youtube_url(match):
121
145
@hook .command ("youtube" , "you" , "yt" , "y" )
122
146
def youtube (text , reply ):
123
147
"""<query> - Returns the first YouTube search result for <query>."""
124
- video_id , err = get_video_id (reply , text )
125
- if err :
126
- return err
127
-
128
148
try :
149
+ video_id = get_video_id (text )
129
150
return get_video_description (video_id ) + " - " + video_url % video_id
130
151
except APIError as e :
131
152
reply (e .message )
@@ -135,18 +156,17 @@ def youtube(text, reply):
135
156
@hook .command ("youtime" , "ytime" )
136
157
def youtime (text , reply ):
137
158
"""<query> - Gets the total run time of the first YouTube search result for <query>."""
138
- video_id , err = get_video_id (reply , text )
139
- if err :
140
- return err
141
-
142
159
dev_key = bot .config .get_api_key ('google_dev_key' )
143
- request = requests .get (api_url .format (video_id , dev_key ))
144
- request .raise_for_status ()
160
+ try :
161
+ video_id = get_video_id (text )
162
+ request = requests .get (api_url .format (video_id , dev_key ))
163
+ handle_api_errors (request )
164
+ except APIError as e :
165
+ reply (e .message )
166
+ raise
145
167
146
168
json = request .json ()
147
169
148
- if json .get ('error' ):
149
- return
150
170
data = json ['items' ]
151
171
snippet = data [0 ]['snippet' ]
152
172
content_details = data [0 ]['contentDetails' ]
@@ -163,42 +183,37 @@ def youtime(text, reply):
163
183
length_text = timeformat .format_time (l_sec , simple = True )
164
184
total_text = timeformat .format_time (total , accuracy = 8 )
165
185
166
- return 'The video \x02 {}\x02 has a length of {} and has been viewed {:,} times for ' \
167
- 'a total run time of {}!' .format (snippet ['title' ], length_text , views ,
168
- total_text )
186
+ return (
187
+ 'The video \x02 {}\x02 has a length of {} and has been viewed {:,} times for '
188
+ 'a total run time of {}!' .format (
189
+ snippet ['title' ], length_text , views , total_text
190
+ )
191
+ )
169
192
170
193
171
- ytpl_re = re .compile (r'(.*:)//(www.youtube.com/playlist|youtube.com/playlist)(:[0-9]+)?(.*)' , re .I )
194
+ ytpl_re = re .compile (
195
+ r'(.*:)//(www.youtube.com/playlist|youtube.com/playlist)(:[0-9]+)?(.*)' , re .I
196
+ )
172
197
173
198
174
199
@hook .regex (ytpl_re )
175
- def ytplaylist_url (match , reply ):
200
+ def ytplaylist_url (match ):
176
201
location = match .group (4 ).split ("=" )[- 1 ]
177
202
dev_key = bot .config .get_api_key ("google_dev_key" )
178
- try :
179
- request = requests .get (playlist_api_url , params = {"id" : location , "key" : dev_key })
180
- request .raise_for_status ()
181
- except Exception :
182
- reply ("Error looking up playlist." )
183
- raise
203
+ request = requests .get (playlist_api_url , params = {"id" : location , "key" : dev_key })
204
+ handle_api_errors (request )
184
205
185
206
json = request .json ()
186
207
187
- if json .get ('error' ):
188
- if json ['error' ]['code' ] == 403 :
189
- return err_no_api
190
-
191
- return 'Error looking up playlist.'
192
-
193
208
data = json ['items' ]
194
209
if not data :
195
- return "No results found."
210
+ return
196
211
197
212
snippet = data [0 ]['snippet' ]
198
213
content_details = data [0 ]['contentDetails' ]
199
214
200
215
title = snippet ['title' ]
201
216
author = snippet ['channelTitle' ]
202
217
num_videos = int (content_details ['itemCount' ])
203
- count_videos = ' - \x02 {:,}\x02 video{}' .format (num_videos , "s" [num_videos == 1 :])
218
+ count_videos = ' - \x02 {:,}\x02 video{}' .format (num_videos , "s" [num_videos == 1 :])
204
219
return "\x02 {}\x02 {} - \x02 {}\x02 " .format (title , count_videos , author )
0 commit comments