[BACKEND] info log download titles as they appear (#115)
* [BACKEND] info log download titles as they appear * complete if exception
This commit is contained in:
parent
99cbcb8243
commit
ce25f4d28c
6 changed files with 108 additions and 1 deletions
|
|
@ -11,6 +11,9 @@ class Entry(EntryVariables, BaseEntry):
|
|||
Entry object to represent a single media object returned from yt-dlp.
|
||||
"""
|
||||
|
||||
# The ytdl extractor type that the entry represents
|
||||
entry_extractor: str
|
||||
|
||||
def get_download_file_name(self) -> str:
|
||||
"""
|
||||
Returns
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ class SoundcloudTrack(SoundcloudVariables, Entry):
|
|||
it does not belong to an album.
|
||||
"""
|
||||
|
||||
entry_extractor = "soundcloud"
|
||||
|
||||
def is_premiere(self) -> bool:
|
||||
"""
|
||||
Returns whether the entry is a premier track. Check this by seeing if the track's url is
|
||||
|
|
@ -87,6 +89,8 @@ class SoundcloudAlbum(Entry):
|
|||
Entry object to represent a Soundcloud album.
|
||||
"""
|
||||
|
||||
entry_extractor = "soundcloud:set"
|
||||
|
||||
def __init__(self, entry_dict: Dict, working_directory: Optional[str] = None):
|
||||
"""
|
||||
Initialize the entry using ytdl metadata
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ class YoutubeVideo(YoutubeVideoVariables, Entry):
|
|||
Entry object to represent a Youtube video. Reserved for shared Youtube entry logic.
|
||||
"""
|
||||
|
||||
entry_extractor = "youtube"
|
||||
|
||||
@property
|
||||
def ext(self) -> str:
|
||||
"""
|
||||
|
|
@ -45,6 +47,8 @@ class YoutubePlaylistVideo(YoutubeVideo):
|
|||
|
||||
|
||||
class YoutubeChannel(Entry):
|
||||
entry_extractor = "youtube:tab"
|
||||
|
||||
def _get_thumbnail_url(self, thumbnail_id: str) -> Optional[str]:
|
||||
"""
|
||||
Downloads a specific thumbnail from a YTDL entry's thumbnail list
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ from ytdl_sub.downloaders.downloader import DownloaderValidator
|
|||
from ytdl_sub.entries.entry import Entry
|
||||
from ytdl_sub.plugins.plugin import Plugin
|
||||
from ytdl_sub.plugins.plugin import PluginOptions
|
||||
from ytdl_sub.thread.log_entries_downloaded_listener import LogEntriesDownloadedListener
|
||||
from ytdl_sub.utils.file_handler import FileHandlerTransactionLog
|
||||
from ytdl_sub.utils.file_handler import FileMetadata
|
||||
from ytdl_sub.utils.thumbnail import convert_download_thumbnail
|
||||
|
|
@ -225,6 +226,33 @@ class Subscription:
|
|||
|
||||
self._enhanced_download_archive.save_download_mappings()
|
||||
|
||||
@contextlib.contextmanager
|
||||
def _listen_and_log_downloaded_entries(self):
|
||||
"""
|
||||
Context manager that starts a separate thread that listens for new .info.json files,
|
||||
prints their titles as they appear
|
||||
"""
|
||||
info_json_listener = LogEntriesDownloadedListener(
|
||||
working_directory=self.working_directory,
|
||||
info_json_extractor=self.downloader_class.downloader_entry_type.entry_extractor,
|
||||
)
|
||||
|
||||
info_json_listener.start()
|
||||
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
info_json_listener.complete = True
|
||||
|
||||
@contextlib.contextmanager
|
||||
def _subscription_download_context_managers(self) -> None:
|
||||
with (
|
||||
self._prepare_working_directory(),
|
||||
self._maintain_archive_file(),
|
||||
self._listen_and_log_downloaded_entries(),
|
||||
):
|
||||
yield
|
||||
|
||||
def _initialize_plugins(self) -> List[Plugin]:
|
||||
"""
|
||||
Returns
|
||||
|
|
@ -268,7 +296,7 @@ class Subscription:
|
|||
)
|
||||
|
||||
plugins = self._initialize_plugins()
|
||||
with self._prepare_working_directory(), self._maintain_archive_file():
|
||||
with self._subscription_download_context_managers():
|
||||
downloader = self.downloader_class(
|
||||
download_options=self.downloader_options,
|
||||
enhanced_download_archive=self._enhanced_download_archive,
|
||||
|
|
|
|||
0
src/ytdl_sub/thread/__init__.py
Normal file
0
src/ytdl_sub/thread/__init__.py
Normal file
68
src/ytdl_sub/thread/log_entries_downloaded_listener.py
Normal file
68
src/ytdl_sub/thread/log_entries_downloaded_listener.py
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
import json
|
||||
import os.path
|
||||
import threading
|
||||
import time
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
from typing import Set
|
||||
|
||||
from ytdl_sub.utils.logger import Logger
|
||||
|
||||
logger = Logger.get(name="downloader")
|
||||
|
||||
|
||||
class LogEntriesDownloadedListener(threading.Thread):
|
||||
def __init__(self, working_directory, info_json_extractor):
|
||||
"""
|
||||
To be ran in a thread while download via ytdl-sub. Listens for new .info.json files in the
|
||||
working directory, checks the extractor value, and if it matches the input arg, log the
|
||||
title.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
working_directory
|
||||
subscription download working directory
|
||||
info_json_extractor
|
||||
print the titles of the info.json file with this extractor
|
||||
"""
|
||||
threading.Thread.__init__(self)
|
||||
self.working_directory = working_directory
|
||||
self.info_json_extractor = info_json_extractor
|
||||
self.complete = False
|
||||
|
||||
self._files_read: Set[str] = set()
|
||||
|
||||
def _get_title_from_info_json(self, path: Path) -> Optional[str]:
|
||||
with open(path, "r", encoding="utf-8") as file:
|
||||
file_json = json.load(file)
|
||||
|
||||
if file_json.get("extractor") == self.info_json_extractor:
|
||||
return file_json.get("title")
|
||||
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def _is_info_json(cls, path: Path) -> bool:
|
||||
if path.is_file():
|
||||
_, ext = os.path.splitext(path)
|
||||
return ext == ".json"
|
||||
return False
|
||||
|
||||
def loop(self) -> None:
|
||||
"""
|
||||
Read new files in the directory and print their titles
|
||||
"""
|
||||
for path in Path(self.working_directory).rglob("*"):
|
||||
if path.name not in self._files_read and self._is_info_json(path):
|
||||
title = self._get_title_from_info_json(path)
|
||||
self._files_read.add(path.name)
|
||||
if title:
|
||||
logger.info("Downloading %s", title)
|
||||
|
||||
def run(self):
|
||||
"""
|
||||
Loops over new files and prints their titles
|
||||
"""
|
||||
while not self.complete:
|
||||
self.loop()
|
||||
time.sleep(0.1)
|
||||
Loading…
Reference in a new issue