SpiffyTitles: Fixes #30 - Upgrades SpiffyTitles Youtube handler to v3 API
This commit is contained in:
parent
b34abcbb23
commit
19df36b638
|
|
@ -40,9 +40,16 @@ Example output:
|
|||
|
||||
### Youtube handler ###
|
||||
|
||||
Note: as of April 20 2015 version 2 of the Youtube API was deprecated. As a result, this feature now
|
||||
requires a [developer key](https://code.google.com/apis/youtube/dashboard/gwt/index.html#settings).
|
||||
|
||||
- Obtain a [developer key](https://code.google.com/apis/youtube/dashboard/gwt/index.html#settings)
|
||||
- Set the key: `!config supybot.plugins.SpiffyTitles.youtubeDeveloperKey your_developer_key_here`
|
||||
- Reload: `!reload SpiffyTitles`
|
||||
|
||||
`youtubeTitleTemplate` - This is the template used when showing the title of a YouTube video
|
||||
|
||||
Default value: `^ {{title}} :: Duration: {{duration}} :: Views: {{view_count}} :: Rating: {{rating}}`
|
||||
Default value: `^ {{title}} :: Duration: {{duration}} :: Views: {{view_count}}`
|
||||
|
||||
Example output:
|
||||
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ conf.registerGlobalValue(SpiffyTitles, 'defaultTitleTemplate',
|
|||
|
||||
# YouTube template
|
||||
conf.registerGlobalValue(SpiffyTitles, 'youtubeTitleTemplate',
|
||||
registry.String("^ {{title}} :: Duration: {{duration}} :: Views: {{view_count}} :: Rating: {{rating}}", _("""Template used for YouTube title responses""")))
|
||||
registry.String("^ {{title}} :: Duration: {{duration}} :: Views: {{view_count}}", _("""Template used for YouTube title responses""")))
|
||||
|
||||
# User agents
|
||||
conf.registerGlobalValue(SpiffyTitles, 'userAgents',
|
||||
|
|
@ -80,7 +80,9 @@ conf.registerGlobalValue(SpiffyTitles, 'imgurTemplate',
|
|||
conf.registerGlobalValue(SpiffyTitles, 'imgurAlbumTemplate',
|
||||
registry.String("^{%if section %} [{{section}}] {% endif -%}{%- if title -%} {{title}} :: {% endif %}{{image_count}} images :: {{view_count}} views :: {%if nsfw == None %}not sure if safe for work{% elif nsfw == True %}not safe for work!{% else %}safe for work{% endif %}", _("""imgur template""")))
|
||||
|
||||
|
||||
# Youtube API
|
||||
conf.registerGlobalValue(SpiffyTitles, 'youtubeDeveloperKey',
|
||||
registry.String("", _("""Youtube developer key - required for Youtube handler.""")))
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
89
plugin.py
89
plugin.py
|
|
@ -21,6 +21,8 @@ import json
|
|||
import cgi
|
||||
import datetime
|
||||
from jinja2 import Template
|
||||
from urllib import urlencode
|
||||
from datetime import timedelta
|
||||
|
||||
try:
|
||||
from supybot.i18n import PluginInternationalization
|
||||
|
|
@ -42,6 +44,7 @@ class SpiffyTitles(callbacks.Plugin):
|
|||
self.__parent.__init__(irc)
|
||||
|
||||
self.link_throttle_in_seconds = self.registryValue("cooldownInSeconds")
|
||||
self.youtube_developer_key = self.registryValue("youtubeDeveloperKey")
|
||||
|
||||
"""
|
||||
Check if imgur client id or secret are set, and if so initialize
|
||||
|
|
@ -55,7 +58,7 @@ class SpiffyTitles(callbacks.Plugin):
|
|||
self.handlers["i.imgur.com"] = self.handler_imgur_image
|
||||
|
||||
# Albums, galleries, etc
|
||||
self.handlers["imgur.com"] = self.handler_imgur
|
||||
#self.handlers["imgur.com"] = self.handler_imgur
|
||||
|
||||
# Initialize API client
|
||||
try:
|
||||
|
|
@ -68,7 +71,8 @@ class SpiffyTitles(callbacks.Plugin):
|
|||
except ImportError, e:
|
||||
self.log.error("SpiffyTitles ImportError: %s" % str(e))
|
||||
|
||||
self.add_youtube_handlers()
|
||||
if self.youtube_developer_key:
|
||||
self.add_youtube_handlers()
|
||||
|
||||
def doPrivmsg(self, irc, msg):
|
||||
"""
|
||||
|
|
@ -79,7 +83,7 @@ class SpiffyTitles(callbacks.Plugin):
|
|||
is_ctcp = ircmsgs.isCtcp(msg)
|
||||
message = msg.args[1]
|
||||
now = datetime.datetime.now()
|
||||
|
||||
|
||||
if is_channel and not is_ctcp:
|
||||
channel_is_allowed = self.is_channel_allowed(channel)
|
||||
url = self.get_url_from_message(message)
|
||||
|
|
@ -225,7 +229,14 @@ class SpiffyTitles(callbacks.Plugin):
|
|||
title = ""
|
||||
|
||||
if video_id:
|
||||
api_url = "https://gdata.youtube.com/feeds/api/videos/%s?v=2&alt=jsonc" % (video_id)
|
||||
options = {
|
||||
"part": "snippet,statistics,contentDetails",
|
||||
"maxResults": 1,
|
||||
"key": self.youtube_developer_key,
|
||||
"id": video_id
|
||||
}
|
||||
encoded_options = urlencode(options)
|
||||
api_url = "https://www.googleapis.com/youtube/v3/videos?%s" % (encoded_options)
|
||||
agent = self.get_user_agent()
|
||||
headers = {
|
||||
"User-Agent": agent
|
||||
|
|
@ -241,11 +252,12 @@ class SpiffyTitles(callbacks.Plugin):
|
|||
|
||||
if response:
|
||||
try:
|
||||
data = response["data"]
|
||||
title = data["title"]
|
||||
rating = str(round(data["rating"], 2))
|
||||
view_count = "{:,}".format(int(data["viewCount"]))
|
||||
duration_seconds = int(data["duration"])
|
||||
items = response["items"]
|
||||
video = items[0]
|
||||
title = video["snippet"]["title"]
|
||||
statistics = video["statistics"]
|
||||
view_count = "{:,}".format(int(statistics["viewCount"]))
|
||||
duration_seconds = self.get_total_seconds_from_duration(video["contentDetails"]["duration"])
|
||||
|
||||
"""
|
||||
#23 - If duration is zero, then it"s a LIVE video
|
||||
|
|
@ -264,15 +276,14 @@ class SpiffyTitles(callbacks.Plugin):
|
|||
|
||||
compiled_template = yt_template.render({
|
||||
"title": title,
|
||||
"rating": rating,
|
||||
"duration": duration,
|
||||
"view_count": view_count
|
||||
})
|
||||
|
||||
title = compiled_template
|
||||
|
||||
except IndexError:
|
||||
self.log.error("SpiffyTitles: IndexError parsing Youtube API JSON response")
|
||||
except IndexError, e:
|
||||
self.log.error("SpiffyTitles: IndexError parsing Youtube API JSON response: %s" % (str(e)))
|
||||
else:
|
||||
self.log.error("SpiffyTitles: Error parsing Youtube API JSON response")
|
||||
else:
|
||||
|
|
@ -287,6 +298,21 @@ class SpiffyTitles(callbacks.Plugin):
|
|||
|
||||
return self.handler_default(url, domain)
|
||||
|
||||
def get_total_seconds_from_duration(self, input):
|
||||
"""
|
||||
Duration comes in a format like this: PT4M41S which translates to
|
||||
4 minutes and 41 seconds. This method returns the total seconds
|
||||
so that the duration can be parsed as usual.
|
||||
"""
|
||||
pattern = regex = re.compile('(?P<sign>-?)P(?:(?P<years>\d+)Y)?(?:(?P<months>\d+)M)?(?:(?P<days>\d+)D)?(?:T(?:(?P<hours>\d+)H)?(?:(?P<minutes>\d+)M)?(?:(?P<seconds>\d+)S)?)?')
|
||||
duration = regex.match(input).groupdict(0)
|
||||
|
||||
delta = timedelta(hours=int(duration['hours']),
|
||||
minutes=int(duration['minutes']),
|
||||
seconds=int(duration['seconds']))
|
||||
|
||||
return delta.total_seconds()
|
||||
|
||||
def handler_default(self, url, domain):
|
||||
"""
|
||||
Default handler for websites
|
||||
|
|
@ -303,6 +329,15 @@ class SpiffyTitles(callbacks.Plugin):
|
|||
|
||||
return title_template
|
||||
|
||||
def is_valid_imgur_id(self, input):
|
||||
"""
|
||||
Tests if input matches the typical imgur id, which seems to be alphanumeric. Images, galleries,
|
||||
and albums all share their format in their identifier.
|
||||
"""
|
||||
match = re.match(r"[a-z0-9]+", input, re.IGNORECASE)
|
||||
|
||||
return match is not None
|
||||
|
||||
def handler_imgur(self, url, info):
|
||||
"""
|
||||
Queries imgur API for additional information about imgur links.
|
||||
|
|
@ -311,10 +346,13 @@ class SpiffyTitles(callbacks.Plugin):
|
|||
"""
|
||||
is_album = info.path.startswith("/a/")
|
||||
is_gallery = info.path.startswith("/gallery/")
|
||||
is_image_page = not is_album and not is_gallery and re.match(r"^\/[a-zA-Z0-9]+", info.path)
|
||||
result = None
|
||||
|
||||
if is_album:
|
||||
result = self.handler_imgur_album(url, info)
|
||||
elif is_image_page:
|
||||
result = self.handler_imgur_image(url, info)
|
||||
else:
|
||||
result = self.handler_default(url, info)
|
||||
|
||||
|
|
@ -335,7 +373,7 @@ class SpiffyTitles(callbacks.Plugin):
|
|||
if "?" in album_id:
|
||||
album_id = album_id.split("?")[0]
|
||||
|
||||
if album_id:
|
||||
if self.is_valid_imgur_id(album_id):
|
||||
self.log.info("SpiffyTitles: found imgur album id %s" % (album_id))
|
||||
|
||||
try:
|
||||
|
|
@ -366,21 +404,26 @@ class SpiffyTitles(callbacks.Plugin):
|
|||
"""
|
||||
Handles retrieving information about images from the imgur API.
|
||||
|
||||
This handler is only run when the domain is i.imgur.com which is usually
|
||||
just images, except in the case of gifv - which is a HTML file which has
|
||||
a title. The latter case is why there are fallbacks here.
|
||||
|
||||
The path comes in this form: /image_id.extension so strip off the left
|
||||
forward slash and then split by period to get the image id.
|
||||
Used for both direct images and imgur.com/some_image_id_here type links, as
|
||||
they're both single images.
|
||||
"""
|
||||
from imgurpython.helpers.error import ImgurClientRateLimitError
|
||||
from imgurpython.helpers.error import ImgurClientError
|
||||
|
||||
path = info.path.lstrip("/")
|
||||
image_id = path.split(".")[0]
|
||||
title = None
|
||||
|
||||
if image_id:
|
||||
"""
|
||||
If there is a period in the path, it's a direct link to an image. If not, then
|
||||
it's a imgur.com/some_image_id_here type link
|
||||
"""
|
||||
if "." in info.path:
|
||||
path = info.path.lstrip("/")
|
||||
image_id = path.split(".")[0]
|
||||
else:
|
||||
image_id = info.path.lstrip("/")
|
||||
|
||||
if self.is_valid_imgur_id(image_id):
|
||||
self.log.info("SpiffyTitles: found image id %s" % (image_id))
|
||||
|
||||
try:
|
||||
image = self.imgur_client.get_image(image_id)
|
||||
|
||||
|
|
|
|||
6
test.py
6
test.py
|
|
@ -15,10 +15,6 @@ class SpiffyTitlesTestCase(ChannelPluginTestCase):
|
|||
ChannelPluginTestCase.setUp(self)
|
||||
|
||||
self.assertNotError('reload SpiffyTitles')
|
||||
|
||||
def test_is_url(self):
|
||||
actual = self.is_url("http://google.com")
|
||||
|
||||
self.assertTrue(actual)
|
||||
|
||||
|
||||
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79:
|
||||
|
|
|
|||
Loading…
Reference in New Issue