Always convert thumbnails to jpg (#51)
* always use jpg * fix unit test * update readme and configs
This commit is contained in:
parent
a6cf9c3a11
commit
8b336e6fb0
14 changed files with 80 additions and 113 deletions
|
|
@ -94,9 +94,6 @@ presets:
|
|||
thumbnail_name: "{episode_name}.jpg"
|
||||
maintain_download_archive: True
|
||||
|
||||
convert_thumbnail:
|
||||
to: "jpg"
|
||||
|
||||
nfo_tags:
|
||||
nfo_name: "{episode_name}.nfo"
|
||||
nfo_root: "episodedetails"
|
||||
|
|
|
|||
|
|
@ -14,8 +14,6 @@ presets:
|
|||
file_name: "[{album_year}] {album_sanitized}/{track_number_padded} - {title_sanitized}.{ext}"
|
||||
thumbnail_name: "[{album_year}] {album_sanitized}/folder.jpg"
|
||||
maintain_download_archive: True
|
||||
convert_thumbnail:
|
||||
to: "jpg"
|
||||
music_tags:
|
||||
tags:
|
||||
artist: "{artist}"
|
||||
|
|
@ -47,11 +45,6 @@ presets:
|
|||
file_name: "{music_video_name}.{ext}"
|
||||
thumbnail_name: "{music_video_name}.jpg"
|
||||
|
||||
# Always convert the video thumbnail to jpg
|
||||
# TODO: always convert all thumbnails to jpg in code
|
||||
convert_thumbnail:
|
||||
to: "jpg"
|
||||
|
||||
# For each video downloaded, add a music video NFO file for it. Populate it
|
||||
# with tags that Kodi will read and use to display it in the music or music
|
||||
# videos section.
|
||||
|
|
@ -101,8 +94,6 @@ presets:
|
|||
output_options:
|
||||
file_name: "{output_file_name}.{ext}"
|
||||
thumbnail_name: "{output_file_name}.jpg"
|
||||
convert_thumbnail:
|
||||
to: "jpg"
|
||||
nfo_tags:
|
||||
nfo_name: "{output_file_name}.nfo"
|
||||
nfo_root: "episodedetails"
|
||||
|
|
|
|||
|
|
@ -21,9 +21,6 @@ presets:
|
|||
file_name: "{music_video_name}.{ext}"
|
||||
thumbnail_name: "{music_video_name}.jpg"
|
||||
|
||||
convert_thumbnail:
|
||||
to: "jpg"
|
||||
|
||||
nfo_tags:
|
||||
nfo_name: "{music_video_name}.nfo"
|
||||
nfo_root: "musicvideo"
|
||||
|
|
|
|||
|
|
@ -37,11 +37,6 @@ presets:
|
|||
file_name: "{music_video_name}.{ext}"
|
||||
thumbnail_name: "{music_video_name}.jpg"
|
||||
|
||||
# Always convert the video thumbnail to jpg
|
||||
# TODO: always convert all thumbnails to jpg in code
|
||||
convert_thumbnail:
|
||||
to: "jpg"
|
||||
|
||||
# For each video downloaded, add a music video NFO file for it. Populate it
|
||||
# with tags that Kodi will read and use to display it in the music or music
|
||||
# videos section.
|
||||
|
|
|
|||
|
|
@ -55,11 +55,6 @@ presets:
|
|||
thumbnail_name: "{episode_name}.jpg"
|
||||
maintain_download_archive: True
|
||||
|
||||
# Always convert the video thumbnail to jpg
|
||||
# TODO: always convert all thumbnails to jpg in code
|
||||
convert_thumbnail:
|
||||
to: "jpg"
|
||||
|
||||
# For each video downloaded, add an episode NFO file for it. We give it
|
||||
# the same date-like episode name using our {episode_name} variable, and
|
||||
# populate the NFO with available video metadata that Kodi will recognize.
|
||||
|
|
|
|||
|
|
@ -48,11 +48,6 @@ presets:
|
|||
thumbnail_name: "{album_directory_name}/folder.jpg"
|
||||
maintain_download_archive: True
|
||||
|
||||
# Always convert the video thumbnail to jpg
|
||||
# TODO: always convert all thumbnails to jpg in code
|
||||
convert_thumbnail:
|
||||
to: "jpg"
|
||||
|
||||
# For each song downloaded, populate the audio file with music tags.
|
||||
# Tagging should work with most audio file formats. See
|
||||
# https://ytdl-sub.readthedocs.io/en/latest/config.html#music-tags
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ from ytdl_sub.downloaders.soundcloud_downloader import SoundcloudAlbumsAndSingle
|
|||
from ytdl_sub.downloaders.youtube_downloader import YoutubeChannelDownloader
|
||||
from ytdl_sub.downloaders.youtube_downloader import YoutubePlaylistDownloader
|
||||
from ytdl_sub.downloaders.youtube_downloader import YoutubeVideoDownloader
|
||||
from ytdl_sub.plugins.convert_thumbnail import ConvertThumbnailPlugin
|
||||
from ytdl_sub.plugins.music_tags import MusicTagsPlugin
|
||||
from ytdl_sub.plugins.nfo_tags import NfoTagsPlugin
|
||||
from ytdl_sub.plugins.output_directory_nfo_tags import OutputDirectoryNfoTagsPlugin
|
||||
|
|
@ -101,7 +100,6 @@ class PluginMapping:
|
|||
"""
|
||||
|
||||
_MAPPING: Dict[str, Type[Plugin]] = {
|
||||
"convert_thumbnail": ConvertThumbnailPlugin,
|
||||
"music_tags": MusicTagsPlugin,
|
||||
"nfo_tags": NfoTagsPlugin,
|
||||
"output_directory_nfo_tags": OutputDirectoryNfoTagsPlugin,
|
||||
|
|
|
|||
|
|
@ -5,10 +5,6 @@ from typing import Generic
|
|||
from typing import List
|
||||
from typing import Optional
|
||||
from typing import TypeVar
|
||||
from urllib.request import urlopen
|
||||
|
||||
from PIL.Image import Image
|
||||
from PIL.Image import open as pil_open
|
||||
|
||||
from ytdl_sub.config.preset_options import Overrides
|
||||
from ytdl_sub.downloaders.downloader import Downloader
|
||||
|
|
@ -18,6 +14,7 @@ from ytdl_sub.entries.youtube import YoutubeChannel
|
|||
from ytdl_sub.entries.youtube import YoutubePlaylistVideo
|
||||
from ytdl_sub.entries.youtube import YoutubeVideo
|
||||
from ytdl_sub.utils.logger import Logger
|
||||
from ytdl_sub.utils.thumbnail import convert_url_thumbnail
|
||||
from ytdl_sub.validators.date_range_validator import DateRangeValidator
|
||||
from ytdl_sub.validators.string_formatter_validators import OverridesStringFormatterValidator
|
||||
from ytdl_sub.validators.validators import StringValidator
|
||||
|
|
@ -312,10 +309,9 @@ class YoutubeChannelDownloader(YoutubeDownloader[YoutubeChannelDownloaderOptions
|
|||
logger.warning("Could not find a thumbnail for %s", self.channel.uid)
|
||||
return
|
||||
|
||||
with urlopen(thumbnail_url) as file:
|
||||
image: Image = pil_open(file).convert("RGB")
|
||||
|
||||
image.save(fp=output_thumbnail_path, format="jpeg")
|
||||
convert_url_thumbnail(
|
||||
thumbnail_url=thumbnail_url, output_thumbnail_path=output_thumbnail_path
|
||||
)
|
||||
|
||||
def post_download(self, overrides: Overrides, output_directory: str):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -82,10 +82,10 @@ class EntryVariables(SourceVariables):
|
|||
"""
|
||||
Returns
|
||||
-------
|
||||
The download entry's thumbnail extension
|
||||
The download entry's thumbnail extension. Will always return 'jpg'. Until there is a need
|
||||
to support other image types, we always convert to jpg.
|
||||
"""
|
||||
# TODO: Always return jpg
|
||||
return self.kwargs("thumbnail").split(".")[-1]
|
||||
return "jpg"
|
||||
|
||||
@property
|
||||
def upload_date(self: BaseEntry) -> str:
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
import os.path
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
from ytdl_sub.entries.entry import Entry
|
||||
|
|
@ -8,31 +6,9 @@ from ytdl_sub.entries.variables.youtube_variables import YoutubeVideoVariables
|
|||
|
||||
class YoutubeVideo(YoutubeVideoVariables, Entry):
|
||||
"""
|
||||
Entry object to represent a Youtube video.
|
||||
Entry object to represent a Youtube video. Reserved for shared Youtube entry logic.
|
||||
"""
|
||||
|
||||
def get_download_thumbnail_path(self) -> str:
|
||||
"""
|
||||
Returns
|
||||
-------
|
||||
The entry's thumbnail's file path to where it was downloaded. Ytdl has a bug where the
|
||||
thumbnail entry is not the actual thumbnail it downloaded. So check all the possible
|
||||
extensions that it may have downloaded, and see if that thumbnail's extension exists.
|
||||
|
||||
TODO: always make thumbnails jpg
|
||||
"""
|
||||
thumbnails = self.kwargs("thumbnails")
|
||||
possible_thumbnail_exts = set()
|
||||
for thumbnail in thumbnails:
|
||||
possible_thumbnail_exts.add(thumbnail["url"].split(".")[-1])
|
||||
|
||||
for ext in possible_thumbnail_exts:
|
||||
possible_thumbnail_path = str(Path(self.working_directory()) / f"{self.uid}.{ext}")
|
||||
if os.path.isfile(possible_thumbnail_path):
|
||||
return possible_thumbnail_path
|
||||
|
||||
return super().get_download_thumbnail_path()
|
||||
|
||||
|
||||
class YoutubePlaylistVideo(YoutubeVideo):
|
||||
@property
|
||||
|
|
|
|||
|
|
@ -1,44 +0,0 @@
|
|||
import os
|
||||
|
||||
from PIL.Image import Image
|
||||
from PIL.Image import open as pil_open
|
||||
|
||||
from ytdl_sub.entries.entry import Entry
|
||||
from ytdl_sub.plugins.plugin import Plugin
|
||||
from ytdl_sub.plugins.plugin import PluginOptions
|
||||
from ytdl_sub.validators.string_select_validator import StringSelectValidator
|
||||
|
||||
|
||||
class ThumbnailTypes(StringSelectValidator):
|
||||
"""Valid image types that thumbnails can be converted to"""
|
||||
|
||||
_select_values = {"jpg"}
|
||||
|
||||
|
||||
class ConvertThumbnailOptions(PluginOptions):
|
||||
_required_keys = {"to"}
|
||||
|
||||
def __init__(self, name, value):
|
||||
super().__init__(name, value)
|
||||
self.convert_to = self._validate_key(key="to", validator=ThumbnailTypes)
|
||||
|
||||
|
||||
class ConvertThumbnailPlugin(Plugin[ConvertThumbnailOptions]):
|
||||
plugin_options_type = ConvertThumbnailOptions
|
||||
|
||||
def post_process_entry(self, entry: Entry):
|
||||
"""
|
||||
Convert the source thumbnail to the desired type. Leave the original file name and
|
||||
extension intact to let output_options deal with renaming it.
|
||||
"""
|
||||
if not os.path.isfile(entry.get_download_thumbnail_path()):
|
||||
raise ValueError("Thumbnail not found")
|
||||
|
||||
image: Image = pil_open(entry.get_download_thumbnail_path()).convert("RGB")
|
||||
|
||||
# Pillow likes the formal 'jpeg' name and not 'jpg'
|
||||
thumbnail_format = self.plugin_options.convert_to.value
|
||||
if thumbnail_format == "jpg":
|
||||
thumbnail_format = "jpeg"
|
||||
|
||||
image.save(fp=entry.get_download_thumbnail_path(), format=thumbnail_format)
|
||||
|
|
@ -18,6 +18,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.utils.thumbnail import convert_download_thumbnail
|
||||
from ytdl_sub.ytdl_additions.enhanced_download_archive import EnhancedDownloadArchive
|
||||
|
||||
|
||||
|
|
@ -178,6 +179,10 @@ class Subscription:
|
|||
output_thumbnail_name = self.overrides.apply_formatter(
|
||||
formatter=self.output_options.thumbnail_name, entry=entry
|
||||
)
|
||||
|
||||
# We always convert entry thumbnails to jpgs, and is performed here
|
||||
convert_download_thumbnail(entry=entry)
|
||||
|
||||
self._copy_file_to_output_directory(
|
||||
entry=entry,
|
||||
source_file_path=entry.get_download_thumbnail_path(),
|
||||
|
|
|
|||
66
src/ytdl_sub/utils/thumbnail.py
Normal file
66
src/ytdl_sub/utils/thumbnail.py
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
import os
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
from urllib.request import urlopen
|
||||
|
||||
from PIL.Image import Image
|
||||
from PIL.Image import open as pil_open
|
||||
|
||||
from ytdl_sub.entries.entry import Entry
|
||||
|
||||
|
||||
def _get_downloaded_thumbnail_path(entry: Entry) -> Optional[str]:
|
||||
thumbnails = entry.kwargs("thumbnails")
|
||||
possible_thumbnail_exts = set()
|
||||
|
||||
# The source `thumbnail` value and the actual downloaded thumbnail extension sometimes do
|
||||
# not match. Find all possible extensions by checking all available thumbnails.
|
||||
for thumbnail in thumbnails:
|
||||
possible_thumbnail_exts.add(thumbnail["url"].split(".")[-1])
|
||||
|
||||
for ext in possible_thumbnail_exts:
|
||||
possible_thumbnail_path = str(Path(entry.working_directory()) / f"{entry.uid}.{ext}")
|
||||
if os.path.isfile(possible_thumbnail_path):
|
||||
return possible_thumbnail_path
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def convert_download_thumbnail(entry: Entry):
|
||||
"""
|
||||
Converts an entry's downloaded thumbnail into jpg format
|
||||
|
||||
Parameters
|
||||
----------
|
||||
entry
|
||||
Entry with the thumbnail
|
||||
|
||||
Raises
|
||||
------
|
||||
ValueError
|
||||
Entry thumbnail file not found
|
||||
"""
|
||||
download_thumbnail_path = _get_downloaded_thumbnail_path(entry=entry)
|
||||
download_thumbnail_path_as_jpg = entry.get_download_thumbnail_path()
|
||||
if not download_thumbnail_path:
|
||||
raise ValueError("Thumbnail not found")
|
||||
|
||||
image: Image = pil_open(download_thumbnail_path).convert("RGB")
|
||||
image.save(fp=download_thumbnail_path_as_jpg, format="jpeg")
|
||||
|
||||
|
||||
def convert_url_thumbnail(thumbnail_url: str, output_thumbnail_path: str):
|
||||
"""
|
||||
Downloads and converts a thumbnail from a url into a jpg
|
||||
|
||||
Parameters
|
||||
----------
|
||||
thumbnail_url
|
||||
URL of the thumbnail
|
||||
output_thumbnail_path
|
||||
Thumbnail file destination after its converted to jpg
|
||||
"""
|
||||
with urlopen(thumbnail_url) as file:
|
||||
image: Image = pil_open(file).convert("RGB")
|
||||
|
||||
image.save(fp=output_thumbnail_path, format="jpeg")
|
||||
|
|
@ -57,7 +57,7 @@ def ext():
|
|||
|
||||
@pytest.fixture
|
||||
def thumbnail_ext():
|
||||
return "yall"
|
||||
return "jpg"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
|
|
|||
Loading…
Reference in a new issue