diff options
author | Wojciech Polak <polak@gnu.org> | 2015-07-03 16:11:17 +0200 |
---|---|---|
committer | Wojciech Polak <polak@gnu.org> | 2015-07-03 16:11:17 +0200 |
commit | b7536e1ba516f97b5de8bbae5762e02196644bef (patch) | |
tree | 5edec9555fe210d49684aca4d3f194bb96436090 | |
parent | 0d880880d5a2ab367c9cd425c6dc041f15b991c4 (diff) | |
download | glifestream-b7536e1ba516f97b5de8bbae5762e02196644bef.tar.gz glifestream-b7536e1ba516f97b5de8bbae5762e02196644bef.tar.bz2 |
Update YouTube API to v3
-rw-r--r-- | glifestream/apis/youtube.py | 143 |
1 files changed, 111 insertions, 32 deletions
diff --git a/glifestream/apis/youtube.py b/glifestream/apis/youtube.py index d08ac75..6511756 100644 --- a/glifestream/apis/youtube.py +++ b/glifestream/apis/youtube.py | |||
@@ -13,51 +13,130 @@ | |||
13 | # You should have received a copy of the GNU General Public License along | 13 | # You should have received a copy of the GNU General Public License along |
14 | # with this program. If not, see <http://www.gnu.org/licenses/>. | 14 | # with this program. If not, see <http://www.gnu.org/licenses/>. |
15 | 15 | ||
16 | import datetime | ||
16 | from django.utils.translation import ugettext as _ | 17 | from django.utils.translation import ugettext as _ |
17 | from glifestream.stream import media | 18 | from glifestream.stream import media |
18 | from glifestream.apis import webfeed | 19 | from glifestream.stream.models import Entry |
20 | from glifestream.utils import httpclient | ||
21 | from glifestream.utils.time import mtime, now | ||
19 | 22 | ||
20 | 23 | ||
21 | class API (webfeed.API): | 24 | class API: |
22 | name = 'YouTube API v2' | 25 | name = 'YouTube API v3' |
23 | limit_sec = 3600 | 26 | limit_sec = 3600 |
27 | playlist_types = {} | ||
28 | |||
29 | def __init__(self, service, verbose=0, force_overwrite=False): | ||
30 | self.service = service | ||
31 | self.verbose = verbose | ||
32 | self.force_overwrite = force_overwrite | ||
33 | if self.verbose: | ||
34 | print('%s: %s' % (self.name, self.service)) | ||
24 | 35 | ||
25 | def get_urls(self): | 36 | def get_urls(self): |
26 | if self.service.url.startswith('http://'): | 37 | if self.service.url.startswith('http://') or \ |
38 | self.service.url.startswith('https://'): | ||
27 | return (self.service.url,) | 39 | return (self.service.url,) |
28 | else: | 40 | else: |
29 | h = 'http://gdata.youtube.com/feeds/api/users/%s' % self.service.url | 41 | urls = [] |
30 | return ('%s/favorites?v=2' % h, | 42 | if ':' in self.service.url: |
31 | '%s/uploads?v=2' % h) | 43 | apikey, playlists = self.service.url.split(':') |
32 | 44 | for playlist in playlists.split(','): | |
33 | def custom_process(self, e, ent): | 45 | if '#' in playlist: |
34 | if 'yt_videoid' in ent: | 46 | playlist, kind = playlist.split('#') |
35 | vid = ent['yt_videoid'] | 47 | else: |
36 | elif 'media_player' in ent and 'url' in ent['media_player']: | 48 | kind = 'video' |
37 | vid = ent['media_player']['url'][22:] | 49 | url = ('https://www.googleapis.com/youtube/v3/' |
38 | else: | 50 | 'playlistItems?part=snippet,contentDetails,status&' |
39 | vid = None | 51 | 'playlistId=%s&' |
52 | 'maxResults=25&' | ||
53 | 'key=%s' % (playlist, apikey)) | ||
54 | self.playlist_types[url] = kind | ||
55 | urls.append(url) | ||
56 | return urls | ||
57 | |||
58 | def run(self): | ||
59 | for url in self.get_urls(): | ||
60 | try: | ||
61 | self.fetch(url) | ||
62 | except: | ||
63 | pass | ||
64 | |||
65 | def fetch(self, url): | ||
66 | try: | ||
67 | r = httpclient.get(url) | ||
68 | if r.status_code == 200: | ||
69 | self.json = r.json() | ||
70 | self.service.last_checked = now() | ||
71 | self.service.save() | ||
72 | self.process(url) | ||
73 | elif self.verbose: | ||
74 | print('%s (%d) HTTP: %s' % (self.service.api, | ||
75 | self.service.id, r.reason)) | ||
76 | except Exception as e: | ||
77 | if self.verbose: | ||
78 | import sys | ||
79 | import traceback | ||
80 | print('%s (%d) Exception: %s' % (self.service.api, | ||
81 | self.service.id, e)) | ||
82 | traceback.print_exc(file=sys.stdout) | ||
83 | |||
84 | def process(self, url): | ||
85 | for ent in self.json.get('items', ()): | ||
86 | snippet = ent.get('snippet', {}) | ||
87 | date = snippet['publishedAt'][:10] | ||
88 | |||
89 | vid = ent['contentDetails']['videoId'] | ||
90 | if self.playlist_types[url] == 'favorite': | ||
91 | guid = 'tag:youtube.com,2008:favorite:%s' % ent.get('id') | ||
92 | else: | ||
93 | guid = 'tag:youtube.com,2008:video:%s' % vid | ||
40 | 94 | ||
41 | if vid and 'media_thumbnail' in ent and len(ent.media_thumbnail): | 95 | t = datetime.datetime.strptime(snippet['publishedAt'], |
42 | tn = None | 96 | '%Y-%m-%dT%H:%M:%S.000Z') |
43 | for mt in ent.media_thumbnail: | 97 | |
44 | if 'name' in mt and mt['name'] == 'hqdefault': | 98 | if self.verbose: |
45 | tn = mt | 99 | print("ID: %s" % guid) |
100 | try: | ||
101 | e = Entry.objects.get(service=self.service, guid=guid) | ||
102 | if not self.force_overwrite and e.date_updated \ | ||
103 | and mtime(t.timetuple()) <= e.date_updated: | ||
104 | continue | ||
105 | if e.protected: | ||
106 | continue | ||
107 | except Entry.DoesNotExist: | ||
108 | e = Entry(service=self.service, guid=guid) | ||
109 | |||
110 | e.title = snippet['title'] | ||
111 | e.link = 'https://www.youtube.com/watch?v=%s' % vid | ||
112 | e.date_published = t | ||
113 | e.date_updated = t | ||
114 | e.author_name = snippet['channelTitle'] | ||
115 | |||
116 | if vid and 'thumbnails' in snippet: | ||
117 | tn = None | ||
118 | if 'high' in snippet['thumbnails']: | ||
119 | tn = snippet['thumbnails']['high'] | ||
46 | tn['width'], tn['height'] = 200, 150 | 120 | tn['width'], tn['height'] = 200, 150 |
47 | if not tn: | 121 | elif 'medium' in snippet['thumbnails']: |
48 | tn = ent.media_thumbnail[0] | 122 | tn = snippet['thumbnails']['medium'] |
123 | tn['width'], tn['height'] = 200, 150 | ||
124 | if not tn: | ||
125 | tn = snippet['thumbnails']['default'] | ||
49 | 126 | ||
50 | if self.service.public: | 127 | if self.service.public: |
51 | tn['url'] = media.save_image(tn['url'], downscale=True, | 128 | tn['url'] = media.save_image(tn['url'], downscale=True, |
52 | size=(200, 150)) | 129 | size=(200, 150)) |
53 | 130 | ||
54 | e.link = e.link.replace('&feature=youtube_gdata', '') | 131 | e.content = """<table class="vc"><tr><td><div id="youtube-%s" class="play-video"><a href="%s" rel="nofollow"><img src="%s" width="%s" height="%s" alt="YouTube Video" /></a><div class="playbutton"></div></div></td></tr></table>""" % ( |
55 | e.content = """<table class="vc"><tr><td><div id="youtube-%s" class="play-video"><a href="%s" rel="nofollow"><img src="%s" width="%s" height="%s" alt="YouTube Video" /></a><div class="playbutton"></div></div></td></tr></table>""" % ( | 132 | vid, e.link, tn['url'], tn['width'], tn['height']) |
56 | vid, e.link, tn['url'], tn['width'], tn['height']) | 133 | else: |
57 | else: | 134 | e.content = '<a href="%s">%s</a>' % (e.link, e.title) |
58 | e.content = ent.get('yt_state', '<a href="%s">%s</a>' % | 135 | |
59 | (ent.get('link', '#').replace( | 136 | try: |
60 | '&feature=youtube_gdata', ''), ent.get('title', ''))) | 137 | e.save() |
138 | except Exception as exc: | ||
139 | print(exc) | ||
61 | 140 | ||
62 | 141 | ||
63 | def filter_title(entry): | 142 | def filter_title(entry): |