variables as mixins for docs
This commit is contained in:
parent
53b1923653
commit
5eeb117fca
22 changed files with 547 additions and 288 deletions
|
|
@ -57,3 +57,5 @@ html_static_path = ["_static"]
|
|||
# Do not show full module path in api docs
|
||||
add_module_names = False
|
||||
python_use_unqualified_type_names = True
|
||||
napoleon_numpy_docstrings = True
|
||||
napoleon_use_rtype = False
|
||||
|
|
|
|||
53
docs/source/ytdl_subscribe.config.rst
Normal file
53
docs/source/ytdl_subscribe.config.rst
Normal 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:
|
||||
|
|
@ -1,9 +1,25 @@
|
|||
ytdl\_subscribe.entries package
|
||||
===============================
|
||||
|
||||
Subpackages
|
||||
-----------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 4
|
||||
|
||||
ytdl_subscribe.entries.variables
|
||||
|
||||
Submodules
|
||||
----------
|
||||
|
||||
ytdl\_subscribe.entries.base\_entry module
|
||||
------------------------------------------
|
||||
|
||||
.. automodule:: ytdl_subscribe.entries.base_entry
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ytdl\_subscribe.entries.entry module
|
||||
------------------------------------
|
||||
|
||||
|
|
|
|||
37
docs/source/ytdl_subscribe.entries.variables.rst
Normal file
37
docs/source/ytdl_subscribe.entries.variables.rst
Normal 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:
|
||||
53
docs/source/ytdl_subscribe.plugins.rst
Normal file
53
docs/source/ytdl_subscribe.plugins.rst
Normal 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:
|
||||
|
|
@ -8,9 +8,12 @@ Subpackages
|
|||
:maxdepth: 4
|
||||
|
||||
ytdl_subscribe.cli
|
||||
ytdl_subscribe.config
|
||||
ytdl_subscribe.downloaders
|
||||
ytdl_subscribe.entries
|
||||
ytdl_subscribe.plugins
|
||||
ytdl_subscribe.subscriptions
|
||||
ytdl_subscribe.utils
|
||||
ytdl_subscribe.validators
|
||||
ytdl_subscribe.ytdl_additions
|
||||
|
||||
|
|
|
|||
|
|
@ -4,14 +4,6 @@ ytdl\_subscribe.subscriptions package
|
|||
Submodules
|
||||
----------
|
||||
|
||||
ytdl\_subscribe.subscriptions.soundcloud module
|
||||
-----------------------------------------------
|
||||
|
||||
.. automodule:: ytdl_subscribe.subscriptions.soundcloud
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ytdl\_subscribe.subscriptions.subscription module
|
||||
-------------------------------------------------
|
||||
|
||||
|
|
@ -20,14 +12,6 @@ ytdl\_subscribe.subscriptions.subscription module
|
|||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ytdl\_subscribe.subscriptions.youtube module
|
||||
--------------------------------------------
|
||||
|
||||
.. automodule:: ytdl_subscribe.subscriptions.youtube
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
Module contents
|
||||
---------------
|
||||
|
||||
|
|
|
|||
21
docs/source/ytdl_subscribe.utils.rst
Normal file
21
docs/source/ytdl_subscribe.utils.rst
Normal 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:
|
||||
|
|
@ -1,22 +1,53 @@
|
|||
ytdl\_subscribe.validators package
|
||||
==================================
|
||||
|
||||
Subpackages
|
||||
-----------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 4
|
||||
|
||||
ytdl_subscribe.validators.base
|
||||
ytdl_subscribe.validators.config
|
||||
|
||||
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:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@ def mock_entry_to_dict(
|
|||
uid,
|
||||
title,
|
||||
ext,
|
||||
extractor,
|
||||
upload_date,
|
||||
upload_year,
|
||||
thumbnail_ext,
|
||||
|
|
@ -87,6 +88,7 @@ def mock_entry_to_dict(
|
|||
"title": title,
|
||||
"sanitized_title": title,
|
||||
"ext": ext,
|
||||
"extractor": extractor,
|
||||
"upload_date": upload_date,
|
||||
"upload_date_standardized": f"{upload_year}-{_pad(upload_month)}-{_pad(upload_day)}",
|
||||
"upload_year": upload_year,
|
||||
|
|
@ -134,15 +136,11 @@ def validate_entry_properties(
|
|||
def _validate_entry_properties(entry: Entry):
|
||||
assert entry.uid == uid
|
||||
assert entry.title == title
|
||||
# Title does not require sanitizing, so should be the same
|
||||
assert entry.sanitized_title == title
|
||||
assert entry.upload_date == upload_date
|
||||
assert entry.upload_year == upload_year
|
||||
assert entry.ext == 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
|
||||
|
||||
return True
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@ def mock_soundcloud_track_to_dict(
|
|||
"album": title,
|
||||
"sanitized_album": title,
|
||||
"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.sanitized_album == title
|
||||
assert soundcloud_track.album_year == upload_year
|
||||
assert soundcloud_track.is_premiere == is_premiere
|
||||
|
||||
assert soundcloud_track.is_premiere() == is_premiere
|
||||
|
||||
return True
|
||||
|
||||
|
|
|
|||
53
ytdl_subscribe/entries/base_entry.py
Normal file
53
ytdl_subscribe/entries/base_entry.py
Normal 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
|
||||
|
|
@ -1,193 +1,29 @@
|
|||
from abc import ABC
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
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 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):
|
||||
class Entry(EntryVariables, BaseEntry):
|
||||
"""
|
||||
Entry object to represent a single media object returned from yt-dlp.
|
||||
"""
|
||||
|
||||
@property
|
||||
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:
|
||||
def get_download_file_path(self) -> str:
|
||||
"""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 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:
|
||||
def get_download_thumbnail_path(self) -> str:
|
||||
"""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:
|
||||
"""Returns the entry's values as a dictionary"""
|
||||
return dict(
|
||||
super().to_dict(),
|
||||
**{
|
||||
"title": self.title,
|
||||
"sanitized_title": self.sanitized_title,
|
||||
"description": self.description,
|
||||
"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,
|
||||
},
|
||||
)
|
||||
@final
|
||||
def to_dict(self) -> Dict[str, str]:
|
||||
"""
|
||||
Returns
|
||||
-------
|
||||
The variables in dictionary format
|
||||
"""
|
||||
return self._to_dict()
|
||||
|
|
|
|||
|
|
@ -1,45 +1,17 @@
|
|||
from typing import Dict
|
||||
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 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
|
||||
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:
|
||||
"""
|
||||
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")
|
||||
|
||||
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):
|
||||
"""
|
||||
|
|
@ -83,10 +41,6 @@ class SoundcloudAlbumTrack(SoundcloudTrack):
|
|||
self._album_year = album_year
|
||||
self._playlist_metadata = playlist_metadata
|
||||
|
||||
@property
|
||||
def playlist_metadata(self) -> Optional[PlaylistMetadata]:
|
||||
return self._playlist_metadata
|
||||
|
||||
@property
|
||||
def track_number(self) -> int:
|
||||
"""Returns the entry's track number"""
|
||||
|
|
@ -112,7 +66,7 @@ class SoundcloudAlbumTrack(SoundcloudTrack):
|
|||
) -> "SoundcloudAlbumTrack":
|
||||
return SoundcloudAlbumTrack(
|
||||
entry_dict=soundcloud_track._kwargs, # pylint: disable=protected-access
|
||||
working_directory=soundcloud_track.working_directory,
|
||||
working_directory=soundcloud_track.working_directory(),
|
||||
album=album,
|
||||
album_year=album_year,
|
||||
playlist_metadata=playlist_metadata,
|
||||
|
|
@ -143,7 +97,7 @@ class SoundcloudAlbum(Entry):
|
|||
tracks = [
|
||||
track
|
||||
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 = [
|
||||
|
|
|
|||
0
ytdl_subscribe/entries/variables/__init__.py
Normal file
0
ytdl_subscribe/entries/variables/__init__.py
Normal file
156
ytdl_subscribe/entries/variables/entry_variables.py
Normal file
156
ytdl_subscribe/entries/variables/entry_variables.py
Normal 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")
|
||||
54
ytdl_subscribe/entries/variables/soundcloud_variables.py
Normal file
54
ytdl_subscribe/entries/variables/soundcloud_variables.py
Normal 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
|
||||
20
ytdl_subscribe/entries/variables/youtube_variables.py
Normal file
20
ytdl_subscribe/entries/variables/youtube_variables.py
Normal 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
|
||||
|
|
@ -2,27 +2,15 @@ import os.path
|
|||
from pathlib import Path
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
@property
|
||||
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:
|
||||
def get_download_thumbnail_path(self) -> str:
|
||||
"""
|
||||
Returns
|
||||
-------
|
||||
|
|
@ -30,7 +18,7 @@ class YoutubeVideo(Entry):
|
|||
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: move this into the init so the thumbnail_ext variable can be set accordingly
|
||||
TODO: always make thumbnails jpg
|
||||
"""
|
||||
thumbnails = self.kwargs("thumbnails")
|
||||
possible_thumbnail_exts = set()
|
||||
|
|
@ -38,8 +26,8 @@ class YoutubeVideo(Entry):
|
|||
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}")
|
||||
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().download_thumbnail_path
|
||||
return super().get_download_thumbnail_path()
|
||||
|
|
|
|||
|
|
@ -31,14 +31,14 @@ class ConvertThumbnailPlugin(Plugin[ConvertThumbnailOptions]):
|
|||
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.download_thumbnail_path):
|
||||
if not os.path.isfile(entry.get_download_thumbnail_path()):
|
||||
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'
|
||||
thumbnail_format = self.plugin_options.convert_to.value
|
||||
if thumbnail_format == "jpg":
|
||||
thumbnail_format = "jpeg"
|
||||
|
||||
image.save(fp=entry.download_thumbnail_path, format=thumbnail_format)
|
||||
image.save(fp=entry.get_download_thumbnail_path(), format=thumbnail_format)
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ class MusicTagsPlugin(Plugin[MusicTagsOptions]):
|
|||
"""
|
||||
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():
|
||||
audio_file[tag] = self.overrides.apply_formatter(formatter=tag_formatter, entry=entry)
|
||||
audio_file.save()
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ class Subscription:
|
|||
)
|
||||
self._enhanced_download_archive.mapping.add_entry(entry, output_file_name)
|
||||
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:
|
||||
|
|
@ -117,7 +117,7 @@ class Subscription:
|
|||
)
|
||||
self._enhanced_download_archive.mapping.add_entry(entry, output_thumbnail_name)
|
||||
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,
|
||||
)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue