diff --git a/twitch-cache-api/app.py b/twitch-cache-api/app.py index 8cfab99..d1bfd82 100644 --- a/twitch-cache-api/app.py +++ b/twitch-cache-api/app.py @@ -9,7 +9,7 @@ import flask_restful.reqparse import sqlalchemy import sqlalchemy.engine -from db import db, Video, Comment, Association, Emote, Clip +from db import db, Video, Comment, Association, Emote, Clip, Event app = flask.Flask(__name__) @@ -37,7 +37,13 @@ app.config.update( args=(app, db), max_instances=1, trigger='interval', - seconds=120)]) + seconds=120), + dict(id='sync_events', + func='sync:Sync.sync_events', + args=(app, db), + max_instances=1, + trigger='interval', + seconds=600)]) if app.config.get('SQLALCHEMY_DATABASE_URI', '').startswith('sqlite://'): @sqlalchemy.event.listens_for(sqlalchemy.engine.Engine, 'connect') @@ -119,6 +125,19 @@ clip_fields = { 'created_at': flask_restful.fields.DateTime(dt_format='iso8601'), } +event_fields = { + 'id': flask_restful.fields.String(), + 'start': flask_restful.fields.DateTime(dt_format='iso8601'), + 'end': flask_restful.fields.DateTime(dt_format='iso8601'), + 'title': flask_restful.fields.String(), + 'description': flask_restful.fields.String(), + 'game_id': flask_restful.fields.Integer(), + 'game_name': flask_restful.fields.String(), + 'game_box_small': flask_restful.fields.String(), + 'game_box_medium': flask_restful.fields.String(), + 'game_box_large': flask_restful.fields.String(), +} + filter_parser = flask_restful.reqparse.RequestParser() filter_parser.add_argument('filter', type=str) @@ -287,7 +306,7 @@ class ClipResource(flask_restful.Resource): clip = q.first() if not clip: flask_restful.abort(404, message='Clip {0} does not exist'.format(slug)) - return slug, 200 + return clip, 200 class ClipsResource(flask_restful.Resource): @@ -317,6 +336,43 @@ class ClipsResource(flask_restful.Resource): return clips, 200, {'X-Total-Count': count} +class EventResource(flask_restful.Resource): + @flask_restful.marshal_with(event_fields) + def get(self, id): + q = db.session.query(Event).filter(Event.id == id) + event = q.first() + if not event: + flask_restful.abort(404, message='Event {0} does not exist'.format(id)) + return event, 200 + + +class EventsResource(flask_restful.Resource): + @flask_restful.marshal_with(event_fields) + def get(self): + args = filter_parser.parse_args() + q = db.session.query(Event) + if args['filter']: + q = q.filter(Event.title.ilike('%{}%'.format(args['filter']))) + count = q.count() + if args['sort_order'] == 'random': + q = q.order_by(sqlalchemy.func.random()) + elif args['sort_by']: + col = getattr(Event, args['sort_by'], None) + if col: + if args['sort_order']: + order_by = getattr(col, args['sort_order'], None) + if order_by: + q = q.order_by(order_by()) + else: + q = q.order_by(col) + if args['page_size']: + q = q.limit(args['page_size']) + if args['page_number'] and args['page_size']: + q = q.offset(args['page_number'] * args['page_size']) + events = q.all() + return events, 200, {'X-Total-Count': count} + + api.add_resource(VideoResource, '/videos/') api.add_resource(VideosResource, '/videos') api.add_resource(CommentResource, '/videos//comments/') @@ -326,6 +382,8 @@ api.add_resource(EmoteResource, '/emotes/') api.add_resource(EmotesResource, '/emotes') api.add_resource(ClipResource, '/clips/') api.add_resource(ClipsResource, '/clips') +api.add_resource(EventResource, '/events/') +api.add_resource(EventsResource, '/events') if __name__ == '__main__': diff --git a/twitch-cache-api/db.py b/twitch-cache-api/db.py index fb642df..5cfa69a 100644 --- a/twitch-cache-api/db.py +++ b/twitch-cache-api/db.py @@ -72,3 +72,18 @@ class Clip(db.Model): thumbnail_small = db.Column(db.String) thumbnail_medium = db.Column(db.String) created_at = db.Column(db.DateTime) + + +class Event(db.Model): + __tablename__ = 'events' + + id = db.Column(db.String, primary_key=True) + start = db.Column(db.DateTime) + end = db.Column(db.DateTime) + title = db.Column(db.String) + description = db.Column(db.String) + game_id = db.Column(db.Integer) + game_name = db.Column(db.String) + game_box_small = db.Column(db.String) + game_box_medium = db.Column(db.String) + game_box_large = db.Column(db.String) diff --git a/twitch-cache-api/sync.py b/twitch-cache-api/sync.py index e5a3ae7..3507249 100644 --- a/twitch-cache-api/sync.py +++ b/twitch-cache-api/sync.py @@ -3,7 +3,7 @@ import os import flask_restful.inputs -from db import Video, Comment, Association, Emote, Clip +from db import Video, Comment, Association, Emote, Clip, Event from twitch import Twitch @@ -154,3 +154,30 @@ class Sync(object): 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.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') diff --git a/twitch-cache-api/twitch.py b/twitch-cache-api/twitch.py index 55fd0d0..02a2618 100644 --- a/twitch-cache-api/twitch.py +++ b/twitch-cache-api/twitch.py @@ -95,3 +95,16 @@ class Twitch(object): if not cursor: break return result + + def fetch_events(self, channel_id): + if not channel_id: + return [] + session = FuturesSession() + def get_events(): + url = 'https://api.twitch.tv/v5/channels/{0}/events'.format(channel_id) + params = dict(client_id=self.client_id) + return session.get(url, params=params) + request = get_events() + data = request.result().json() + result = data.get('events', []) + return result