variables as mixins for docs

This commit is contained in:
jbannon 2022-04-24 00:30:53 +00:00
parent 53b1923653
commit 5eeb117fca
22 changed files with 547 additions and 288 deletions

View file

@ -57,3 +57,5 @@ html_static_path = ["_static"]
# Do not show full module path in api docs # Do not show full module path in api docs
add_module_names = False add_module_names = False
python_use_unqualified_type_names = True python_use_unqualified_type_names = True
napoleon_numpy_docstrings = True
napoleon_use_rtype = False

View file

@ -0,0 +1,53 @@
ytdl\_subscribe.config package
==============================
Submodules
----------
ytdl\_subscribe.config.config\_file module
------------------------------------------
.. automodule:: ytdl_subscribe.config.config_file
:members:
:undoc-members:
:show-inheritance:
ytdl\_subscribe.config.preset module
------------------------------------
.. automodule:: ytdl_subscribe.config.preset
:members:
:undoc-members:
:show-inheritance:
ytdl\_subscribe.config.preset\_class\_mappings module
-----------------------------------------------------
.. automodule:: ytdl_subscribe.config.preset_class_mappings
:members:
:undoc-members:
:show-inheritance:
ytdl\_subscribe.config.preset\_options module
---------------------------------------------
.. automodule:: ytdl_subscribe.config.preset_options
:members:
:undoc-members:
:show-inheritance:
ytdl\_subscribe.config.subscription module
------------------------------------------
.. automodule:: ytdl_subscribe.config.subscription
:members:
:undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: ytdl_subscribe.config
:members:
:undoc-members:
:show-inheritance:

View file

@ -1,9 +1,25 @@
ytdl\_subscribe.entries package ytdl\_subscribe.entries package
=============================== ===============================
Subpackages
-----------
.. toctree::
:maxdepth: 4
ytdl_subscribe.entries.variables
Submodules Submodules
---------- ----------
ytdl\_subscribe.entries.base\_entry module
------------------------------------------
.. automodule:: ytdl_subscribe.entries.base_entry
:members:
:undoc-members:
:show-inheritance:
ytdl\_subscribe.entries.entry module ytdl\_subscribe.entries.entry module
------------------------------------ ------------------------------------

View file

@ -0,0 +1,37 @@
ytdl\_subscribe.entries.variables package
=========================================
Submodules
----------
ytdl\_subscribe.entries.variables.entry\_variables module
---------------------------------------------------------
.. automodule:: ytdl_subscribe.entries.variables.entry_variables
:members:
:undoc-members:
:show-inheritance:
ytdl\_subscribe.entries.variables.soundcloud\_variables module
--------------------------------------------------------------
.. automodule:: ytdl_subscribe.entries.variables.soundcloud_variables
:members:
:undoc-members:
:show-inheritance:
ytdl\_subscribe.entries.variables.youtube\_variables module
-----------------------------------------------------------
.. automodule:: ytdl_subscribe.entries.variables.youtube_variables
:members:
:undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: ytdl_subscribe.entries.variables
:members:
:undoc-members:
:show-inheritance:

View file

@ -0,0 +1,53 @@
ytdl\_subscribe.plugins package
===============================
Submodules
----------
ytdl\_subscribe.plugins.convert\_thumbnail module
-------------------------------------------------
.. automodule:: ytdl_subscribe.plugins.convert_thumbnail
:members:
:undoc-members:
:show-inheritance:
ytdl\_subscribe.plugins.music\_tags module
------------------------------------------
.. automodule:: ytdl_subscribe.plugins.music_tags
:members:
:undoc-members:
:show-inheritance:
ytdl\_subscribe.plugins.nfo\_tags module
----------------------------------------
.. automodule:: ytdl_subscribe.plugins.nfo_tags
:members:
:undoc-members:
:show-inheritance:
ytdl\_subscribe.plugins.output\_directory\_nfo\_tags module
-----------------------------------------------------------
.. automodule:: ytdl_subscribe.plugins.output_directory_nfo_tags
:members:
:undoc-members:
:show-inheritance:
ytdl\_subscribe.plugins.plugin module
-------------------------------------
.. automodule:: ytdl_subscribe.plugins.plugin
:members:
:undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: ytdl_subscribe.plugins
:members:
:undoc-members:
:show-inheritance:

View file

