[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.
|
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:
|
def get_download_file_name(self) -> str:
|
||||||
"""
|
"""
|
||||||
Returns
|
Returns
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,8 @@ class SoundcloudTrack(SoundcloudVariables, Entry):
|
||||||
it does not belong to an album.
|
it does not belong to an album.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
entry_extractor = "soundcloud"
|
||||||
|
|
||||||
def is_premiere(self) -> bool:
|
def is_premiere(self) -> bool:
|
||||||
"""
|
"""
|
||||||
Returns whether the entry is a premier track. Check this by seeing if the track's url is
|
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 object to represent a Soundcloud album.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
entry_extractor = "soundcloud:set"
|
||||||
|
|
||||||
def __init__(self, entry_dict: Dict, working_directory: Optional[str] = None):
|
def __init__(self, entry_dict: Dict, working_directory: Optional[str] = None):
|
||||||
"""
|
"""
|
||||||
Initialize the entry using ytdl metadata
|
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 object to represent a Youtube video. Reserved for shared Youtube entry logic.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
entry_extractor = "youtube"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ext(self) -> str:
|
def ext(self) -> str:
|
||||||
"""
|
"""
|
||||||
|
|
@ -45,6 +47,8 @@ class YoutubePlaylistVideo(YoutubeVideo):
|
||||||
|
|
||||||
|
|
||||||
class YoutubeChannel(Entry):
|
class YoutubeChannel(Entry):
|
||||||
|
entry_extractor = "youtube:tab"
|
||||||
|
|
||||||
def _get_thumbnail_url(self, thumbnail_id: str) -> Optional[str]:
|
def _get_thumbnail_url(self, thumbnail_id: str) -> Optional[str]:
|
||||||
"""
|
"""
|
||||||
Downloads a specific thumbnail from a YTDL entry's thumbnail list
|
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.entries.entry import Entry
|
||||||
from ytdl_sub.plugins.plugin import Plugin
|
from ytdl_sub.plugins.plugin import Plugin
|
||||||
from ytdl_sub.plugins.plugin import PluginOptions
|
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 FileHandlerTransactionLog
|
||||||
from ytdl_sub.utils.file_handler import FileMetadata
|
from ytdl_sub.utils.file_handler import FileMetadata
|
||||||
from ytdl_sub.utils.thumbnail import convert_download_thumbnail
|
from ytdl_sub.utils.thumbnail import convert_download_thumbnail
|
||||||
|
|
@ -225,6 +226,33 @@ class Subscription:
|
||||||
|
|
||||||
self._enhanced_download_archive.save_download_mappings()
|
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]:
|
def _initialize_plugins(self) -> List[Plugin]:
|
||||||
"""
|
"""
|
||||||
Returns
|
Returns
|
||||||
|
|
@ -268,7 +296,7 @@ class Subscription:
|
||||||
)
|
)
|
||||||
|
|
||||||
plugins = self._initialize_plugins()
|
plugins = self._initialize_plugins()
|
||||||
with self._prepare_working_directory(), self._maintain_archive_file():
|
with self._subscription_download_context_managers():
|
||||||
downloader = self.downloader_class(
|
downloader = self.downloader_class(
|
||||||
download_options=self.downloader_options,
|
download_options=self.downloader_options,
|
||||||
enhanced_download_archive=self._enhanced_download_archive,
|
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