Skip to content

Commit c291434

Browse files
committed
Merge pull request #66 from FurCode/python3.4
youtube.py - Updated for the v3 API (users will need a new API key to use this command)
2 parents 9d05a90 + f067be2 commit c291434

3 files changed

Lines changed: 73 additions & 56 deletions

File tree

config.default.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,8 @@
7979
"wunderground": "",
8080
"googletranslate": "",
8181
"rdio_key": "",
82-
"rdio_secret": ""
82+
"rdio_secret": "",
83+
"google_dev_key": ""
8384
},
8485
"database": "sqlite:///cloudbot.db",
8586
"plugin_loading": {

plugins/youtube.py

Lines changed: 70 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import re
22
import time
3+
import isodate
34

45
import bs4
56
import requests
@@ -11,109 +12,119 @@
1112

1213
youtube_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'
1719
video_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 += ' - \x034NSFW\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")
7279
def 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")
8895
def 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)
124135
def 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 "\x034\x02Invalid 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)

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@ tweepy
1515
pyenchant
1616
pythonwhois
1717
imgurpython
18+
isodate

0 commit comments

Comments
 (0)