import datetime import os import flask_restful.inputs from db import Video, Comment, Association, Emote, Clip, Event from twitch import Twitch class Sync(object): @staticmethod def _get(d, *keys, default=None): try: result = None for key in keys: if result: if isinstance(result, list): result = result[key] else: result = result.get(key, default) else: result = d.get(key, default) return result except (KeyError, IndexError): return default @staticmethod def _to_datetime(val): if not val: return None result = flask_restful.inputs.datetime_from_iso8601(val) return result.astimezone(tz=datetime.timezone.utc).replace(tzinfo=None) @classmethod def sync_videos(cls, app, db): app.logger.info('Starting synchronization of videos') with app.app_context(): twitch = Twitch(os.getenv('TWITCH_CLIENT_ID'), os.getenv('TWITCH_OAUTH_TOKEN')) channel_id = os.getenv('TWITCH_CHANNEL_ID') updated = [] for vid in twitch.fetch_videos(channel_id): id = cls._get(vid, '_id', default='').lstrip('v') if not id: continue if cls._get(vid, 'status') == 'recording': continue q = db.session.query(Video).filter(Video.id == id) video = q.first() if not video: video = Video(id=id) created_at = cls._get(vid, 'created_at') updated_at = cls._to_datetime(cls._get(vid, 'updated_at', default=created_at)) created_at = cls._to_datetime(created_at) if not video.updated_at or video.updated_at < updated_at: updated.append(id) video.broadcast_type = cls._get(vid, 'broadcast_type') video.title = cls._get(vid, 'title') video.description = cls._get(vid, 'description') video.game = cls._get(vid, 'game') video.length = cls._get(vid, 'length') video.thumbnail_small = cls._get(vid, 'thumbnails', 'small', 0, 'url') video.thumbnail_medium = cls._get(vid, 'thumbnails', 'medium', 0, 'url') video.thumbnail_large = cls._get(vid, 'thumbnails', 'large', 0, 'url') video.created_at = created_at video.updated_at = updated_at video.recorded_at = cls._to_datetime(cls._get(vid, 'recorded_at')) video.published_at = cls._to_datetime(cls._get(vid, 'published_at')) db.session.add(video) db.session.commit() app.logger.info('Updated videos: %s', ', '.join(updated) if updated else 'NONE') for com in twitch.fetch_comments(updated): q = db.session.query(Video).filter(Video.id == cls._get(com, 'content_id')) video = q.first() if not video: continue id = cls._get(com, '_id') q = db.session.query(Comment).filter(Comment.id == id) comment = q.first() if not comment: comment = Comment(id=id) comment.commenter_id = cls._get(com, 'commenter', '_id') comment.commenter_name = cls._get(com, 'commenter', 'name') comment.commenter_display_name = cls._get(com, 'commenter', 'display_name') comment.commenter_logo = cls._get(com, 'commenter', 'logo') comment.source = cls._get(com, 'source') comment.message_body = cls._get(com, 'message', 'body') comment.message_user_color = cls._get(com, 'message', 'user_color') badges = cls._get(com, 'message', 'user_badges') if badges: badges = ','.join(['{_id}:{version}'.format(**b) for b in badges]) comment.message_user_badges = badges comment.created_at = cls._to_datetime(cls._get(com, 'created_at')) comment.updated_at = cls._to_datetime(cls._get(com, 'updated_at')) q = db.session.query(Association).filter( Association.video_id == video.id, Association.comment_id == comment.id) assoc = q.first() if not assoc: assoc = Association() assoc.comment = comment assoc.offset = cls._get(com, 'content_offset_seconds') video.associations.append(assoc) db.session.add(video) db.session.commit() app.logger.info('Synchronization of videos completed') @classmethod def sync_emotes(cls, app, db): app.logger.info('Starting synchronization of emotes') with app.app_context(): twitch = Twitch(os.getenv('TWITCH_CLIENT_ID'), os.getenv('TWITCH_OAUTH_TOKEN')) channel_id = os.getenv('TWITCH_CHANNEL_ID') for em in twitch.fetch_emotes(channel_id): id = cls._get(em, 'id') if not id: continue q = db.session.query(Emote).filter(Emote.id == id) emote = q.first() if not emote: emote = Emote(id=id) emote.code = cls._get(em, 'code') db.session.add(emote) db.session.commit() app.logger.info('Synchronization of emotes completed') @classmethod def sync_clips(cls, app, db): app.logger.info('Starting synchronization of clips') with app.app_context(): twitch = Twitch(os.getenv('TWITCH_CLIENT_ID'), os.getenv('TWITCH_OAUTH_TOKEN')) channel_name = os.getenv('TWITCH_CHANNEL_NAME') for clp in twitch.fetch_clips(channel_name): slug = cls._get(clp, 'slug') if not slug: continue q = db.session.query(Clip).filter(Clip.slug == slug) clip = q.first() if not clip: clip = Clip(slug=slug) clip.code = cls._get(clp, 'code') clip.video_id = cls._get(clp, 'vod', 'id') clip.video_offset = cls._get(clp, 'vod', 'offset') clip.title = cls._get(clp, 'title') clip.game = cls._get(clp, 'game') clip.duration = cls._get(clp, 'duration') clip.curator_id = cls._get(clp, 'curator', 'id') clip.curator_name = cls._get(clp, 'curator', 'name') clip.curator_display_name = cls._get(clp, 'curator', 'display_name') clip.curator_logo = cls._get(clp, 'curator', 'logo') clip.thumbnail_tiny = cls._get(clp, 'thumbnails', 'tiny') clip.thumbnail_small = cls._get(clp, 'thumbnails', 'small') clip.thumbnail_medium = cls._get(clp, 'thumbnails', 'medium') clip.created_at = cls._to_datetime(cls._get(clp, 'created_at')) db.session.add(clip) db.session.commit() app.logger.info('Synchronization of clips completed') @classmethod def sync_events(cls, app, db): app.logger.info('Starting synchronization of events') with app.app_context(): twitch = Twitch(os.getenv('TWITCH_CLIENT_ID'), os.getenv('TWITCH_OAUTH_TOKEN')) channel_id = os.getenv('TWITCH_CHANNEL_ID') for evt in twitch.fetch_events(channel_id): id = cls._get(evt, '_id') if not id: continue q = db.session.query(Event).filter(Event.id == id) event = q.first() if not event: event = Event(id=id) event.start = cls._to_datetime(cls._get(evt, 'start_time')) event.end = cls._to_datetime(cls._get(evt, 'end_time')) event.title = cls._get(evt, 'title') event.description = cls._get(evt, 'description') event.cover_image_id = cls._get(evt, 'cover_image_id') event.cover_image_url = cls._get(evt, 'cover_image_url') event.game_id = cls._get(evt, 'game', '_id') event.game_name = cls._get(evt, 'game', 'name') event.game_box_small = cls._get(evt, 'game', 'box', 'small') event.game_box_medium = cls._get(evt, 'game', 'box', 'medium') event.game_box_large = cls._get(evt, 'game', 'box', 'large') db.session.add(event) db.session.commit() app.logger.info('Synchronization of events completed')