mirror of
https://github.com/lrnselfreliance/wrolpi.git
synced 2026-10-26 19:01:39 -04:00
166 lines
5.7 KiB
Python
166 lines
5.7 KiB
Python
from datetime import datetime
|
|
from typing import Tuple, Optional, List
|
|
|
|
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, optional_session
|
|
from wrolpi.errors import UnknownVideo
|
|
from wrolpi.files.lib import handle_search_results
|
|
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()
|
|
|
|
caption = video.video_file.d_text
|
|
video = video.video_file.__json__()
|
|
video['video']['caption'] = caption
|
|
previous_video = previous_video.video_file.__json__() if previous_video and previous_video.video_file else None
|
|
next_video = next_video.video_file.__json__() if next_video and next_video.video_file else None
|
|
|
|
return video, previous_video, next_video
|
|
|
|
|
|
VIDEO_ORDERS = {
|
|
'upload_date': 'v.upload_date ASC, LOWER(v.video_path) ASC',
|
|
'-upload_date': 'v.upload_date DESC NULLS LAST, LOWER(v.video_path) DESC',
|
|
'rank': '2 DESC, LOWER(v.video_path) DESC',
|
|
'-rank': '2 ASC, LOWER(v.video_path) ASC',
|
|
'id': 'v.id ASC',
|
|
'-id': 'v.id DESC',
|
|
'size': 'f.size ASC, LOWER(v.video_path) ASC',
|
|
'-size': 'f.size DESC, LOWER(v.video_path) DESC',
|
|
'duration': 'duration ASC, LOWER(v.video_path) ASC',
|
|
'-duration': 'duration DESC, LOWER(v.video_path) DESC',
|
|
'favorite': 'favorite ASC, LOWER(v.video_path) ASC',
|
|
'-favorite': 'favorite DESC, LOWER(v.video_path) DESC',
|
|
'viewed': 'v.viewed ASC',
|
|
'-viewed': 'v.viewed DESC',
|
|
'view_count': 'v.view_count ASC',
|
|
'-view_count': 'v.view_count DESC',
|
|
'modification_datetime': 'f.modification_datetime ASC',
|
|
'-modification_datetime': 'f.modification_datetime DESC',
|
|
}
|
|
NO_NULL_ORDERS = {
|
|
'viewed': 'v.viewed IS NOT NULL',
|
|
'-viewed': 'v.viewed IS NOT NULL',
|
|
'duration': 'v.duration IS NOT NULL',
|
|
'-duration': 'v.duration IS NOT NULL',
|
|
'size': 'f.size IS NOT NULL',
|
|
'-size': 'f.size IS NOT NULL',
|
|
'view_count': 'v.view_count IS NOT NULL',
|
|
'-view_count': 'v.view_count IS NOT NULL',
|
|
'modification_datetime': 'f.modification_datetime IS NOT NULL',
|
|
'-modification_datetime': 'f.modification_datetime IS NOT NULL',
|
|
}
|
|
DEFAULT_VIDEO_ORDER = 'rank'
|
|
VIDEO_QUERY_LIMIT = 24
|
|
|
|
|
|
def search_videos(
|
|
search_str: str = None,
|
|
offset: int = None,
|
|
limit: int = VIDEO_QUERY_LIMIT,
|
|
channel_id: int = None,
|
|
order_by: str = None,
|
|
filters: List[str] = None,
|
|
) -> Tuple[List[dict], int]:
|
|
filters = filters or []
|
|
wheres = ['v.video_path IS NOT NULL']
|
|
|
|
params = dict(search_str=search_str, offset=offset)
|
|
if channel_id:
|
|
wheres.append('v.channel_id = %(channel_id)s')
|
|
params['channel_id'] = channel_id
|
|
|
|
# Apply filters.
|
|
if 'favorite' in filters:
|
|
wheres.append('v.favorite IS NOT NULL')
|
|
if 'censored' in filters:
|
|
wheres.append('v.censored = true')
|
|
|
|
if search_str:
|
|
# A search_str was provided by the user, modify the query to filter by it.
|
|
select_columns = 'f.path, ts_rank(f.textsearch, websearch_to_tsquery(%(search_str)s)), ' \
|
|
'COUNT(*) OVER() AS total'
|
|
wheres.append('f.textsearch @@ websearch_to_tsquery(%(search_str)s)')
|
|
params['search_str'] = search_str
|
|
join = 'LEFT JOIN file f on f.path = v.video_path'
|
|
else:
|
|
# No search_str provided. Get path and total only.
|
|
select_columns = 'v.video_path AS path, COUNT(*) OVER() AS total'
|
|
join = ''
|
|
|
|
if order_by in {'size', '-size', 'modification_datetime', '-modification_datetime'}:
|
|
# Size and modification_datetime are from the video file.
|
|
join = 'LEFT JOIN file f on f.path = v.video_path'
|
|
|
|
# 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:
|
|
wheres.append(NO_NULL_ORDERS[order_by])
|
|
|
|
wheres = '\n AND '.join(wheres)
|
|
where = f'WHERE\n{wheres}' if wheres else ''
|
|
stmt = f'''
|
|
SELECT
|
|
{select_columns}
|
|
FROM video v
|
|
{join}
|
|
{where}
|
|
ORDER BY {order}
|
|
OFFSET %(offset)s LIMIT {int(limit)}
|
|
'''.strip()
|
|
logger.debug(stmt, params)
|
|
|
|
results, total = handle_search_results(stmt, params)
|
|
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
|
|
|
|
|
|
@optional_session
|
|
def delete_videos(*video_ids: int, session: Session = None):
|
|
videos = list(session.query(Video).filter(Video.id.in_(video_ids)))
|
|
if not videos:
|
|
raise UnknownVideo('Could not find videos to delete')
|
|
|
|
logger.warning(f'Deleting {len(videos)} videos')
|
|
for video in videos:
|
|
video.delete()
|
|
session.commit()
|