class TreeWidgetItemData: def __init__(self, item_type, songs=None, **kwargs): # metadata values self.metadata = None # application values # always strings self.dict = {} app_fields = (InputField.SONG_FIELDS if item_type == TreeWidgetType.SONG else InputField.ALBUM_FIELDS) for field in set(kwargs.keys()) | app_fields: # set all mandatory settings to their defaults if not # specified in the parameters # and any extra settings specified in the parameters if field in kwargs: self.dict[field] = kwargs[field] else: # set to default setting self.dict[field] = get_setting(field) if field == "coverArt" and self.dict[field] in APPLICATION_IMAGES: # convert resource path to real file path for ffmpeg self.dict[field] = APPLICATION_IMAGES[get_setting(field)] # add song metadata if item_type == TreeWidgetType.SONG: try: self.metadata = Metadata(self.dict["song_path"]) cover_exts = {".jpg", ".jpeg", ".bmp", ".gif", ".png"} cover_names = { "cover", "folder", "front", os.path.splitext(self.dict["song_file"])[0], } cover_file = None for file in os.listdir(self.dict["song_dir"]): path = posixpath.join(self.dict["song_dir"], file) name, ext = os.path.splitext(file) if (os.path.isfile(path) and name.lower() in cover_names and ext.lower() in cover_exts): logger.info(f"Found cover file {path}") cover_file = path break if (get_setting("preferCoverArtFile") == SETTINGS_VALUES.CheckBox.CHECKED and cover_file): self.set_value("coverArt", cover_file) elif get_setting( "extractCoverArt") == SETTINGS_VALUES.CheckBox.CHECKED: if (cover_path := self.metadata.get_cover_art()) is not None: self.set_value("coverArt", cover_path) elif cover_file: self.set_value("coverArt", cover_file) except Exception as e: logger.warning("Error while getting cover art") logger.warning(e) logger.warning(self.dict["song_path"]) else: # album gets metadata from children # song metadata is stored as song.<key> # e.g. song.album would be the album name # # we will only get metadata from one song # because the album shouldn't care about # the varying metadata values for the songs # such as title or track number for song in songs: for key, value in song.to_dict().items(): key = "song.{}".format(key) self.dict[key] = value break self.update_fields() def update_fields(self): for field, value in self.dict.items(): self.set_value(field, value) def to_dict(self): dict = { **self.dict, **(self.metadata.get_tags() if self.metadata is not None else {}), } return dict def get_value(self, field): return self.dict[field] def get_metadata_value(self, key): if key in self.metadata.get_tags(): return self.metadata.get_tags()[key] return None def set_value(self, field, value): # replace {variable} with value from metadata value = SettingTemplate(value).safe_substitute(**self.to_dict()) self.dict[field] = value def get_duration_ms(self): if "length" in self.metadata.get_tags(): return float(self.metadata.get_tags()["length"]) * 1000 else: logger.error("Could not find duration of file {}".format( self.dict["song_path"])) logger.debug(self.metadata.get_tags()) return 999999999 def get_track_number(self): if "tracknumber" in self.metadata.get_tags(): try: tracknumber = self.metadata.get_tags()["tracknumber"] if "/" in tracknumber: # sometimes track number is represented as a fraction tracknumber = tracknumber[:tracknumber.index("/")] return int(tracknumber) except: logger.warning("Could not convert {} to int".format( self.metadata.get_tags()["tracknumber"])) return 0 return 0 def __str__(self): return str(self.dict)