Start comprehensive typing

This commit is contained in:
nathom 2021-04-28 00:24:17 -07:00
parent dad58d8d22
commit e6a5d2cd39
8 changed files with 157 additions and 115 deletions

View file

@ -2,7 +2,7 @@
import logging
import re
from collections import OrderedDict
from typing import Generator, Hashable, Optional, Tuple, Union
from typing import Generator, Hashable, Iterable, Optional, Union
from .constants import (
COPYRIGHT,
@ -59,34 +59,37 @@ class TrackMetadata:
:type album: Optional[dict]
"""
# embedded information
self.title = None
self.album = None
self.albumartist = None
self.composer = None
self.comment = None
self.description = None
self.purchase_date = None
self.grouping = None
self.lyrics = None
self.encoder = None
self.compilation = None
self.cover = None
self.tracktotal = None
self.tracknumber = None
self.discnumber = None
self.disctotal = None
self.title: str
self.album: str
self.albumartist: str
self.composer: str
self.comment: Optional[str]
self.description: Optional[str]
self.purchase_date: Optional[str]
self.grouping: Optional[str]
self.lyrics: Optional[str]
self.encoder: Optional[str]
self.compilation: Optional[str]
self.cover: str
self.tracktotal: int
self.tracknumber: int
self.discnumber: int
self.disctotal: int
# not included in tags
self.explicit = False
self.quality = None
self.sampling_rate = None
self.bit_depth = None
self.explicit: Optional[bool] = False
self.quality: Optional[int] = None
self.sampling_rate: Optional[int] = None
self.bit_depth: Optional[int] = None
self.booklets = None
self.cover_urls = Optional[OrderedDict]
self.work: Optional[str]
self.id: Optional[str]
# Internals
self._artist = None
self._copyright = None
self._genres = None
self._artist: Optional[str] = None
self._copyright: Optional[str] = None
self._genres: Optional[Iterable] = None
self.__source = source
@ -121,7 +124,7 @@ class TrackMetadata:
"""
if self.__source == "qobuz":
# Tags
self.album = resp.get("title")
self.album = resp.get("title", "Unknown Album")
self.tracktotal = resp.get("tracks_count", 1)
self.genre = resp.get("genres_list") or resp.get("genre")
self.date = resp.get("release_date_original") or resp.get("release_date")
@ -144,7 +147,7 @@ class TrackMetadata:
# Non-embedded information
self.version = resp.get("version")
self.cover_urls = OrderedDict(resp.get("image"))
self.cover_urls = OrderedDict(resp["image"])
self.cover_urls["original"] = self.cover_urls["large"].replace("600", "org")
self.streamable = resp.get("streamable", False)
self.bit_depth = resp.get("maximum_bit_depth")
@ -156,14 +159,14 @@ class TrackMetadata:
self.sampling_rate *= 1000
elif self.__source == "tidal":
self.album = resp.get("title")
self.album = resp.get("title", "Unknown Album")
self.tracktotal = resp.get("numberOfTracks", 1)
# genre not returned by API
self.date = resp.get("releaseDate")
self.copyright = resp.get("copyright")
self.albumartist = safe_get(resp, "artist", "name")
self.disctotal = resp.get("numberOfVolumes")
self.disctotal = resp.get("numberOfVolumes", 1)
self.isrc = resp.get("isrc")
# label not returned by API
@ -185,8 +188,8 @@ class TrackMetadata:
self.sampling_rate = 44100
elif self.__source == "deezer":
self.album = resp.get("title")
self.tracktotal = resp.get("track_total") or resp.get("nb_tracks")
self.album = resp.get("title", "Unknown Album")
self.tracktotal = resp.get("track_total", 0) or resp.get("nb_tracks", 0)
self.disctotal = (
max(track.get("disk_number") for track in resp.get("tracks", [{}])) or 1
)
@ -224,7 +227,7 @@ class TrackMetadata:
:param track:
"""
if self.__source == "qobuz":
self.title = track.get("title").strip()
self.title = track["title"].strip()
self._mod_title(track.get("version"), track.get("work"))
self.composer = track.get("composer", {}).get("name")
@ -235,24 +238,23 @@ class TrackMetadata:
self.artist = self.get("albumartist")
elif self.__source == "tidal":
self.title = track.get("title").strip()
self.title = track["title"].strip()
self._mod_title(track.get("version"), None)
self.tracknumber = track.get("trackNumber", 1)
self.discnumber = track.get("volumeNumber")
self.discnumber = track.get("volumeNumber", 1)
self.artist = track.get("artist", {}).get("name")
elif self.__source == "deezer":
self.title = track.get("title").strip()
self.title = track["title"].strip()
self._mod_title(track.get("version"), None)
self.tracknumber = track.get("track_position", 1)
self.discnumber = track.get("disk_number")
self.discnumber = track.get("disk_number", 1)
self.artist = track.get("artist", {}).get("name")
elif self.__source == "soundcloud":
self.title = track["title"].strip()
self.genre = track["genre"]
self.artist = track["user"]["username"]
self.albumartist = self.artist
self.artist = self.albumartist = track["user"]["username"]
self.year = track["created_at"][:4]
self.label = track["label_name"]
self.description = track["description"]
@ -287,7 +289,7 @@ class TrackMetadata:
return album
@album.setter
def album(self, val) -> str:
def album(self, val):
self._album = val
@property
@ -331,7 +333,7 @@ class TrackMetadata:
if isinstance(self._genres, list):
if self.__source == "qobuz":
genres = re.findall(r"([^\u2192\/]+)", "/".join(self._genres))
genres: Iterable = re.findall(r"([^\u2192\/]+)", "/".join(self._genres))
genres = set(genres)
return ", ".join(genres)
@ -342,7 +344,7 @@ class TrackMetadata:
raise TypeError(f"Genre must be list or str, not {type(self._genres)}")
@genre.setter
def genre(self, val: Union[str, list]):
def genre(self, val: Union[Iterable, dict]):
"""Sets the internal `genre` field to the given list.
It is not formatted until it is requested with `meta.genre`.
@ -352,7 +354,7 @@ class TrackMetadata:
self._genres = val
@property
def copyright(self) -> Union[str, None]:
def copyright(self) -> Optional[str]:
"""Formats the copyright string to use nice-looking unicode
characters.
@ -361,11 +363,11 @@ class TrackMetadata:
if hasattr(self, "_copyright"):
if self._copyright is None:
return None
copyright = re.sub(r"(?i)\(P\)", PHON_COPYRIGHT, self._copyright)
copyright: str = re.sub(r"(?i)\(P\)", PHON_COPYRIGHT, self._copyright)
copyright = re.sub(r"(?i)\(C\)", COPYRIGHT, copyright)
return copyright
logger.debug("Accessed copyright tag before setting, return None")
logger.debug("Accessed copyright tag before setting, returning None")
return None
@copyright.setter
@ -440,7 +442,7 @@ class TrackMetadata:
raise InvalidContainerError(f"Invalid container {container}")
def __gen_flac_tags(self) -> Tuple[str, str]:
def __gen_flac_tags(self) -> Generator:
"""Generate key, value pairs to tag FLAC files.
:rtype: Tuple[str, str]
@ -454,7 +456,7 @@ class TrackMetadata:
logger.debug("Adding tag %s: %s", v, tag)
yield (v, str(tag))
def __gen_mp3_tags(self) -> Tuple[str, str]:
def __gen_mp3_tags(self) -> Generator:
"""Generate key, value pairs to tag MP3 files.
:rtype: Tuple[str, str]
@ -470,7 +472,7 @@ class TrackMetadata:
if text is not None and v is not None:
yield (v.__name__, v(encoding=3, text=text))
def __gen_mp4_tags(self) -> Tuple[str, Union[str, int, tuple]]:
def __gen_mp4_tags(self) -> Generator:
"""Generate key, value pairs to tag ALAC or AAC files in
an MP4 container.
@ -510,7 +512,7 @@ class TrackMetadata:
"""
return getattr(self, key)
def get(self, key, default=None) -> str:
def get(self, key, default=None):
"""Returns the requested attribute of the object, with
a default value.