diff --git a/music_kraken/objects/collection.py b/music_kraken/objects/collection.py index 255caaa..9fd9f90 100644 --- a/music_kraken/objects/collection.py +++ b/music_kraken/objects/collection.py @@ -2,6 +2,8 @@ from __future__ import annotations from collections import defaultdict from typing import TypeVar, Generic, Dict, Optional, Iterable, List, Iterator, Tuple, Generator, Union, Any, Set +import copy + from .parents import OuterProxy from ..utils import object_trace from ..utils import output, BColors @@ -47,8 +49,15 @@ class Collection(Generic[T]): self.extend(data) + def __hash__(self) -> int: + return id(self) + + @property + def collection_names(self) -> List[str]: + return list(set(self._collection_for.values())) + def __repr__(self) -> str: - return f"Collection({' | '.join(self._collection_for.values())} {id(self)})" + return f"Collection({' | '.join(self.collection_names)} {id(self)})" def _map_element(self, __object: T, no_unmap: bool = False, **kwargs): if not no_unmap: @@ -104,8 +113,9 @@ class Collection(Generic[T]): """ self._data.append(other) + other._inner._is_in_collection.add(self) - # all of the existing hooks to get the defined datastructure + # all of the existing hooks to get the defined datastructures for collection_attribute, generator in self.extend_object_to_attribute.items(): other.__getattribute__(collection_attribute).extend(generator, **kwargs) @@ -148,32 +158,28 @@ class Collection(Generic[T]): object_trace(f"Appending {other.option_string} to {self}") - - for c in self.pull_from: - r = c._find_object(other) - if r is not None: - output("found pull from", r, other, self, color=BColors.RED, sep="\t") - other.merge(r, **kwargs) - c.remove(r, existing=r, **kwargs) - break - - existing_object = self._find_object(other) - # switching collection in the case of push to for c in self.push_to: r = c._find_object(other) if r is not None: - output("found push to", r, other, self, color=BColors.RED, sep="\t") - if existing_object is not None: - self.remove(existing_object) + # output("found push to", r, other, c, self, color=BColors.RED, sep="\t") return c.append(other, **kwargs) + + for c in self.pull_from: + r = c._find_object(other) + if r is not None: + # output("found pull from", r, other, c, self, color=BColors.RED, sep="\t") + c.remove(r, existing=r, **kwargs) - if existing_object is None: + existing = self._find_object(other) + + if existing is None: self._append_new_object(other, **kwargs) else: - existing_object.merge(other, **kwargs) + existing.merge(other, **kwargs) - def remove(self, *other_list: List[T], silent: bool = False, existing: Optional[T] = None, **kwargs): + def remove(self, *other_list: List[T], silent: bool = False, existing: Optional[T] = None, remove_from_other_collection=True, **kwargs): + other: T for other in other_list: existing: Optional[T] = existing or self._indexed_values["id"].get(other.id, None) if existing is None: @@ -181,14 +187,13 @@ class Collection(Generic[T]): raise ValueError(f"Object {other} not found in {self}") return other - for collection_attribute, generator in self.extend_object_to_attribute.items(): - other.__getattribute__(collection_attribute).remove(*generator, silent=True, **kwargs) - - for attribute, new_object in self.append_object_to_attribute.items(): - other.__getattribute__(attribute).remove(new_object, silent=True, **kwargs) - - self._data.remove(existing) - self._unmap_element(existing) + if remove_from_other_collection: + for c in copy.copy(other._inner._is_in_collection): + c.remove(other, silent=True, remove_from_other_collection=False, **kwargs) + other._inner._is_in_collection = set() + else: + self._data.remove(existing) + self._unmap_element(existing) def contains(self, __object: T) -> bool: return self._find_object(__object) is not None diff --git a/music_kraken/objects/parents.py b/music_kraken/objects/parents.py index a79887a..b4f867a 100644 --- a/music_kraken/objects/parents.py +++ b/music_kraken/objects/parents.py @@ -29,12 +29,15 @@ class InnerData: """ _refers_to_instances: set = None + _is_in_collection: set = None """ Attribute versions keep track, of if the attribute has been changed. """ def __init__(self, object_type, **kwargs): self._refers_to_instances = set() + self._is_in_collection = set() + self._fetched_from: dict = {} # initialize the default values @@ -58,6 +61,7 @@ class InnerData: """ self._fetched_from.update(__other._fetched_from) + self._is_in_collection.update(__other._is_in_collection) for key, value in __other.__dict__.copy().items(): if key.startswith("_"): diff --git a/music_kraken/objects/song.py b/music_kraken/objects/song.py index fb4efc3..33f68a0 100644 --- a/music_kraken/objects/song.py +++ b/music_kraken/objects/song.py @@ -222,17 +222,9 @@ class Song(Base): r = OPTION_FOREGROUND.value + self.title_string + BColors.ENDC.value + OPTION_BACKGROUND.value r += get_collection_string(self.album_collection, " from {}", ignore_titles={self.title}) r += get_collection_string(self.main_artist_collection, " by {}") - r += get_collection_string(self.feature_artist_collection, " feat. {}" if not self.main_artist_collection.empty or True else " by {}") + r += get_collection_string(self.feature_artist_collection, " feat. {}") return r - @property - def options(self) -> List[P]: - options = self.main_artist_collection.shallow_list - options.extend(self.feature_artist_collection) - options.extend(self.album_collection) - options.append(self) - return options - @property def tracksort_str(self) -> str: """ @@ -260,7 +252,6 @@ class Album(Base): barcode: str albumsort: int notes: FormattedText - artwork: Artwork source_collection: SourceCollection @@ -279,7 +270,6 @@ class Album(Base): "language": lambda: Language.by_alpha_2("en"), "date": ID3Timestamp, "notes": FormattedText, - "artwork": Artwork, "source_collection": SourceCollection, "artist_collection": Collection,