mirror of
https://git.vectorsigma.ru/public/tubearchivist.git
synced 2026-08-04 22:29:39 +00:00
196 lines
6.3 KiB
Python
196 lines
6.3 KiB
Python
"""
|
|
Functionality:
|
|
- handle channel subscriptions
|
|
- handle playlist subscriptions
|
|
"""
|
|
|
|
from appsettings.src.config import AppConfig
|
|
from channel.src.index import YoutubeChannel
|
|
from channel.src.remote_query import VideoQueryBuilder
|
|
from common.src.helper import get_channels, get_playlists
|
|
from common.src.urlparser import ParsedURLType, Parser
|
|
from download.src.queue import PendingList
|
|
from playlist.src.index import YoutubePlaylist
|
|
from video.src.constants import VideoTypeEnum
|
|
from video.src.index import YoutubeVideo
|
|
|
|
|
|
class ChannelSubscription:
|
|
"""scan subscribed channels to find missing videos to add to pending"""
|
|
|
|
def __init__(self, task=None):
|
|
self.config = AppConfig().config
|
|
self.task = task
|
|
|
|
def find_missing(self) -> int:
|
|
"""find missing videos from channel subscriptions"""
|
|
if self.task:
|
|
self.task.send_progress(["Looking up channels."])
|
|
|
|
all_channels = get_channels(
|
|
subscribed_only=True,
|
|
source=["channel_id", "channel_overwrites", "channel_tabs"],
|
|
)
|
|
if not all_channels:
|
|
return 0
|
|
|
|
all_channel_urls: list[ParsedURLType] = []
|
|
|
|
for channel in all_channels:
|
|
channel_tabs = channel["channel_tabs"]
|
|
if not channel_tabs:
|
|
continue
|
|
|
|
enum = [getattr(VideoTypeEnum, i.upper()) for i in channel_tabs]
|
|
queries = VideoQueryBuilder(
|
|
config=self.config,
|
|
channel_overwrites=channel.get("channel_overwrites", {}),
|
|
).build_queries(video_type=enum)
|
|
|
|
for query in queries:
|
|
all_channel_urls.append(
|
|
ParsedURLType(
|
|
type="channel",
|
|
url=channel["channel_id"],
|
|
vid_type=query[0],
|
|
limit=query[1],
|
|
)
|
|
)
|
|
|
|
if self.task:
|
|
self.task.send_progress([f"Scanning {len(all_channels)} channels"])
|
|
|
|
pending_handler = PendingList(
|
|
youtube_ids=all_channel_urls,
|
|
task=self.task,
|
|
auto_start=self.config["subscriptions"].get("auto_start", False),
|
|
flat=self.config["subscriptions"].get("extract_flat", False),
|
|
)
|
|
added = pending_handler.parse_url_list()
|
|
|
|
return added
|
|
|
|
|
|
class PlaylistSubscription:
|
|
"""scan subscribed playlists for videos to add to pending"""
|
|
|
|
def __init__(self, task=None):
|
|
self.config = AppConfig().config
|
|
self.task = task
|
|
|
|
def find_missing(self) -> int:
|
|
"""find missing"""
|
|
all_playlists = get_playlists(
|
|
subscribed_only=True, source=["playlist_id"]
|
|
)
|
|
if not all_playlists:
|
|
return 0
|
|
|
|
size_limit = self.config["subscriptions"]["playlist_size"]
|
|
all_playlist_urls: list[ParsedURLType] = []
|
|
for playlist in all_playlists:
|
|
all_playlist_urls.append(
|
|
ParsedURLType(
|
|
type="playlist",
|
|
url=playlist["playlist_id"],
|
|
vid_type=VideoTypeEnum.UNKNOWN,
|
|
limit=size_limit,
|
|
)
|
|
)
|
|
|
|
pending_handler = PendingList(
|
|
youtube_ids=all_playlist_urls,
|
|
task=self.task,
|
|
auto_start=self.config["subscriptions"].get("auto_start", False),
|
|
flat=self.config["subscriptions"].get("extract_flat", False),
|
|
)
|
|
added = pending_handler.parse_url_list()
|
|
|
|
return added
|
|
|
|
|
|
class SubscriptionScanner:
|
|
"""add missing videos to queue"""
|
|
|
|
def __init__(self, task=False):
|
|
self.task = task
|
|
self.missing_videos = False
|
|
self.auto_start = AppConfig().config["subscriptions"].get("auto_start")
|
|
|
|
def scan(self):
|
|
"""scan channels and playlists"""
|
|
if self.task:
|
|
self.task.send_progress(["Rescanning channels and playlists."])
|
|
|
|
added = 0
|
|
added += ChannelSubscription(task=self.task).find_missing()
|
|
if self.task and not self.task.is_stopped():
|
|
added += PlaylistSubscription(task=self.task).find_missing()
|
|
|
|
return added
|
|
|
|
|
|
class SubscriptionHandler:
|
|
"""subscribe to channels and playlists from url_str"""
|
|
|
|
def __init__(self, url_str, task=False):
|
|
self.url_str = url_str
|
|
self.task = task
|
|
self.to_subscribe = False
|
|
|
|
def subscribe(self, expected_type=False):
|
|
"""subscribe to url_str items"""
|
|
if self.task:
|
|
self.task.send_progress(["Processing form content."])
|
|
self.to_subscribe = Parser(self.url_str).parse()
|
|
|
|
total = len(self.to_subscribe)
|
|
for idx, item in enumerate(self.to_subscribe):
|
|
if self.task:
|
|
self._notify(idx, item, total)
|
|
|
|
self.subscribe_type(item, expected_type=expected_type)
|
|
|
|
def subscribe_type(self, item, expected_type):
|
|
"""process single item"""
|
|
if item["type"] == "playlist":
|
|
if expected_type and expected_type != "playlist":
|
|
raise TypeError(
|
|
f"expected {expected_type} url but got {item.get('type')}"
|
|
)
|
|
|
|
playlist = YoutubePlaylist(item["url"])
|
|
playlist.change_subscribe(new_subscribe_state=True)
|
|
return
|
|
|
|
if item["type"] == "video":
|
|
# extract channel id from video
|
|
video = YoutubeVideo(item["url"])
|
|
video.get_from_youtube()
|
|
video.process_youtube_meta()
|
|
channel_id = video.channel_id
|
|
elif item["type"] == "channel":
|
|
channel_id = item["url"]
|
|
else:
|
|
raise ValueError("failed to subscribe to: " + item["url"])
|
|
|
|
if expected_type and expected_type != "channel":
|
|
raise TypeError(
|
|
f"expected {expected_type} url but got {item.get('type')}"
|
|
)
|
|
|
|
self._subscribe(channel_id)
|
|
|
|
def _subscribe(self, channel_id):
|
|
"""subscribe to channel"""
|
|
YoutubeChannel(channel_id).change_subscribe(new_subscribe_state=True)
|
|
|
|
def _notify(self, idx, item, total):
|
|
"""send notification message to redis"""
|
|
subscribe_type = item["type"].title()
|
|
message_lines = [
|
|
f"Subscribe to {subscribe_type}",
|
|
f"Progress: {idx + 1}/{total}",
|
|
]
|
|
self.task.send_progress(message_lines, progress=(idx + 1) / total)
|