[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:
Jesse Bannon 2022-07-21 23:00:17 -07:00 committed by GitHub
parent 99cbcb8243
commit ce25f4d28c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 108 additions and 1 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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,

View file

View 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)