11import re
22import time
3+ import isodate
34
45import bs4
56import requests
1112
1213youtube_re = re .compile (r'(?:youtube.*?(?:v=|/v/)|youtu\.be/|yooouuutuuube.*?id=)([-_a-zA-Z0-9]+)' , re .I )
1314
14- base_url = 'http://gdata.youtube.com/feeds/api/'
15- api_url = base_url + 'videos/{}?v=2&alt=jsonc'
16- search_api_url = base_url + 'videos?v=2&alt=jsonc&max-results=1'
15+ base_url = 'https://www.googleapis.com/youtube/v3/'
16+ api_url = base_url + 'videos?part=contentDetails%2C+snippet%2C+statistics&id={}&key={}'
17+ search_api_url = base_url + 'search?part=id&maxResults=1'
18+ playlist_api_url = base_url + 'playlists?part=snippet%2CcontentDetails%2Cstatus'
1719video_url = "http://youtu.be/%s"
1820
1921
20- def get_video_description (video_id ):
21- json = requests .get (api_url .format (video_id )).json ()
22+ def get_video_description (video_id , key ):
23+ json = requests .get (api_url .format (video_id , key )).json ()
2224
2325 if json .get ('error' ):
2426 return
2527
26- data = json ['data' ]
28+ data = json ['items' ]
29+ snippet = data [0 ]['snippet' ]
30+ statistics = data [0 ]['statistics' ]
31+ content_details = data [0 ]['contentDetails' ]
2732
28- out = '\x02 {}\x02 ' .format (data ['title' ])
33+ out = '\x02 {}\x02 ' .format (snippet ['title' ])
2934
30- if not data .get ('duration' ):
35+ if not content_details .get ('duration' ):
3136 return out
3237
33- length = data ['duration' ]
34- out += ' - length \x02 {}\x02 ' .format (timeformat .format_time (length , simple = True ))
38+ length = isodate .parse_duration (content_details ['duration' ])
39+ out += ' - length \x02 {}\x02 ' .format (timeformat .format_time (int (length .total_seconds ()), simple = True ))
40+ totalvotes = float (statistics ['likeCount' ]) + float (statistics ['dislikeCount' ])
3541
36- if 'ratingCount' in data :
42+ if totalvotes != 0 :
3743 # format
38- likes = pluralize (int (data ['likeCount' ]), "like" )
39- dislikes = pluralize (data [ 'ratingCount' ] - int (data [ 'likeCount ' ]), "dislike" )
44+ likes = pluralize (int (statistics ['likeCount' ]), "like" )
45+ dislikes = pluralize (int (statistics [ 'dislikeCount ' ]), "dislike" )
4046
41- percent = 100 * float (data ['likeCount' ]) / float ( data [ 'ratingCount' ])
47+ percent = 100 * float (statistics ['likeCount' ]) / totalvotes
4248 out += ' - {}, {} (\x02 {:.1f}\x02 %)' .format (likes ,
4349 dislikes , percent )
4450
45- if 'viewCount' in data :
46- views = data ['viewCount' ]
51+ if 'viewCount' in statistics :
52+ views = int ( statistics ['viewCount' ])
4753 out += ' - \x02 {:,}\x02 view{}' .format (views , "s" [views == 1 :])
4854
49- try :
50- json = requests .get (base_url + "users/{}?alt=json" .format (data ["uploader" ])).json ()
51- uploader = json ["entry" ]["author" ][0 ]["name" ][
52- "$t" ]
53- except :
54- uploader = data ["uploader" ]
55+ uploader = snippet ['channelTitle' ]
5556
56- upload_time = time .strptime (data [ 'uploaded ' ], "%Y-%m-%dT%H:%M:%S.000Z" )
57+ upload_time = time .strptime (snippet [ 'publishedAt ' ], "%Y-%m-%dT%H:%M:%S.000Z" )
5758 out += ' - \x02 {}\x02 on \x02 {}\x02 ' .format (uploader ,
5859 time .strftime ("%Y.%m.%d" , upload_time ))
5960
60- if 'contentRating' in data :
61+ if 'contentRating' in content_details :
6162 out += ' - \x03 4NSFW\x02 '
6263
6364 return out
6465
6566
67+ @hook .on_start ()
68+ def load_key (bot ):
69+ global dev_key
70+ dev_key = bot .config .get ("api_keys" , {}).get ("google_dev_key" )
71+
72+
6673@hook .regex (youtube_re )
67- def youtube_url (match ):
68- return get_video_description (match .group (1 ))
74+ def youtube_url (match , bot ):
75+ return get_video_description (match .group (1 ), dev_key )
6976
7077
7178@hook .command ("youtube" , "you" , "yt" , "y" )
7279def youtube (text ):
7380 """youtube <query> -- Returns the first YouTube search result for <query>."""
74- json = requests .get (search_api_url , params = {"q" : text }).json ()
81+ json = requests .get (search_api_url , params = {"q" : text , "key" : dev_key }).json ()
7582
7683 if 'error' in json :
77- return 'error performing search'
84+ return 'Error performing search. '
7885
79- if json ['data ' ]['totalItems ' ] == 0 :
80- return 'no results found'
86+ if json ['pageInfo ' ]['totalResults ' ] == 0 :
87+ return 'No results found. '
8188
82- video_id = json ['data' ][ ' items' ][0 ]['id' ]
89+ video_id = json ['items' ][0 ]['id' ][ 'videoId ' ]
8390
84- return get_video_description (video_id ) + " - " + video_url % video_id
91+ return get_video_description (video_id , dev_key ) + " - " + video_url % video_id
8592
8693
8794@hook .command ("youtime" , "ytime" )
8895def youtime (text ):
8996 """youtime <query> -- Gets the total run time of the first YouTube search result for <query>."""
90- json = requests .get (search_api_url , params = {"q" : text }).json ()
97+ json = requests .get (search_api_url , params = {"q" : text , "key" : dev_key }).json ()
9198
9299 if 'error' in json :
93- return 'error performing search'
100+ return 'Error performing search. '
94101
95- if json ['data ' ]['totalItems ' ] == 0 :
96- return 'no results found'
102+ if json ['pageInfo ' ]['totalResults ' ] == 0 :
103+ return 'No results found. '
97104
98- video_id = json ['data' ][ ' items' ][0 ]['id' ]
99- json = requests .get (api_url .format (video_id )).json ()
105+ video_id = json ['items' ][0 ]['id' ][ 'videoId ' ]
106+ json = requests .get (api_url .format (video_id , dev_key )).json ()
100107
101108 if json .get ('error' ):
102109 return
103- data = json ['data' ]
110+ data = json ['items' ]
111+ snippet = data [0 ]['snippet' ]
112+ content_details = data [0 ]['contentDetails' ]
113+ statistics = data [0 ]['statistics' ]
104114
105- if not data .get ('duration' ):
115+ if not content_details .get ('duration' ):
106116 return
107117
108- length = data ['duration' ]
109- views = data ['viewCount' ]
110- total = int (length * views )
118+ length = isodate .parse_duration (content_details ['duration' ])
119+ l_sec = int (length .total_seconds ())
120+ views = int (statistics ['viewCount' ])
121+ total = int (l_sec * views )
111122
112- length_text = timeformat .format_time (length , simple = True )
123+ length_text = timeformat .format_time (l_sec , simple = True )
113124 total_text = timeformat .format_time (total , accuracy = 8 )
114125
115126 return 'The video \x02 {}\x02 has a length of {} and has been viewed {:,} times for ' \
116- 'a total run time of {}!' .format (data ['title' ], length_text , views ,
127+ 'a total run time of {}!' .format (snippet ['title' ], length_text , views ,
117128 total_text )
118129
119130
@@ -123,13 +134,17 @@ def youtime(text):
123134@hook .regex (ytpl_re )
124135def ytplaylist_url (match ):
125136 location = match .group (4 ).split ("=" )[- 1 ]
126- try :
127- request = requests .get ("https://www.youtube.com/playlist?list=" + location )
128- soup = bs4 .BeautifulSoup (request .text , 'lxml' )
129- except Exception :
130- return "\x03 4\x02 Invalid response."
131- title = soup .find ('title' ).text .split ('-' )[0 ].strip ()
132- author = soup .find ('img' , {'class' : 'channel-header-profile-image' })['title' ]
133- num_videos = soup .find ('ul' , {'class' : 'header-stats' }).findAll ('li' )[0 ].text .split (' ' )[0 ]
134- views = soup .find ('ul' , {'class' : 'header-stats' }).findAll ('li' )[1 ].text .split (' ' )[0 ]
135- return "\x02 {}\x02 - \x02 {}\x02 views - \x02 {}\x02 videos - \x02 {}\x02 " .format (title , views , num_videos , author )
137+ json = requests .get (playlist_api_url , params = {"id" : location , "key" : dev_key }).json ()
138+
139+ if 'error' in json :
140+ return 'Error looking up playlist.'
141+
142+ data = json ['items' ]
143+ snippet = data [0 ]['snippet' ]
144+ content_details = data [0 ]['contentDetails' ]
145+
146+ title = snippet ['title' ]
147+ author = snippet ['channelTitle' ]
148+ num_videos = int (content_details ['itemCount' ])
149+ count_videos = ' - \x02 {:,}\x02 video{}' .format (num_videos , "s" [num_videos == 1 :])
150+ return "\x02 {}\x02 {} - \x02 {}\x02 " .format (title , count_videos , author )
0 commit comments