import json import pathlib from typing import Optional, Dict, List from sqlalchemy import Column, Integer, String, Boolean, JSON, Date, ForeignKey, BigInteger from sqlalchemy.orm import relationship, Session, deferred from sqlalchemy.orm.collections import InstrumentedList from modules.videos.errors import UnknownVideo, UnknownChannel from wrolpi import flags from wrolpi.captions import read_captions from wrolpi.common import Base, ModelHelper, logger, get_media_directory, background_task, replace_file, \ unique_by_predicate from wrolpi.db import get_db_curs, get_db_session, optional_session from wrolpi.downloader import Download, download_manager, save_downloads_config from wrolpi.errors import InvalidDownload from wrolpi.events import Events from wrolpi.files.lib import refresh_files, split_path_stem_and_suffix, move from wrolpi.files.models import FileGroup from wrolpi.media_path import MediaPathType from wrolpi.tags import Tag, TagFile, save_tags_config from wrolpi.vars import PYTEST, VIDEO_INFO_JSON_KEYS_TO_CLEAN logger = logger.getChild(__name__) __all__ = ['Video', 'Channel'] class Video(ModelHelper, Base): __tablename__ = 'video' id = Column(Integer, primary_key=True) source_id = Column(String) # The id from yt-dlp view_count = Column(Integer) # The view count from the ChannelDownloader (or from initial download) ffprobe_json = deferred(Column(JSON)) # Data that is fetched once from ffprobe (ffmpeg) have_comments = Column(Boolean, default=False) # see `get_missing_videos_comments` comments_failed = Column(Boolean, default=False) # see `get_missing_videos_comments` channel_id = Column(Integer, ForeignKey('channel.id')) channel = relationship('Channel', primaryjoin='Video.channel_id==Channel.id', back_populates='videos') file_group_id = Column(BigInteger, ForeignKey('file_group.id', ondelete='CASCADE'), unique=True, nullable=False) file_group: FileGroup = relationship('FileGroup') def __repr__(self): v = None if self.video_path: v = repr(str(self.video_path.relative_to(get_media_directory()))) return f'