feat: ✨ Enhance media formatting functions
* Added detailed docstrings for `format_str_media` and related helper functions. * Refactored media formatting logic to utilize dedicated functions for better readability and maintainability. * Improved error handling in media formatting. * Updated `file_unique_suffix` function to correct the spelling of `separator`.
This commit is contained in:
+281
-123
@@ -116,119 +116,282 @@ def format_str_media(
|
|||||||
list_pos: int = 0,
|
list_pos: int = 0,
|
||||||
list_total: int = 0,
|
list_total: int = 0,
|
||||||
) -> str:
|
) -> str:
|
||||||
result: str = name
|
"""Format a string based on media attributes.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name (str): The format template name.
|
||||||
|
media (Track | Album | Playlist | UserPlaylist | Video | Mix): The media object.
|
||||||
|
album_track_num_pad_min (int): Minimum padding for track numbers. Defaults to 0.
|
||||||
|
list_pos (int): Position in a list. Defaults to 0.
|
||||||
|
list_total (int): Total items in a list. Defaults to 0.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: The formatted string.
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
match name:
|
# Try each formatter function in sequence
|
||||||
case "artist_name":
|
for formatter in (
|
||||||
if isinstance(media, Track | Video):
|
_format_names,
|
||||||
if hasattr(media, "artists"):
|
_format_numbers,
|
||||||
result = name_builder_artist(media)
|
_format_ids,
|
||||||
elif hasattr(media, "artist"):
|
_format_durations,
|
||||||
result = media.artist.name
|
_format_metadata,
|
||||||
case "album_artist":
|
_format_volumes,
|
||||||
result = name_builder_album_artist(media, first_only=True)
|
):
|
||||||
case "album_artists":
|
result = formatter(name, media, album_track_num_pad_min, list_pos, list_total)
|
||||||
result = name_builder_album_artist(media)
|
if result is not None:
|
||||||
case "track_title":
|
return result
|
||||||
if isinstance(media, Track | Video):
|
|
||||||
result = name_builder_title(media)
|
|
||||||
case "mix_name":
|
|
||||||
if isinstance(media, Mix):
|
|
||||||
result = media.title
|
|
||||||
case "playlist_name":
|
|
||||||
if isinstance(media, Playlist | UserPlaylist):
|
|
||||||
result = media.name
|
|
||||||
case "album_title":
|
|
||||||
if isinstance(media, Album):
|
|
||||||
result = media.name
|
|
||||||
elif isinstance(media, Track):
|
|
||||||
result = media.album.name
|
|
||||||
case "album_track_num":
|
|
||||||
if isinstance(media, Track | Video):
|
|
||||||
result = calculate_number_padding(
|
|
||||||
album_track_num_pad_min,
|
|
||||||
media.track_num,
|
|
||||||
media.album.num_tracks if hasattr(media, "album") else 1,
|
|
||||||
)
|
|
||||||
case "album_num_tracks":
|
|
||||||
if isinstance(media, Track | Video):
|
|
||||||
result = str(media.album.num_tracks if hasattr(media, "album") else 1)
|
|
||||||
case "track_id":
|
|
||||||
if isinstance(media, Track | Video):
|
|
||||||
result = str(media.id)
|
|
||||||
case "playlist_id":
|
|
||||||
if isinstance(media, Playlist):
|
|
||||||
result = str(media.id)
|
|
||||||
case "album_id":
|
|
||||||
if isinstance(media, Album):
|
|
||||||
result = str(media.id)
|
|
||||||
elif isinstance(media, Track):
|
|
||||||
result = str(media.album.id)
|
|
||||||
case "track_duration_seconds":
|
|
||||||
if isinstance(media, Track | Video):
|
|
||||||
result = str(media.duration)
|
|
||||||
case "track_duration_minutes":
|
|
||||||
if isinstance(media, Track | Video):
|
|
||||||
m, s = divmod(media.duration, 60)
|
|
||||||
result = f"{m:01d}:{s:02d}"
|
|
||||||
case "album_duration_seconds":
|
|
||||||
if isinstance(media, Album):
|
|
||||||
result = str(media.duration)
|
|
||||||
case "album_duration_minutes":
|
|
||||||
if isinstance(media, Album):
|
|
||||||
m, s = divmod(media.duration, 60)
|
|
||||||
result = f"{m:01d}:{s:02d}"
|
|
||||||
case "playlist_duration_seconds":
|
|
||||||
if isinstance(media, Album):
|
|
||||||
result = str(media.duration)
|
|
||||||
case "playlist_duration_minutes":
|
|
||||||
if isinstance(media, Album):
|
|
||||||
m, s = divmod(media.duration, 60)
|
|
||||||
result = f"{m:01d}:{s:02d}"
|
|
||||||
case "album_year":
|
|
||||||
if isinstance(media, Album):
|
|
||||||
result = str(media.year)
|
|
||||||
elif isinstance(media, Track):
|
|
||||||
result = str(media.album.year)
|
|
||||||
case "video_quality":
|
|
||||||
if isinstance(media, Video):
|
|
||||||
result = media.video_quality
|
|
||||||
case "track_quality":
|
|
||||||
if isinstance(media, Track):
|
|
||||||
result = ", ".join(tag for tag in media.media_metadata_tags if tag is not None)
|
|
||||||
case "track_explicit":
|
|
||||||
if isinstance(media, Track | Video):
|
|
||||||
result = FORMAT_TEMPLATE_EXPLICIT if media.explicit else ""
|
|
||||||
case "album_explicit":
|
|
||||||
if isinstance(media, Album):
|
|
||||||
result = FORMAT_TEMPLATE_EXPLICIT if media.explicit else ""
|
|
||||||
case "album_num_volumes":
|
|
||||||
if isinstance(media, Album):
|
|
||||||
result = str(media.num_volumes)
|
|
||||||
case "track_volume_num":
|
|
||||||
if isinstance(media, Track | Video):
|
|
||||||
result = str(media.volume_num)
|
|
||||||
case "track_volume_num_optional":
|
|
||||||
if isinstance(media, Track | Video):
|
|
||||||
num_volumes: int = media.album.num_volumes if hasattr(media, "album") else 1
|
|
||||||
result = "" if num_volumes == 1 else str(media.volume_num)
|
|
||||||
case "track_volume_num_optional_CD":
|
|
||||||
if isinstance(media, Track | Video):
|
|
||||||
num_volumes: int = media.album.num_volumes if hasattr(media, "album") else 1
|
|
||||||
result = "" if num_volumes == 1 else f"CD{media.volume_num!s}"
|
|
||||||
case "isrc":
|
|
||||||
if isinstance(media, Track):
|
|
||||||
result = media.isrc
|
|
||||||
case "list_pos":
|
|
||||||
if isinstance(media, Track | Video):
|
|
||||||
# TODO: Rename `album_track_num_pad_min` globally.
|
|
||||||
result = calculate_number_padding(album_track_num_pad_min, list_pos, list_total)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# TODO: Implement better exception logging.
|
# TODO: Implement better exception logging.
|
||||||
print(e)
|
print(e)
|
||||||
|
|
||||||
return result
|
return name
|
||||||
|
|
||||||
|
|
||||||
|
def _format_artist_names(
|
||||||
|
name: str,
|
||||||
|
media: Track | Album | Playlist | UserPlaylist | Video | Mix,
|
||||||
|
*_args,
|
||||||
|
) -> str | None:
|
||||||
|
"""Handle artist name-related format strings.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name (str): The format string name to check.
|
||||||
|
media (Track | Album | Playlist | UserPlaylist | Video | Mix): The media object to extract artist information from.
|
||||||
|
*_args (Any): Additional arguments (not used).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str | None: The formatted artist name or None if the format string is not artist-related.
|
||||||
|
"""
|
||||||
|
if name == "artist_name" and isinstance(media, Track | Video):
|
||||||
|
if hasattr(media, "artists"):
|
||||||
|
return name_builder_artist(media)
|
||||||
|
elif hasattr(media, "artist"):
|
||||||
|
return media.artist.name
|
||||||
|
elif name == "album_artist":
|
||||||
|
return name_builder_album_artist(media, first_only=True)
|
||||||
|
elif name == "album_artists":
|
||||||
|
return name_builder_album_artist(media)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _format_titles(
|
||||||
|
name: str,
|
||||||
|
media: Track | Album | Playlist | UserPlaylist | Video | Mix,
|
||||||
|
*_args,
|
||||||
|
) -> str | None:
|
||||||
|
"""Handle title-related format strings.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name (str): The format string name to check.
|
||||||
|
media (Track | Album | Playlist | UserPlaylist | Video | Mix): The media object to extract title information from.
|
||||||
|
*_args (Any): Additional arguments (not used).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str | None: The formatted title or None if the format string is not title-related.
|
||||||
|
"""
|
||||||
|
if name == "track_title" and isinstance(media, Track | Video):
|
||||||
|
return name_builder_title(media)
|
||||||
|
elif name == "mix_name" and isinstance(media, Mix):
|
||||||
|
return media.title
|
||||||
|
elif name == "playlist_name" and isinstance(media, Playlist | UserPlaylist):
|
||||||
|
return media.name
|
||||||
|
elif name == "album_title":
|
||||||
|
if isinstance(media, Album):
|
||||||
|
return media.name
|
||||||
|
elif isinstance(media, Track):
|
||||||
|
return media.album.name
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _format_names(
|
||||||
|
name: str,
|
||||||
|
media: Track | Album | Playlist | UserPlaylist | Video | Mix,
|
||||||
|
*_args,
|
||||||
|
) -> str | None:
|
||||||
|
"""Handle name-related format strings.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name (str): The format string name to check.
|
||||||
|
media (Track | Album | Playlist | UserPlaylist | Video | Mix): The media object to extract name information from.
|
||||||
|
*_args (Any): Additional arguments (not used).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str | None: The formatted name or None if the format string is not name-related.
|
||||||
|
"""
|
||||||
|
# First try artist name formats
|
||||||
|
result = _format_artist_names(name, media)
|
||||||
|
if result is not None:
|
||||||
|
return result
|
||||||
|
|
||||||
|
# Then try title formats
|
||||||
|
return _format_titles(name, media)
|
||||||
|
|
||||||
|
|
||||||
|
def _format_numbers(
|
||||||
|
name: str,
|
||||||
|
media: Track | Album | Playlist | UserPlaylist | Video | Mix,
|
||||||
|
album_track_num_pad_min: int,
|
||||||
|
list_pos: int,
|
||||||
|
list_total: int,
|
||||||
|
*_args,
|
||||||
|
) -> str | None:
|
||||||
|
"""Handle number-related format strings.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name (str): The format string name to check.
|
||||||
|
media (Track | Album | Playlist | UserPlaylist | Video | Mix): The media object to extract number information from.
|
||||||
|
album_track_num_pad_min (int): Minimum padding for track numbers.
|
||||||
|
list_pos (int): Position in a list.
|
||||||
|
list_total (int): Total items in a list.
|
||||||
|
*_args (Any): Additional arguments (not used).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str | None: The formatted number or None if the format string is not number-related.
|
||||||
|
"""
|
||||||
|
if name == "album_track_num" and isinstance(media, Track | Video):
|
||||||
|
return calculate_number_padding(
|
||||||
|
album_track_num_pad_min,
|
||||||
|
media.track_num,
|
||||||
|
media.album.num_tracks if hasattr(media, "album") else 1,
|
||||||
|
)
|
||||||
|
elif name == "album_num_tracks" and isinstance(media, Track | Video):
|
||||||
|
return str(media.album.num_tracks if hasattr(media, "album") else 1)
|
||||||
|
elif name == "list_pos" and isinstance(media, Track | Video):
|
||||||
|
# TODO: Rename `album_track_num_pad_min` globally.
|
||||||
|
return calculate_number_padding(album_track_num_pad_min, list_pos, list_total)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _format_ids(
|
||||||
|
name: str,
|
||||||
|
media: Track | Album | Playlist | UserPlaylist | Video | Mix,
|
||||||
|
*_args,
|
||||||
|
) -> str | None:
|
||||||
|
"""Handle ID-related format strings.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name (str): The format string name to check.
|
||||||
|
media (Track | Album | Playlist | UserPlaylist | Video | Mix): The media object to extract ID information from.
|
||||||
|
*_args (Any): Additional arguments (not used).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str | None: The formatted ID or None if the format string is not ID-related.
|
||||||
|
"""
|
||||||
|
# Handle track and playlist IDs
|
||||||
|
if (name == "track_id" and isinstance(media, Track | Video)) or (
|
||||||
|
name == "playlist_id" and isinstance(media, Playlist)
|
||||||
|
):
|
||||||
|
return str(media.id)
|
||||||
|
# Handle album IDs
|
||||||
|
elif name == "album_id":
|
||||||
|
if isinstance(media, Album):
|
||||||
|
return str(media.id)
|
||||||
|
elif isinstance(media, Track):
|
||||||
|
return str(media.album.id)
|
||||||
|
# Handle ISRC
|
||||||
|
elif name == "isrc" and isinstance(media, Track):
|
||||||
|
return media.isrc
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _format_durations(
|
||||||
|
name: str,
|
||||||
|
media: Track | Album | Playlist | UserPlaylist | Video | Mix,
|
||||||
|
*_args,
|
||||||
|
) -> str | None:
|
||||||
|
"""Handle duration-related format strings.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name (str): The format string name to check.
|
||||||
|
media (Track | Album | Playlist | UserPlaylist | Video | Mix): The media object to extract duration information from.
|
||||||
|
*_args (Any): Additional arguments (not used).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str | None: The formatted duration or None if the format string is not duration-related.
|
||||||
|
"""
|
||||||
|
# Format track durations
|
||||||
|
if name == "track_duration_seconds" and isinstance(media, Track | Video):
|
||||||
|
return str(media.duration)
|
||||||
|
elif name == "track_duration_minutes" and isinstance(media, Track | Video):
|
||||||
|
m, s = divmod(media.duration, 60)
|
||||||
|
return f"{m:01d}:{s:02d}"
|
||||||
|
|
||||||
|
# Format album durations
|
||||||
|
elif name == "album_duration_seconds" and isinstance(media, Album):
|
||||||
|
return str(media.duration)
|
||||||
|
elif name == "album_duration_minutes" and isinstance(media, Album):
|
||||||
|
m, s = divmod(media.duration, 60)
|
||||||
|
return f"{m:01d}:{s:02d}"
|
||||||
|
|
||||||
|
# Format playlist durations
|
||||||
|
elif name == "playlist_duration_seconds" and isinstance(media, Album):
|
||||||
|
return str(media.duration)
|
||||||
|
elif name == "playlist_duration_minutes" and isinstance(media, Album):
|
||||||
|
m, s = divmod(media.duration, 60)
|
||||||
|
return f"{m:01d}:{s:02d}"
|
||||||
|
|
||||||
|
# Format year
|
||||||
|
elif name == "album_year":
|
||||||
|
if isinstance(media, Album):
|
||||||
|
return str(media.year)
|
||||||
|
elif isinstance(media, Track):
|
||||||
|
return str(media.album.year)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _format_metadata(
|
||||||
|
name: str,
|
||||||
|
media: Track | Album | Playlist | UserPlaylist | Video | Mix,
|
||||||
|
*_args,
|
||||||
|
) -> str | None:
|
||||||
|
"""Handle metadata-related format strings.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name (str): The format string name to check.
|
||||||
|
media (Track | Album | Playlist | UserPlaylist | Video | Mix): The media object to extract metadata information from.
|
||||||
|
*_args (Any): Additional arguments (not used).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str | None: The formatted metadata or None if the format string is not metadata-related.
|
||||||
|
"""
|
||||||
|
if name == "video_quality" and isinstance(media, Video):
|
||||||
|
return media.video_quality
|
||||||
|
elif name == "track_quality" and isinstance(media, Track):
|
||||||
|
return ", ".join(tag for tag in media.media_metadata_tags if tag is not None)
|
||||||
|
elif (name == "track_explicit" and isinstance(media, Track | Video)) or (
|
||||||
|
name == "album_explicit" and isinstance(media, Album)
|
||||||
|
):
|
||||||
|
return FORMAT_TEMPLATE_EXPLICIT if media.explicit else ""
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _format_volumes(
|
||||||
|
name: str,
|
||||||
|
media: Track | Album | Playlist | UserPlaylist | Video | Mix,
|
||||||
|
*_args,
|
||||||
|
) -> str | None:
|
||||||
|
"""Handle volume-related format strings.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name (str): The format string name to check.
|
||||||
|
media (Track | Album | Playlist | UserPlaylist | Video | Mix): The media object to extract volume information from.
|
||||||
|
*_args (Any): Additional arguments (not used).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str | None: The formatted volume information or None if the format string is not volume-related.
|
||||||
|
"""
|
||||||
|
if name == "album_num_volumes" and isinstance(media, Album):
|
||||||
|
return str(media.num_volumes)
|
||||||
|
elif name == "track_volume_num" and isinstance(media, Track | Video):
|
||||||
|
return str(media.volume_num)
|
||||||
|
elif name == "track_volume_num_optional" and isinstance(media, Track | Video):
|
||||||
|
num_volumes: int = media.album.num_volumes if hasattr(media, "album") else 1
|
||||||
|
return "" if num_volumes == 1 else str(media.volume_num)
|
||||||
|
elif name == "track_volume_num_optional_CD" and isinstance(media, Track | Video):
|
||||||
|
num_volumes: int = media.album.num_volumes if hasattr(media, "album") else 1
|
||||||
|
return "" if num_volumes == 1 else f"CD{media.volume_num!s}"
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def calculate_number_padding(padding_minimum: int, item_position: int, items_max: int) -> str:
|
def calculate_number_padding(padding_minimum: int, item_position: int, items_max: int) -> str:
|
||||||
@@ -350,12 +513,12 @@ def path_file_uniquify(path_file: pathlib.Path) -> pathlib.Path:
|
|||||||
return path_file
|
return path_file
|
||||||
|
|
||||||
|
|
||||||
def file_unique_suffix(path_file: pathlib.Path, seperator: str = "_") -> str:
|
def file_unique_suffix(path_file: pathlib.Path, separator: str = "_") -> str:
|
||||||
"""Generate a unique suffix for a file path.
|
"""Generate a unique suffix for a file path.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
path_file (pathlib.Path): The file path to check for uniqueness.
|
path_file (pathlib.Path): The file path to check for uniqueness.
|
||||||
seperator (str, optional): The separator to use for the suffix. Defaults to "_".
|
separator (str, optional): The separator to use for the suffix. Defaults to "_".
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: The unique suffix, or an empty string if not needed.
|
str: The unique suffix, or an empty string if not needed.
|
||||||
@@ -367,7 +530,7 @@ def file_unique_suffix(path_file: pathlib.Path, seperator: str = "_") -> str:
|
|||||||
|
|
||||||
while check_file_exists(path_file_tmp) and count < UNIQUIFY_THRESHOLD:
|
while check_file_exists(path_file_tmp) and count < UNIQUIFY_THRESHOLD:
|
||||||
count += 1
|
count += 1
|
||||||
unique_suffix = seperator + str(count).zfill(threshold_zfill)
|
unique_suffix = separator + str(count).zfill(threshold_zfill)
|
||||||
path_file_tmp = path_file.parent / (path_file.stem + unique_suffix + path_file.suffix)
|
path_file_tmp = path_file.parent / (path_file.stem + unique_suffix + path_file.suffix)
|
||||||
|
|
||||||
return unique_suffix
|
return unique_suffix
|
||||||
@@ -386,31 +549,26 @@ def check_file_exists(path_file: pathlib.Path, extension_ignore: bool = False) -
|
|||||||
if extension_ignore:
|
if extension_ignore:
|
||||||
path_file_stem: str = pathlib.Path(path_file).stem
|
path_file_stem: str = pathlib.Path(path_file).stem
|
||||||
path_parent: pathlib.Path = pathlib.Path(path_file).parent
|
path_parent: pathlib.Path = pathlib.Path(path_file).parent
|
||||||
path_files: [str] = []
|
path_files: list[str] = []
|
||||||
|
|
||||||
for extension in AudioExtensions:
|
path_files.extend(str(path_parent.joinpath(path_file_stem + extension)) for extension in AudioExtensions)
|
||||||
path_files.append(str(path_parent.joinpath(path_file_stem + extension)))
|
|
||||||
else:
|
else:
|
||||||
path_files: [str] = [path_file]
|
path_files: list[str] = [str(path_file)]
|
||||||
|
|
||||||
result = any(os.path.isfile(_file) for _file in path_files)
|
return any(os.path.isfile(_file) for _file in path_files)
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def resource_path(relative_path):
|
def resource_path(relative_path: str) -> str:
|
||||||
"""Get the absolute path to a resource.
|
"""Get the absolute path to a resource.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
relative_path (str): The relative path to the resource.
|
relative_path: The relative path to the resource.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: The absolute path to the resource.
|
str: The absolute path to the resource.
|
||||||
"""
|
"""
|
||||||
try:
|
# PyInstaller creates a temp folder and stores path in _MEIPASS
|
||||||
base_path = sys._MEIPASS
|
base_path = getattr(sys, "_MEIPASS", os.path.abspath("."))
|
||||||
except Exception:
|
|
||||||
base_path = os.path.abspath(".")
|
|
||||||
|
|
||||||
return os.path.join(base_path, relative_path)
|
return os.path.join(base_path, relative_path)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user