wrolpi/modules/videos/video/lib.py
Roland 9b899f6fc0
A new start
Signed-off-by: Roland <roland@learningselfreliance.com>
2021-11-01 16:43:09 -06:00

156 lines
5.3 KiB
Python

from datetime import datetime
from typing import Tuple, Optional, List
import psycopg2
from sqlalchemy.orm import Session
from sqlalchemy.orm.exc import NoResultFound
from wrolpi.common import run_after, logger
from wrolpi.db import get_db_session, get_db_curs
from wrolpi.errors import UnknownVideo
from ..lib import save_channels_config
from ..models import Video
logger.getChild(__name__)
def get_video(session: Session, video_id: int) -> Video:
try:
video = session.query(Video).filter_by(id=video_id).one()
return video
except NoResultFound:
raise UnknownVideo()
def get_video_for_app(video_id: int) -> Tuple[dict, Optional[dict], Optional[dict]]:
"""
Get a Video, with it's prev/next videos. Mark the Video as viewed.
"""
with get_db_session(commit=True) as session:
video = get_video(session, video_id)
video.set_viewed()
previous_video, next_video = video.get_surrounding_videos()
video = video.get_minimize()
previous_video = previous_video.get_minimize() if previous_video else None
next_video = next_video.get_minimize() if next_video else None
return video, previous_video, next_video
VIDEO_ORDERS = {
'upload_date': 'upload_date ASC, LOWER(video_path) ASC',
'-upload_date': 'upload_date DESC NULLS LAST, LOWER(video_path) DESC',
'rank': '2 DESC, LOWER(video_path) DESC',
'-rank': '2 ASC, LOWER(video_path) ASC',
'id': 'id ASC',
'-id': 'id DESC',
'size': 'size ASC, LOWER(video_path) ASC',
'-size': 'size DESC, LOWER(video_path) DESC',
'duration': 'duration ASC, LOWER(video_path) ASC',
'-duration': 'duration DESC, LOWER(video_path) DESC',
'favorite': 'favorite ASC, LOWER(video_path) ASC',
'-favorite': 'favorite DESC, LOWER(video_path) DESC',
'viewed': 'viewed ASC',
'-viewed': 'viewed DESC',
'view_count': 'view_count ASC',
'-view_count': 'view_count DESC',
}
NO_NULL_ORDERS = {
'viewed': '\nAND viewed IS NOT NULL',
'-viewed': '\nAND viewed IS NOT NULL',
'duration': '\nAND duration IS NOT NULL',
'-duration': '\nAND duration IS NOT NULL',
'size': '\nAND size IS NOT NULL',
'-size': '\nAND size IS NOT NULL',
'view_count': '\nAND view_count IS NOT NULL',
'-view_count': '\nAND view_count IS NOT NULL',
}
DEFAULT_VIDEO_ORDER = 'rank'
VIDEO_QUERY_LIMIT = 20
def video_search(
search_str: str = None,
offset: int = None,
limit: int = VIDEO_QUERY_LIMIT,
channel_link: str = None,
order_by: str = None,
favorites: bool = None,
) -> Tuple[List[dict], int]:
with get_db_curs() as curs:
args = dict(search_str=search_str, offset=offset)
channel_where = ''
if channel_link:
channel_where = 'AND channel_id = (select id from channel where link=%(channel_link)s)'
args['channel_link'] = channel_link
# Filter for/against favorites, if it was provided
favorites_where = ''
if favorites is not None:
favorites_where = f'AND favorite IS {"NOT" if favorites else ""} NULL'
where = ''
if search_str:
# A search_str was provided by the user, modify the query to filter by it.
columns = 'id, ts_rank_cd(textsearch, websearch_to_tsquery(%(search_str)s)), COUNT(*) OVER() AS total'
where = 'AND textsearch @@ websearch_to_tsquery(%(search_str)s)'
args['search_str'] = search_str
else:
# No search_str provided. Get id and total only.
columns = 'id, COUNT(*) OVER() AS total'
# Convert the user-friendly order by into a real order by, restrict what can be interpolated by using the whitelist.
order = VIDEO_ORDERS[DEFAULT_VIDEO_ORDER]
if order_by:
try:
order = VIDEO_ORDERS[order_by]
except KeyError:
raise
if order_by in NO_NULL_ORDERS:
where += NO_NULL_ORDERS[order_by]
query = f'''
SELECT
{columns}
FROM video
WHERE
video_path IS NOT NULL
{where}
{channel_where}
{favorites_where}
ORDER BY {order}
OFFSET %(offset)s LIMIT {int(limit)}
'''.strip()
logger.debug(query)
curs.execute(query, args)
try:
results = [dict(i) for i in curs.fetchall()]
except psycopg2.ProgrammingError:
# No videos
return [], 0
total = results[0]['total'] if results else 0
ranked_ids = [i['id'] for i in results]
with get_db_session() as session:
results = []
if ranked_ids:
results = session.query(Video).filter(Video.id.in_(ranked_ids)).all()
results = sorted(results, key=lambda r: ranked_ids.index(r.id))
results = [i.get_minimize() for i in results]
return results, total
@run_after(save_channels_config)
def set_video_favorite(video_id: int, favorite: bool) -> Optional[datetime]:
"""
Set the Video.favorite to the current datetime if `favorite` is True, otherwise None.
"""
with get_db_session(commit=True) as session:
video = session.query(Video).filter_by(id=video_id).one()
favorite = video.set_favorite(favorite)
return favorite