Always convert thumbnails to jpg (#51)

* always use jpg

* fix unit test

* update readme and configs
This commit is contained in:
Jesse Bannon 2022-05-30 09:59:25 -07:00 committed by GitHub
parent a6cf9c3a11
commit 8b336e6fb0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 80 additions and 113 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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):
"""

View file

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

View file

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

View file

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

View file

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

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

View file

@ -57,7 +57,7 @@ def ext():
@pytest.fixture
def thumbnail_ext():
return "yall"
return "jpg"
@pytest.fixture