import math from pathlib import Path from mutagen.mp3 import MP3 from mutagen.flac import FLAC from datetime import datetime from dateutil.parser import parse as dateparse class Track: def __init__( self, file: Path, bitrate: int = None, file_size: int = None, creation_date: int = None, modification_date: int = None, length: int = None, title: str = None, artists: list[str] = None, album: str = None, genre: str = None, bpm: int = None, release_date: datetime = None, label: str = None, cover: bytes = None, filetype: str = None, explicit: bool = False, engine_id: int = None, ): self.file = file self.bitrate = bitrate self.file_size = file_size self.creation_date = creation_date self.modification_date = modification_date self.length = length self.title = title self.artists = artists self.album = album self.genre = genre self.bpm = bpm self.release_date = release_date self.label = label self.cover = cover self.filetype = filetype self.explicit = explicit self.engine_id = engine_id def fill_empty(self): if self.title is None: self.title = "" if self.artists is None: self.artists = [""] if self.album is None: self.album = "" if self.genre is None: self.genre = "" if self.release_date is None: self.release_date = datetime.now() if self.label is None: self.label = "" if self.explicit is None: self.explicit = False def load_from_metadata(self): """ Try to fill up non-defined fields from file metadata. """ if self.file.suffix == ".flac": self.load_from_metadata_flac() def load_from_metadata_flac(self): """ Try to fill up non-defined fields from file metadata. Loads the fields from a FLAC file. """ tags = FLAC(self.file) if self.bitrate is None: self.bitrate = int(tags.info.bitrate / 1000) if self.file_size is None: self.file_size = self.file.stat().st_size if self.creation_date is None: self.creation_date = int(self.file.stat().st_ctime) if self.modification_date is None: self.modification_date = int(self.file.stat().st_mtime) if self.length is None: self.length = int(math.ceil(tags.info.length)) if self.title is None: try: self.title = tags["title"][0] except KeyError: pass if self.artists is None: try: self.artists = tags["artist"] except KeyError: pass if self.album is None: try: self.album = tags["album"][0] except KeyError: pass if self.genre is None: try: self.genre = tags["genre"][0] except KeyError: pass if self.bpm is None: try: self.bpm = int(tags["bpm"][0]) except KeyError: pass if self.release_date is None: try: self.release_date = dateparse(tags["date"][0]) except KeyError: pass if self.label is None: try: self.label = tags["publisher"][0] except KeyError: pass if self.cover is None: pictures = tags.pictures if pictures is not None and len(pictures) > 0: self.cover = tags.pictures[0].data if self.explicit is None: try: self.explicit = bool(tags["itunesadvisory"]) except KeyError: self.explicit = False def __str__(self) -> str: st = ( "'" + self.title + "' by " + ", ".join(self.artists) + " in album '" + self.album + "', released " + str(self.release_date.date()) ) if self.label is not None: st += " by " + self.label + ". " else: st += ". " st += "Song is " if self.bpm is not None: st += "at " + str(self.bpm) + " BPM, " st += "Explicit " + str(self.explicit) if self.genre is not None: st += ", Genre " + self.genre st += ( ", Runtime " + str(self.length) + ", Bitrate " + str(self.bitrate) + ", File size " + str(self.file_size) + ", Created " + str(self.creation_date) + ", Last modified " + str(self.modification_date) ) if self.engine_id is not None: st += ", Engine ID " + str(self.engine_id) if self.cover is None: st += ", No cover." else: st += ", Cover attached." return st