@ -8,9 +8,12 @@ Subpackages
:maxdepth: 4 :maxdepth: 4
ytdl_subscribe.cli ytdl_subscribe.cli
ytdl_subscribe.config
ytdl_subscribe.downloaders ytdl_subscribe.downloaders
ytdl_subscribe.entries ytdl_subscribe.entries
ytdl_subscribe.plugins
ytdl_subscribe.subscriptions ytdl_subscribe.subscriptions
ytdl_subscribe.utils
ytdl_subscribe.validators ytdl_subscribe.validators
ytdl_subscribe.ytdl_additions ytdl_subscribe.ytdl_additions

View file

@ -4,14 +4,6 @@ ytdl\_subscribe.subscriptions package
Submodules Submodules
---------- ----------
ytdl\_subscribe.subscriptions.soundcloud module
-----------------------------------------------
.. automodule:: ytdl_subscribe.subscriptions.soundcloud
:members:
:undoc-members:
:show-inheritance:
ytdl\_subscribe.subscriptions.subscription module ytdl\_subscribe.subscriptions.subscription module
------------------------------------------------- -------------------------------------------------
@ -20,14 +12,6 @@ ytdl\_subscribe.subscriptions.subscription module
:undoc-members: :undoc-members:
:show-inheritance: :show-inheritance:
ytdl\_subscribe.subscriptions.youtube module
--------------------------------------------
.. automodule:: ytdl_subscribe.subscriptions.youtube
:members:
:undoc-members:
:show-inheritance:
Module contents Module contents
--------------- ---------------

View file

@ -0,0 +1,21 @@
ytdl\_subscribe.utils package
=============================
Submodules
----------
ytdl\_subscribe.utils.exceptions module
---------------------------------------
.. automodule:: ytdl_subscribe.utils.exceptions
:members:
:undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: ytdl_subscribe.utils
:members:
:undoc-members:
:show-inheritance:

View file

@ -1,22 +1,53 @@
ytdl\_subscribe.validators package ytdl\_subscribe.validators package
================================== ==================================
Subpackages
-----------
.. toctree::
:maxdepth: 4
ytdl_subscribe.validators.base
ytdl_subscribe.validators.config
Submodules Submodules
---------- ----------
ytdl\_subscribe.validators.exceptions module ytdl\_subscribe.validators.date\_range\_validator module
--------------------------------------------------------
.. automodule:: ytdl_subscribe.validators.date_range_validator
:members:
:undoc-members:
:show-inheritance:
ytdl\_subscribe.validators.strict\_dict\_validator module
---------------------------------------------------------
.. automodule:: ytdl_subscribe.validators.strict_dict_validator
:members:
:undoc-members:
:show-inheritance:
ytdl\_subscribe.validators.string\_datetime module
--------------------------------------------------
.. automodule:: ytdl_subscribe.validators.string_datetime
:members:
:undoc-members:
:show-inheritance:
ytdl\_subscribe.validators.string\_formatter\_validators module
---------------------------------------------------------------
.. automodule:: ytdl_subscribe.validators.string_formatter_validators
:members:
:undoc-members:
:show-inheritance:
ytdl\_subscribe.validators.string\_select\_validator module
-----------------------------------------------------------
.. automodule:: ytdl_subscribe.validators.string_select_validator
:members:
:undoc-members:
:show-inheritance:
ytdl\_subscribe.validators.validators module
-------------------------------------------- --------------------------------------------
.. automodule:: ytdl_subscribe.validators.exceptions .. automodule:: ytdl_subscribe.validators.validators
:members: :members:
:undoc-members: :undoc-members:
:show-inheritance: :show-inheritance:

View file

