|
|
|
import logging
|
|
|
|
import os
|
|
|
|
|
|
|
|
import flask
|
|
|
|
import flask_apscheduler
|
|
|
|
import flask_restful
|
|
|
|
import flask_restful.fields
|
|
|
|
import flask_restful.reqparse
|
|
|
|
import sqlalchemy
|
|
|
|
import sqlalchemy.engine
|
|
|
|
|
|
|
|
from db import db, Video, Comment, Association
|
|
|
|
|
|
|
|
|
|
|
|
app = flask.Flask(__name__)
|
|
|
|
app.logger.setLevel(logging.INFO)
|
|
|
|
app.config.update(
|
|
|
|
ERROR_404_HELP=False,
|
|
|
|
SQLALCHEMY_TRACK_MODIFICATIONS=False,
|
|
|
|
SQLALCHEMY_DATABASE_URI=os.getenv('SQLALCHEMY_DATABASE_URI'),
|
|
|
|
SCHEDULER_TIMEZONE='UTC',
|
|
|
|
SCHEDULER_JOBS=[dict(
|
|
|
|
id='sync',
|
|
|
|
func='sync:Sync.perform',
|
|
|
|
args=(app, db),
|
|
|
|
max_instances=1,
|
|
|
|
trigger='interval',
|
|
|
|
seconds=300)])
|
|
|
|
|
|
|
|
if app.config.get('SQLALCHEMY_DATABASE_URI', '').startswith('sqlite://'):
|
|
|
|
@sqlalchemy.event.listens_for(sqlalchemy.engine.Engine, 'connect')
|
|
|
|
def set_sqlite_pragma(dbapi_connection, connection_record):
|
|
|
|
dbapi_connection.execute('PRAGMA journal_mode=WAL')
|
|
|
|
dbapi_connection.execute('PRAGMA synchronous=NORMAL')
|
|
|
|
|
|
|
|
db.init_app(app)
|
|
|
|
db.create_all(app=app)
|
|
|
|
|
|
|
|
scheduler = flask_apscheduler.APScheduler()
|
|
|
|
scheduler.init_app(app)
|
|
|
|
|
|
|
|
api = flask_restful.Api(app)
|
|
|
|
|
|
|
|
|
|
|
|
video_fields = {
|
|
|
|
'id': flask_restful.fields.Integer(),
|
|
|
|
'broadcast_type': flask_restful.fields.String(),
|
|
|
|
'title': flask_restful.fields.String(),
|
|
|
|
'description': flask_restful.fields.String(),
|
|
|
|
'game': flask_restful.fields.String(),
|
|
|
|
'length': flask_restful.fields.Integer(),
|
|
|
|
'thumbnail_small': flask_restful.fields.String(),
|
|
|
|
'thumbnail_medium': flask_restful.fields.String(),
|
|
|
|
'thumbnail_large': flask_restful.fields.String(),
|
|
|
|
'created_at': flask_restful.fields.DateTime(dt_format='iso8601'),
|
|
|
|
'updated_at': flask_restful.fields.DateTime(dt_format='iso8601'),
|
|
|
|
'recorded_at': flask_restful.fields.DateTime(dt_format='iso8601'),
|
|
|
|
'published_at': flask_restful.fields.DateTime(dt_format='iso8601'),
|
|
|
|
}
|
|
|
|
|
|
|
|
comment_fields = {
|
|
|
|
'id': flask_restful.fields.String(attribute='comment.id'),
|
|
|
|
'video_id': flask_restful.fields.Integer(),
|
|
|
|
'offset': flask_restful.fields.Float(),
|
|
|
|
'commenter_id': flask_restful.fields.Integer(attribute='comment.commenter_id'),
|
|
|
|
'commenter_name': flask_restful.fields.String(attribute='comment.commenter_name'),
|
|
|
|
'commenter_display_name': flask_restful.fields.String(attribute='comment.commenter_display_name'),
|
|
|
|
'commenter_logo': flask_restful.fields.String(attribute='comment.commenter_logo'),
|
|
|
|
'source': flask_restful.fields.String(attribute='comment.source'),
|
|
|
|
'message_body': flask_restful.fields.String(attribute='comment.message_body'),
|
|
|
|
'message_user_color': flask_restful.fields.String(attribute='comment.message_user_color'),
|
|
|
|
'message_user_badges': flask_restful.fields.String(attribute='comment.message_user_badges'),
|
|
|
|
'created_at': flask_restful.fields.DateTime(dt_format='iso8601', attribute='comment.created_at'),
|
|
|
|
'updated_at': flask_restful.fields.DateTime(dt_format='iso8601', attribute='comment.updated_at'),
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
filter_parser = flask_restful.reqparse.RequestParser()
|
|
|
|
filter_parser.add_argument('filter', type=str)
|
|
|
|
filter_parser.add_argument('sort_by', type=str)
|
|
|
|
filter_parser.add_argument('sort_order', type=str)
|
|
|
|
filter_parser.add_argument('page_number', type=int)
|
|
|
|
filter_parser.add_argument('page_size', type=int)
|
|
|
|
|
|
|
|
|
|
|
|
class VideoResource(flask_restful.Resource):
|
|
|
|
@flask_restful.marshal_with(video_fields)
|
|
|
|
def get(self, id):
|
|
|
|
q = db.session.query(Video).filter(Video.id == id)
|
|
|
|
video = q.first()
|
|
|
|
if not video:
|
|
|
|
flask_restful.abort(404, message='Video {0} does not exist'.format(id))
|
|
|
|
return video, 200
|
|
|
|
|
|
|
|
|
|
|
|
class VideosResource(flask_restful.Resource):
|
|
|
|
@flask_restful.marshal_with(video_fields)
|
|
|
|
def get(self):
|
|
|
|
args = filter_parser.parse_args()
|
|
|
|
q = db.session.query(Video)
|
|
|
|
if args['filter']:
|
|
|
|
q = q.filter(Video.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(Video, 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'])
|
|
|
|
videos = q.all()
|
|
|
|
return videos, 200, {'X-Total-Count': count}
|
|
|
|
|
|
|
|
|
|
|
|
class CommentResource(flask_restful.Resource):
|
|
|
|
@flask_restful.marshal_with(comment_fields)
|
|
|
|
def get(self, video_id, comment_id):
|
|
|
|
q = db.session.query(Video).filter(Video.id == video_id)
|
|
|
|
video = q.first()
|
|
|
|
if not video:
|
|
|
|
flask_restful.abort(404, message='Video {0} does not exist'.format(video_id))
|
|
|
|
q = db.session.query(Association).join(Comment).filter(
|
|
|
|
Association.video_id == video_id, Comment.id == comment_id)
|
|
|
|
assoc = q.first()
|
|
|
|
if not assoc:
|
|
|
|
flask_restful.abort(404,
|
|
|
|
message='Comment {0} does not exist'.format(comment_id))
|
|
|
|
return assoc, 200
|
|
|
|
|
|
|
|
|
|
|
|
class CommentsResource(flask_restful.Resource):
|
|
|
|
@flask_restful.marshal_with(comment_fields)
|
|
|
|
def get(self, id):
|
|
|
|
args = filter_parser.parse_args()
|
|
|
|
q = db.session.query(Video).filter(Video.id == id)
|
|
|
|
video = q.first()
|
|
|
|
if not video:
|
|
|
|
flask_restful.abort(404, message='Video {0} does not exist'.format(id))
|
|
|
|
q = db.session.query(Association).join(Comment).filter(Association.video_id == id)
|
|
|
|
if args['filter']:
|
|
|
|
q = q.filter(Comment.message_body.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(Association, args['sort_by'], None)
|
|
|
|
if not col:
|
|
|
|
col = getattr(Comment, 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'])
|
|
|
|
assocs = q.all()
|
|
|
|
return assocs, 200, {'X-Total-Count': count}
|
|
|
|
|
|
|
|
|
|
|
|
api.add_resource(VideoResource, '/videos/<int:id>')
|
|
|
|
api.add_resource(VideosResource, '/videos')
|
|
|
|
api.add_resource(CommentResource, '/videos/<int:video_id>/comments/<string:comment_id>')
|
|
|
|
api.add_resource(CommentsResource, '/videos/<int:id>/comments')
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
scheduler.start()
|
|
|
|
app.run(host='0.0.0.0', threaded=True, debug=False)
|