wrolpi/modules/videos/api.py

135 lines
4.2 KiB
Python

import asyncio
from functools import wraps
from http import HTTPStatus
from sanic import Blueprint, response
from sanic.request import Request
from wrolpi import before_startup
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 validate_doc, 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/<link:str>')
@validate_doc(
summary='Search for videos that have previously been downloaded and stored.',
produces=StreamResponse,
responses=[
(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/<link:str>')
@validate_doc(
summary='Update channel catalogs, download any missing videos',
produces=StreamResponse,
responses=[
(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')
@validate_doc(
summary='Toggle the favorite flag on a video',
consumes=FavoriteRequest,
produces=FavoriteResponse,
responses=[
(HTTPStatus.BAD_REQUEST, JSONErrorResponse)
]
)
async def favorite(_: Request, data: dict):
_favorite = video_lib.set_video_favorite(data['video_id'], data['favorite'])
ret = {'video_id': data['video_id'], 'favorite': _favorite}
return json_response(ret, HTTPStatus.OK)
@content_bp.get('/statistics')
@validate_doc(
summary='Retrieve video statistics',
produces=VideosStatisticsResponse,
)
async def statistics(_: Request):
ret = await get_statistics()
return json_response(ret, HTTPStatus.OK)
@before_startup
def startup_spread_channel_downloads():
channel_lib.spread_channel_downloads()