@ -75,6 +75,7 @@ def mock_entry_to_dict(
uid, uid,
title, title,
ext, ext,
extractor,
upload_date, upload_date,
upload_year, upload_year,
thumbnail_ext, thumbnail_ext,
@ -87,6 +88,7 @@ def mock_entry_to_dict(
"title": title, "title": title,
"sanitized_title": title, "sanitized_title": title,
"ext": ext, "ext": ext,
"extractor": extractor,
"upload_date": upload_date, "upload_date": upload_date,
"upload_date_standardized": f"{upload_year}-{_pad(upload_month)}-{_pad(upload_day)}", "upload_date_standardized": f"{upload_year}-{_pad(upload_month)}-{_pad(upload_day)}",
"upload_year": upload_year, "upload_year": upload_year,
@ -134,15 +136,11 @@ def validate_entry_properties(
def _validate_entry_properties(entry: Entry): def _validate_entry_properties(entry: Entry):
assert entry.uid == uid assert entry.uid == uid
assert entry.title == title assert entry.title == title
# Title does not require sanitizing, so should be the same
assert entry.sanitized_title == title assert entry.sanitized_title == title
assert entry.upload_date == upload_date assert entry.upload_date == upload_date
assert entry.upload_year == upload_year assert entry.upload_year == upload_year
assert entry.ext == ext assert entry.ext == ext
assert entry.thumbnail_ext == thumbnail_ext assert entry.thumbnail_ext == thumbnail_ext
assert entry.download_file_name == download_file_name
assert entry.download_thumbnail_name == download_thumbnail_name
assert entry.playlist_metadata is None
assert entry.extractor == extractor assert entry.extractor == extractor
return True return True

View file

@ -40,7 +40,6 @@ def mock_soundcloud_track_to_dict(
"album": title, "album": title,
"sanitized_album": title, "sanitized_album": title,
"album_year": upload_year, "album_year": upload_year,
"is_premiere": is_premiere,
} }
) )
@ -71,7 +70,8 @@ def validate_soundcloud_track_properties(
assert soundcloud_track.album == title assert soundcloud_track.album == title
assert soundcloud_track.sanitized_album == title assert soundcloud_track.sanitized_album == title
assert soundcloud_track.album_year == upload_year assert soundcloud_track.album_year == upload_year
assert soundcloud_track.is_premiere == is_premiere
assert soundcloud_track.is_premiere() == is_premiere
return True return True

View file

@ -0,0 +1,53 @@
from abc import ABC
from dataclasses import dataclass
from typing import Any
from typing import Dict
from typing import Optional
@dataclass
class PlaylistMetadata:
"""
Metadata for storing playlist information.
"""
playlist_index: int
playlist_id: str
playlist_extractor: str
class BaseEntry(ABC):
"""
Abstract entry object to represent anything download from ytdl (playlist metadata, media, etc).
"""
def __init__(self, entry_dict: Dict, working_directory: Optional[str] = None):
"""
Initialize the entry using ytdl metadata
Parameters
----------
entry_dict
Entry metadata
working_directory
Optional. Directory that the entry is downloaded to
"""
self._working_directory = working_directory
self._kwargs = entry_dict
def kwargs_contains(self, key: str) -> bool:
"""Returns whether internal kwargs contains the specified key"""
return key in self._kwargs
def kwargs(self, key) -> Any:
"""Returns an internal kwarg value supplied from ytdl"""
if not self.kwargs_contains(key):
raise KeyError(f"Expected '{key}' in {self.__class__.__name__} but does not exist.")
return self._kwargs[key]
def working_directory(self) -> str:
if self._working_directory is None:
raise ValueError(
"Entry working directory is not set when trying to access its download file path"
)
return self._working_directory

View file

@ -1,193 +1,29 @@
from abc import ABC
from dataclasses import dataclass
from pathlib import Path from pathlib import Path
from typing import Any
from typing import Dict from typing import Dict
from typing import Optional from typing import final
from yt_dlp.utils import sanitize_filename from ytdl_subscribe.entries.base_entry import BaseEntry
from ytdl_subscribe.entries.variables.entry_variables import EntryVariables
@dataclass class Entry(EntryVariables, BaseEntry):
class PlaylistMetadata:
"""
Metadata for storing playlist information.
"""
playlist_index: int
playlist_id: str
playlist_extractor: str
class BaseEntry(ABC):
"""
Abstract entry object to represent anything download from ytdl (playlist metadata, media, etc).
"""
def __init__(self, entry_dict: Dict, working_directory: Optional[str] = None):
"""
Initialize the entry using ytdl metadata
Parameters
----------
entry_dict
Entry metadata
working_directory
Optional. Directory that the entry is downloaded to
"""
self._working_directory = working_directory
self._kwargs = entry_dict
def kwargs_contains(self, key: str) -> bool:
"""Returns whether internal kwargs contains the specified key"""
return key in self._kwargs
def kwargs(self, key) -> Any:
"""Returns an internal kwarg value supplied from ytdl"""
if not self.kwargs_contains(key):
raise KeyError(f"Expected '{key}' in {self.__class__.__name__} but does not exist.")
return self._kwargs[key]
@property
def working_directory(self) -> str:
if self._working_directory is None:
raise ValueError(
"Entry working directory is not set when trying to access its download file path"
)
return self._working_directory
@property
def uid(self) -> str:
"""Returns the entry's unique id"""
return self.kwargs("id")
@property
def extractor(self) -> str:
"""
:return: The ytdl extrator name
"""
return self.kwargs("extractor")
@property
def playlist_metadata(self) -> Optional[PlaylistMetadata]:
"""
Reserved for any child entry class that resides in a playlist.
:return: Playlist metadata for this entry
"""
return None
def to_dict(self) -> Dict[str, str]:
"""Returns the entry's values as a dictionary"""
return {
"uid": self.uid,
}
class Entry(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.
""" """
@property def get_download_file_path(self) -> str:
def title(self) -> str:
"""Returns the entry's title"""
return self.kwargs("title")
@property
def sanitized_title(self) -> str:
"""Returns the entry's sanitized title"""
return sanitize_filename(self.title)
@property
def ext(self) -> str:
"""Returns the entry's file extension"""
return self.kwargs("ext")
@property
def upload_date(self) -> str:
"""Returns the entry's upload date"""
return self.kwargs("upload_date")
@property
def upload_year(self) -> int:
"""Returns the entry's upload year"""
return int(self.upload_date[:4])
@property
def upload_month_padded(self) -> str:
"""Returns the entry's upload month, padded"""
return self.upload_date[4:6]
@property
def upload_day_padded(self) -> str:
"""Returns the entry's upload day, padded"""
return self.upload_date[6:8]
@property
def upload_month(self) -> int:
"""Returns the entry's upload month as an int"""
return int(self.upload_month_padded.lstrip("0"))
@property
def upload_day(self) -> int:
"""Returns the entry's upload month as an int"""
return int(self.upload_day_padded.lstrip("0"))
@property
def upload_date_standardized(self) -> str:
"""
:return: upload date as YYYY-MM-DD
"""
return f"{self.upload_year}-{self.upload_month_padded}-{self.upload_day_padded}"
@property
def description(self) -> str:
return self.kwargs("description")
@property
def download_file_name(self) -> str:
"""Returns the entry's file name when downloaded locally"""
return f"{self.uid}.{self.ext}"
@property
def download_file_path(self) -> str:
"""Returns the entry's file path to where it was downloaded""" """Returns the entry's file path to where it was downloaded"""
return str(Path(self.working_directory) / self.download_file_name) return str(Path(self.working_directory()) / f"{self.uid}.{self.ext}")
@property def get_download_thumbnail_path(self) -> str:
def thumbnail_ext(self) -> str:
"""
:return: The entry's thumbnail extension
"""
return self.kwargs("thumbnail").split(".")[-1]
@property
def download_thumbnail_name(self) -> str:
"""Returns the thumbnail's file name when downloaded locally"""
return f"{self.uid}.{self.thumbnail_ext}"
@property
def download_thumbnail_path(self) -> str:
"""Returns the entry's thumbnail's file path to where it was downloaded""" """Returns the entry's thumbnail's file path to where it was downloaded"""
return str(Path(self.working_directory) / self.download_thumbnail_name) return str(Path(self.working_directory()) / f"{self.uid}.{self.thumbnail_ext}")
def to_dict(self) -> Dict: @final
"""Returns the entry's values as a dictionary""" def to_dict(self) -> Dict[str, str]:
return dict( """
super().to_dict(), Returns
**{ -------
"title": self.title, The variables in dictionary format
"sanitized_title": self.sanitized_title, """
"description": self.description, return self._to_dict()
"ext": self.ext,
"thumbnail_ext": self.thumbnail_ext,
"upload_date": self.upload_date,
"upload_date_standardized": self.upload_date_standardized,
"upload_year": self.upload_year,
"upload_month": self.upload_month,
"upload_month_padded": self.upload_month_padded,
"upload_day": self.upload_day,
"upload_day_padded": self.upload_day_padded,
},
)

View file

@ -1,45 +1,17 @@
from typing import Dict from typing import Dict
from typing import List from typing import List
from typing import Optional
from yt_dlp.utils import sanitize_filename
from ytdl_subscribe.entries.base_entry import PlaylistMetadata
from ytdl_subscribe.entries.entry import Entry from ytdl_subscribe.entries.entry import Entry
from ytdl_subscribe.entries.entry import PlaylistMetadata from ytdl_subscribe.entries.variables.soundcloud_variables import SoundcloudVariables
class SoundcloudTrack(Entry): class SoundcloudTrack(SoundcloudVariables, Entry):
""" """
Entry object to represent a Soundcloud track yt-dlp that is a single, which implies Entry object to represent a Soundcloud track yt-dlp that is a single, which implies
it does not belong to an album. it does not belong to an album.
""" """
@property
def track_number(self) -> int:
"""Returns the entry's track number. Since this is a single, it will always be 1"""
return 1
@property
def track_number_padded(self) -> str:
"""Returns the entry's padded track number, to the tens-place"""
return f"{self.track_number:02d}"
@property
def album(self) -> str:
"""Returns the entry's album name. Since this is a single, set the album to the title"""
return self.title
@property
def sanitized_album(self) -> str:
"""Returns the entry's sanitized album name"""
return sanitize_filename(self.album)
@property
def album_year(self) -> int:
"""Returns the entry's album year. Since this is a single, use the upload year"""
return self.upload_year
@property
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
@ -47,20 +19,6 @@ class SoundcloudTrack(Entry):
""" """
return "/preview/" in self.kwargs("url") return "/preview/" in self.kwargs("url")
def to_dict(self) -> Dict:
"""Returns the entry's values as a dictionary."""
return dict(
super().to_dict(),
**{
"track_number": self.track_number,
"track_number_padded": self.track_number_padded,
"album": self.album,
"sanitized_album": self.sanitized_album,
"album_year": self.album_year,
"is_premiere": self.is_premiere,
},
)
class SoundcloudAlbumTrack(SoundcloudTrack): class SoundcloudAlbumTrack(SoundcloudTrack):
""" """
@ -83,10 +41,6 @@ class SoundcloudAlbumTrack(SoundcloudTrack):
self._album_year = album_year self._album_year = album_year
self._playlist_metadata = playlist_metadata self._playlist_metadata = playlist_metadata
@property
def playlist_metadata(self) -> Optional[PlaylistMetadata]:
return self._playlist_metadata
@property @property
def track_number(self) -> int: def track_number(self) -> int:
"""Returns the entry's track number""" """Returns the entry's track number"""
@ -112,7 +66,7 @@ class SoundcloudAlbumTrack(SoundcloudTrack):
) -> "SoundcloudAlbumTrack": ) -> "SoundcloudAlbumTrack":
return SoundcloudAlbumTrack( return SoundcloudAlbumTrack(
entry_dict=soundcloud_track._kwargs, # pylint: disable=protected-access entry_dict=soundcloud_track._kwargs, # pylint: disable=protected-access
working_directory=soundcloud_track.working_directory, working_directory=soundcloud_track.working_directory(),
album=album, album=album,
album_year=album_year, album_year=album_year,
playlist_metadata=playlist_metadata, playlist_metadata=playlist_metadata,
@ -143,7 +97,7 @@ class SoundcloudAlbum(Entry):
tracks = [ tracks = [
track track
for track in self._single_tracks for track in self._single_tracks
if not (skip_premiere_tracks and track.is_premiere) if not (skip_premiere_tracks and track.is_premiere())
] ]
album_tracks = [ album_tracks = [

View file

@ -0,0 +1,156 @@
from typing import Dict
from typing import final
from yt_dlp.utils import sanitize_filename
from ytdl_subscribe.entries.base_entry import BaseEntry
# This file contains mixins to a BaseEntry subclass. Ignore pylint's "no kwargs member" suggestion
# pylint: disable=no-member
class BaseEntryVariables:
"""
Abstract entry object to represent usable variables for formatting presets and subscriptions
"""
@property
def uid(self: BaseEntry) -> str:
"""
Returns
-------
The entry's unique ID
"""
return self.kwargs("id")
@property
def extractor(self: BaseEntry) -> str:
"""
Returns
-------
The ytdl extractor name
"""
return self.kwargs("extractor")
@final
def _to_dict(self) -> Dict[str, str]:
"""
Returns
-------
Dictionary containing all variables
"""
cls = self.__class__
property_names = [prop for prop in dir(cls) if isinstance(getattr(cls, prop), property)]
return {property_name: getattr(self, property_name) for property_name in property_names}
class EntryVariables(BaseEntryVariables):
@property
def title(self: BaseEntry) -> str:
"""
Returns
-------
The title of the entry
"""
return self.kwargs("title")
@property
def sanitized_title(self) -> str:
"""
Returns
-------
The sanitized title of the entry, which is safe to use for Unix and Windows file names.
"""
return sanitize_filename(self.title)
@property
def ext(self: BaseEntry) -> str:
"""
Returns
-------
The downloaded entry's file extension
"""
return self.kwargs("ext")
@property
def thumbnail_ext(self: BaseEntry) -> str:
"""
Returns
-------
The download entry's thumbnail extension
"""
# TODO: Always return jpg
return self.kwargs("thumbnail").split(".")[-1]
@property
def upload_date(self: BaseEntry) -> str:
"""
Returns
-------
The entry's uploaded date, in YYYYMMDD format.
"""
return self.kwargs("upload_date")
@property
def upload_year(self) -> int:
"""
Returns
-------
The entry's upload year
"""
return int(self.upload_date[:4])
@property
def upload_month_padded(self) -> str:
"""
Returns
-------
The entry's upload month padded to two digits, i.e. March returns "03"
"""
return self.upload_date[4:6]
@property
def upload_day_padded(self) -> str:
"""
Returns
-------
The entry's upload day padded to two digits, i.e. the fifth returns "05"
"""
return self.upload_date[6:8]
@property
def upload_month(self) -> int:
"""
Returns
-------
The upload month as an integer (no padding).
"""
return int(self.upload_month_padded.lstrip("0"))
@property
def upload_day(self) -> int:
"""
Returns
-------
The upload day as an integer (no padding).
"""
return int(self.upload_day_padded.lstrip("0"))
@property
def upload_date_standardized(self) -> str:
"""
Returns
-------
The uploaded date formatted as YYYY-MM-DD
"""
return f"{self.upload_year}-{self.upload_month_padded}-{self.upload_day_padded}"
@property
def description(self: BaseEntry) -> str:
"""
Returns
-------
The description of the entry.
"""
return self.kwargs("description")

View file

@ -0,0 +1,54 @@
from yt_dlp.utils import sanitize_filename
from ytdl_subscribe.entries.variables.entry_variables import EntryVariables
# This file contains mixins to a BaseEntry subclass. Ignore pylint's "no kwargs member" suggestion
# pylint: disable=no-member
class SoundcloudVariables(EntryVariables):
@property
def track_number(self) -> int:
"""
Returns
-------
The entry's track number within an album. For singles, it will always be 1.
"""
return 1
@property
def track_number_padded(self) -> str:
"""
Returns
-------
The entry's track number, padded two digits.
"""
return f"{self.track_number:02d}"
@property
def album(self) -> str:
"""
Returns
-------
The entry's album name. For singles, it will be the same as the title.
"""
return self.title
@property
def sanitized_album(self) -> str:
"""
Returns
-------
The entry's sanitized album name, which is safe to use for Unix and Windows file names.
"""
return sanitize_filename(self.album)
@property
def album_year(self) -> int:
"""
Returns
-------
The entry's album year, which is determined by the latest year amongst all tracks in the
album.
"""
return self.upload_year

View file

@ -0,0 +1,20 @@
from ytdl_subscribe.entries.base_entry import BaseEntry
from ytdl_subscribe.entries.variables.entry_variables import EntryVariables
# This file contains mixins to a BaseEntry subclass. Ignore pylint's "no kwargs member" suggestion
# pylint: disable=no-member
class YoutubeVideoVariables(EntryVariables):
@property
def track_title(self: BaseEntry) -> str:
"""
Returns
-------
The track title of a music video if it is available, otherwise it falls back to the title.
"""
# Try to get the track, fall back on title
if self.kwargs_contains("track"):
return self.kwargs("track")
return super().title

View file

@ -2,27 +2,15 @@ import os.path
from pathlib import Path from pathlib import Path
from ytdl_subscribe.entries.entry import Entry from ytdl_subscribe.entries.entry import Entry
from ytdl_subscribe.entries.variables.youtube_variables import YoutubeVideoVariables
class YoutubeVideo(Entry): class YoutubeVideo(YoutubeVideoVariables, Entry):
""" """
Entry object to represent a Youtube video. Entry object to represent a Youtube video.
""" """
@property def get_download_thumbnail_path(self) -> str:
def title(self) -> str:
"""
Returns the title of the youtube video. Tries to get the track name if it is available,
otherwise it falls back to the title.
"""
# Try to get the track, fall back on title
if self.kwargs_contains("track"):
return self.kwargs("track")
return super().title
@property
def download_thumbnail_path(self) -> str:
""" """
Returns Returns
------- -------
@ -30,7 +18,7 @@ class YoutubeVideo(Entry):
thumbnail entry is not the actual thumbnail it downloaded. So check all the possible 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. extensions that it may have downloaded, and see if that thumbnail's extension exists.
TODO: move this into the init so the thumbnail_ext variable can be set accordingly TODO: always make thumbnails jpg
""" """
thumbnails = self.kwargs("thumbnails") thumbnails = self.kwargs("thumbnails")
possible_thumbnail_exts = set() possible_thumbnail_exts = set()
@ -38,8 +26,8 @@ class YoutubeVideo(Entry):
possible_thumbnail_exts.add(thumbnail["url"].split(".")[-1]) possible_thumbnail_exts.add(thumbnail["url"].split(".")[-1])
for ext in possible_thumbnail_exts: for ext in possible_thumbnail_exts:
possible_thumbnail_path = str(Path(self.working_directory) / f"{self.uid}.{ext}") possible_thumbnail_path = str(Path(self.working_directory()) / f"{self.uid}.{ext}")
if os.path.isfile(possible_thumbnail_path): if os.path.isfile(possible_thumbnail_path):
return possible_thumbnail_path return possible_thumbnail_path
return super().download_thumbnail_path return super().get_download_thumbnail_path()

View file

@ -31,14 +31,14 @@ class ConvertThumbnailPlugin(Plugin[ConvertThumbnailOptions]):
Convert the source thumbnail to the desired type. Leave the original file name and Convert the source thumbnail to the desired type. Leave the original file name and
extension intact to let output_options deal with renaming it. extension intact to let output_options deal with renaming it.
""" """
if not os.path.isfile(entry.download_thumbnail_path): if not os.path.isfile(entry.get_download_thumbnail_path()):
raise ValueError("Thumbnail not found") raise ValueError("Thumbnail not found")
image: Image = pil_open(entry.download_thumbnail_path).convert("RGB") image: Image = pil_open(entry.get_download_thumbnail_path()).convert("RGB")
# Pillow likes the formal 'jpeg' name and not 'jpg' # Pillow likes the formal 'jpeg' name and not 'jpg'
thumbnail_format = self.plugin_options.convert_to.value thumbnail_format = self.plugin_options.convert_to.value
if thumbnail_format == "jpg": if thumbnail_format == "jpg":
thumbnail_format = "jpeg" thumbnail_format = "jpeg"
image.save(fp=entry.download_thumbnail_path, format=thumbnail_format) image.save(fp=entry.get_download_thumbnail_path(), format=thumbnail_format)

View file

@ -28,7 +28,7 @@ class MusicTagsPlugin(Plugin[MusicTagsOptions]):
""" """
Tags the entry's audio file using values defined in the metadata options Tags the entry's audio file using values defined in the metadata options
""" """
audio_file = music_tag.load_file(entry.download_file_path) audio_file = music_tag.load_file(entry.get_download_file_path())
for tag, tag_formatter in self.plugin_options.tags.dict.items(): for tag, tag_formatter in self.plugin_options.tags.dict.items():
audio_file[tag] = self.overrides.apply_formatter(formatter=tag_formatter, entry=entry) audio_file[tag] = self.overrides.apply_formatter(formatter=tag_formatter, entry=entry)
audio_file.save() audio_file.save()

View file

@ -108,7 +108,7 @@ class Subscription:
) )
self._enhanced_download_archive.mapping.add_entry(entry, output_file_name) self._enhanced_download_archive.mapping.add_entry(entry, output_file_name)
self._copy_file_to_output_directory( self._copy_file_to_output_directory(
source_file_path=entry.download_file_path, output_file_name=output_file_name source_file_path=entry.get_download_file_path(), output_file_name=output_file_name
) )
if self.output_options.thumbnail_name: if self.output_options.thumbnail_name:
@ -117,7 +117,7 @@ class Subscription:
) )
self._enhanced_download_archive.mapping.add_entry(entry, output_thumbnail_name) self._enhanced_download_archive.mapping.add_entry(entry, output_thumbnail_name)
self._copy_file_to_output_directory( self._copy_file_to_output_directory(
source_file_path=entry.download_thumbnail_path, source_file_path=entry.get_download_thumbnail_path(),
output_file_name=output_thumbnail_name, output_file_name=output_thumbnail_name,
) )