import asyncio from functools import wraps from http import HTTPStatus from sanic import Blueprint, response from sanic.request import Request from sanic_ext import validate from sanic_ext.extensions.openapi import openapi from wrolpi.common import create_websocket_feed, get_sanic_url, \ wrol_mode_check from wrolpi.common import logger from wrolpi.root_api import add_blueprint, json_response from wrolpi.schema import JSONErrorResponse from .channel import lib as channel_lib from .channel.api import channel_bp from .lib import _refresh_videos, get_statistics from .schema import StreamResponse, \ FavoriteRequest, FavoriteResponse, VideosStatisticsResponse from .video import lib as video_lib from .video.api import video_bp content_bp = Blueprint('VideoContent', '/api/videos') bp = Blueprint('Videos', '/api/videos').group( content_bp, # view and manage video content and settings channel_bp, # view and manage channels video_bp, # view videos ) add_blueprint(bp) logger = logger.getChild(__name__) refresh_queue, refresh_event = create_websocket_feed('refresh', '/feeds/refresh', content_bp) @content_bp.post('/refresh') @content_bp.post('/refresh/') @openapi.description('Search for videos that have previously been downloaded and stored.') @openapi.response(HTTPStatus.OK, StreamResponse) @openapi.response(HTTPStatus.BAD_REQUEST, JSONErrorResponse) @wrol_mode_check async def refresh(_, link: str = None): refresh_logger = logger.getChild('refresh') stream_url = get_sanic_url(scheme='ws', path='/api/videos/feeds/refresh') # Only one refresh can run at a time if refresh_event.is_set(): return response.json({'error': 'Refresh already running', 'stream_url': stream_url}, HTTPStatus.CONFLICT) refresh_event.set() async def do_refresh(): try: refresh_logger.info('refresh started') channel_links = [link] if link else None await refresh_videos(channel_links) refresh_logger.info('refresh complete') except Exception: raise finally: refresh_event.clear() coro = do_refresh() asyncio.ensure_future(coro) refresh_logger.debug('do_refresh scheduled') return response.json({'code': 'stream-started', 'stream_url': stream_url}) download_queue, download_event = create_websocket_feed('download', '/feeds/download', content_bp) @content_bp.post('/download') @content_bp.post('/download/') @openapi.description('Update channel catalogs, download any missing videos') @openapi.response(HTTPStatus.OK, StreamResponse) @openapi.response(HTTPStatus.BAD_REQUEST, JSONErrorResponse) @wrol_mode_check def download(_, link: str = None): download_logger = logger.getChild('download') stream_url = get_sanic_url(scheme='ws', path='/api/videos/feeds/download') # Only one download can run at a time if download_event.is_set(): return response.json({'error': 'download already running', 'stream_url': stream_url}, HTTPStatus.CONFLICT) if refresh_event.is_set(): return response.json({'error': 'Refresh is running. Cannot download.'}) channel_lib.download_channel(link) download_logger.debug('do_download scheduled') return response.json({'code': 'stream-started', 'stream_url': stream_url}) @wraps(_refresh_videos) async def refresh_videos(channel_links: list = None): return _refresh_videos(channel_links=channel_links) @content_bp.post('/favorite') @openapi.description('Toggle the favorite flag on a video') @validate(FavoriteRequest) @openapi.response(HTTPStatus.OK, FavoriteResponse) @openapi.response(HTTPStatus.BAD_REQUEST, JSONErrorResponse) async def favorite(_: Request, body: FavoriteRequest): _favorite = video_lib.set_video_favorite(body.video_id, body.favorite) ret = {'video_id': body.video_id, 'favorite': _favorite} return json_response(ret, HTTPStatus.OK) @content_bp.get('/statistics') @openapi.response(HTTPStatus.OK, VideosStatisticsResponse) async def statistics(_: Request): ret = await get_statistics() return json_response(ret, HTTPStatus.OK)