mirror of
https://github.com/lrnselfreliance/wrolpi.git
synced 2026-10-26 19:01:39 -04:00
156 lines
5.3 KiB
Python
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
|