"""build queries for video extraction from channel subscriptions""" from download.src.yt_dlp_base import YtWrap from video.src.constants import VideoTypeEnum class VideoQueryBuilder: """Build queries for yt-dlp.""" def __init__(self, config: dict, channel_overwrites: dict | None = None): self.config = config self.channel_overwrites = channel_overwrites or {} def build_queries( self, video_type: VideoTypeEnum | list[VideoTypeEnum] | None, limit: bool = True, ) -> list[tuple[VideoTypeEnum, int | None]]: """Build queries for all or specific video type.""" query_methods = { VideoTypeEnum.VIDEOS: self.videos_query, VideoTypeEnum.STREAMS: self.streams_query, VideoTypeEnum.SHORTS: self.shorts_query, } if video_type and video_type != VideoTypeEnum.UNKNOWN: # build query for specific type/s if not isinstance(video_type, list): video_type = [video_type] queries = [] for video_type_item in video_type: query_method = query_methods.get(video_type_item) if not query_method: continue query = query_method(limit) if query[1] != 0: queries.append(query) return queries # Build and return queries for all video types queries = [] for build_query in query_methods.values(): query = build_query(limit) if query[1] != 0: queries.append(query) return queries def videos_query(self, limit: bool) -> tuple[VideoTypeEnum, int | None]: """Build query for videos.""" return self._build_generic_query( video_type=VideoTypeEnum.VIDEOS, overwrite_key="subscriptions_channel_size", config_key="channel_size", limit=limit, ) def streams_query(self, limit: bool) -> tuple[VideoTypeEnum, int | None]: """Build query for streams.""" return self._build_generic_query( video_type=VideoTypeEnum.STREAMS, overwrite_key="subscriptions_live_channel_size", config_key="live_channel_size", limit=limit, ) def shorts_query(self, limit: bool) -> tuple[VideoTypeEnum, int | None]: """Build query for shorts.""" return self._build_generic_query( video_type=VideoTypeEnum.SHORTS, overwrite_key="subscriptions_shorts_channel_size", config_key="shorts_channel_size", limit=limit, ) def _build_generic_query( self, video_type: VideoTypeEnum, overwrite_key: str, config_key: str, limit: bool, ) -> tuple[VideoTypeEnum, int | None]: """Generic query for video page scraping.""" app_config_size = self.config["subscriptions"].get(config_key) if not limit or app_config_size is None: # treat None as unlimited return (video_type, None) if ( overwrite_key in self.channel_overwrites and self.channel_overwrites[overwrite_key] is not None ): overwrite = self.channel_overwrites[overwrite_key] return (video_type, overwrite) if app_config_size: return (video_type, app_config_size) return (video_type, 0) def get_last_channel_videos( channel_id, config, limit=None, query_filter=None, channel_overwrites=None, ): """get a list of last videos from channel""" query_handler = VideoQueryBuilder(config, channel_overwrites) queries = query_handler.build_queries(query_filter) last_videos = [] for vid_type_enum, limit_amount in queries: obs = { "skip_download": True, "extract_flat": True, } vid_type = vid_type_enum.value if limit is not None: obs.update({"playlist_items": f":{limit_amount}:1"}) url = f"https://www.youtube.com/channel/{channel_id}/{vid_type}" channel_query, _ = YtWrap(obs, config).extract(url) if not channel_query: continue for entry in channel_query["entries"]: entry["vid_type"] = vid_type last_videos.append(entry) return last_videos