refactor
This commit is contained in:
		@@ -84,7 +84,8 @@ class Collection:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        return self._data[item]
 | 
					        return self._data[item]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def copy(self) -> List:
 | 
					    @property
 | 
				
			||||||
 | 
					    def shallow_list(self) -> List[DatabaseObject]:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        returns a shallow copy of the data list
 | 
					        returns a shallow copy of the data list
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,12 +3,12 @@ from typing import List
 | 
				
			|||||||
import pycountry
 | 
					import pycountry
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from .parents import DatabaseObject
 | 
					from .parents import DatabaseObject
 | 
				
			||||||
from .source import SourceAttribute, Source
 | 
					from .source import Source, SourceCollection
 | 
				
			||||||
from .metadata import MetadataAttribute
 | 
					from .metadata import Metadata
 | 
				
			||||||
from .formatted_text import FormattedText
 | 
					from .formatted_text import FormattedText
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Lyrics(DatabaseObject, SourceAttribute, MetadataAttribute):
 | 
					class Lyrics(DatabaseObject):
 | 
				
			||||||
    def __init__(
 | 
					    def __init__(
 | 
				
			||||||
            self,
 | 
					            self,
 | 
				
			||||||
            text: FormattedText,
 | 
					            text: FormattedText,
 | 
				
			||||||
@@ -23,5 +23,4 @@ class Lyrics(DatabaseObject, SourceAttribute, MetadataAttribute):
 | 
				
			|||||||
        self.text: FormattedText = text
 | 
					        self.text: FormattedText = text
 | 
				
			||||||
        self.language: pycountry.Languages = language
 | 
					        self.language: pycountry.Languages = language
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if source_list is not None:
 | 
					        self.source_collection: SourceCollection = SourceCollection(source_list)
 | 
				
			||||||
            self.source_list = source_list
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,6 @@
 | 
				
			|||||||
from enum import Enum
 | 
					from enum import Enum
 | 
				
			||||||
from typing import List, Dict, Tuple
 | 
					from typing import List, Dict, Tuple
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import dateutil.tz
 | 
					 | 
				
			||||||
from mutagen import id3
 | 
					from mutagen import id3
 | 
				
			||||||
import datetime
 | 
					import datetime
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -267,126 +266,113 @@ class ID3Timestamp:
 | 
				
			|||||||
    timeformat: str = property(fget=get_time_format)
 | 
					    timeformat: str = property(fget=get_time_format)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class MetadataAttribute:
 | 
					class Metadata:
 | 
				
			||||||
    """
 | 
					    # it's a null byte for the later concatenation of text frames
 | 
				
			||||||
    This class shall be added to any object, which can return data for tagging
 | 
					    NULL_BYTE: str = "\x00"
 | 
				
			||||||
    """
 | 
					    # this is pretty self-explanatory
 | 
				
			||||||
 | 
					    # the key is an enum from Mapping
 | 
				
			||||||
 | 
					    # the value is a list with each value
 | 
				
			||||||
 | 
					    # the mutagen object for each frame will be generated dynamically
 | 
				
			||||||
 | 
					    id3_dict: Dict[any, list]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    class Metadata:
 | 
					    def __init__(self, id3_dict: Dict[any, list] = None) -> None:
 | 
				
			||||||
        # it's a null byte for the later concatenation of text frames
 | 
					        self.id3_dict = dict()
 | 
				
			||||||
        NULL_BYTE: str = "\x00"
 | 
					        if id3_dict is not None:
 | 
				
			||||||
        # this is pretty self-explanatory
 | 
					            self.add_metadata_dict(id3_dict)
 | 
				
			||||||
        # the key is an enum from Mapping
 | 
					 | 
				
			||||||
        # the value is a list with each value
 | 
					 | 
				
			||||||
        # the mutagen object for each frame will be generated dynamically
 | 
					 | 
				
			||||||
        id3_dict: Dict[any, list]
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        def __init__(self, id3_dict: Dict[any, list] = None) -> None:
 | 
					    def __setitem__(self, frame, value_list: list, override_existing: bool = True):
 | 
				
			||||||
            self.id3_dict = dict()
 | 
					        if type(value_list) != list:
 | 
				
			||||||
            if id3_dict is not None:
 | 
					            raise ValueError(f"can only set attribute to list, not {type(value_list)}")
 | 
				
			||||||
                self.add_metadata_dict(id3_dict)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        def __setitem__(self, frame, value_list: list, override_existing: bool = True):
 | 
					        new_val = [i for i in value_list if i not in {None, ''}]
 | 
				
			||||||
            if type(value_list) != list:
 | 
					 | 
				
			||||||
                raise ValueError(f"can only set attribute to list, not {type(value_list)}")
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            new_val = [i for i in value_list if i not in {None, ''}]
 | 
					        if len(new_val) == 0:
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if len(new_val) == 0:
 | 
					        if override_existing:
 | 
				
			||||||
 | 
					            self.id3_dict[frame] = new_val
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            if frame not in self.id3_dict:
 | 
				
			||||||
 | 
					                self.id3_dict[frame] = new_val
 | 
				
			||||||
                return
 | 
					                return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if override_existing:
 | 
					            self.id3_dict[frame].extend(new_val)
 | 
				
			||||||
                self.id3_dict[frame] = new_val
 | 
					 | 
				
			||||||
            else:
 | 
					 | 
				
			||||||
                if frame not in self.id3_dict:
 | 
					 | 
				
			||||||
                    self.id3_dict[frame] = new_val
 | 
					 | 
				
			||||||
                    return
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                self.id3_dict[frame].extend(new_val)
 | 
					    def __getitem__(self, key):
 | 
				
			||||||
 | 
					        if key not in self.id3_dict:
 | 
				
			||||||
 | 
					            return None
 | 
				
			||||||
 | 
					        return self.id3_dict[key]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        def __getitem__(self, key):
 | 
					    def delete_field(self, key: str):
 | 
				
			||||||
            if key not in self.id3_dict:
 | 
					        if key in self.id3_dict:
 | 
				
			||||||
                return None
 | 
					            return self.id3_dict.pop(key)
 | 
				
			||||||
            return self.id3_dict[key]
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        def delete_field(self, key: str):
 | 
					    def add_metadata_dict(self, metadata_dict: dict, override_existing: bool = True):
 | 
				
			||||||
            if key in self.id3_dict:
 | 
					        for field_enum, value in metadata_dict.items():
 | 
				
			||||||
                return self.id3_dict.pop(key)
 | 
					            self.__setitem__(field_enum, value, override_existing=override_existing)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        def add_metadata_dict(self, metadata_dict: dict, override_existing: bool = True):
 | 
					    def merge(self, other, override_existing: bool = False):
 | 
				
			||||||
            for field_enum, value in metadata_dict.items():
 | 
					 | 
				
			||||||
                self.__setitem__(field_enum, value, override_existing=override_existing)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        def merge(self, other, override_existing: bool = False):
 | 
					 | 
				
			||||||
            """
 | 
					 | 
				
			||||||
            adds the values of another metadata obj to this one
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            other is a value of the type MetadataAttribute.Metadata
 | 
					 | 
				
			||||||
            """
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            self.add_metadata_dict(other.id3_dict, override_existing=override_existing)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        def merge_many(self, many_other):
 | 
					 | 
				
			||||||
            """
 | 
					 | 
				
			||||||
            adds the values of many other metadata objects to this one
 | 
					 | 
				
			||||||
            """
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            for other in many_other:
 | 
					 | 
				
			||||||
                self.merge(other)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        def get_id3_value(self, field):
 | 
					 | 
				
			||||||
            if field not in self.id3_dict:
 | 
					 | 
				
			||||||
                return None
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            list_data = self.id3_dict[field]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            # convert for example the time objects to timestamps
 | 
					 | 
				
			||||||
            for i, element in enumerate(list_data):
 | 
					 | 
				
			||||||
                # for performance’s sake I don't do other checks if it is already the right type
 | 
					 | 
				
			||||||
                if type(element) == str:
 | 
					 | 
				
			||||||
                    continue
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if type(element) in {int}:
 | 
					 | 
				
			||||||
                    list_data[i] = str(element)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if type(element) == ID3Timestamp:
 | 
					 | 
				
			||||||
                    list_data[i] = element.timestamp
 | 
					 | 
				
			||||||
                    continue
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            """
 | 
					 | 
				
			||||||
            Version 2.4 of the specification prescribes that all text fields (the fields that start with a T, except for TXXX) can contain multiple values separated by a null character. 
 | 
					 | 
				
			||||||
            Thus if above conditions are met, I concatenate the list,
 | 
					 | 
				
			||||||
            else I take the first element
 | 
					 | 
				
			||||||
            """
 | 
					 | 
				
			||||||
            if field.value[0].upper() == "T" and field.value.upper() != "TXXX":
 | 
					 | 
				
			||||||
                return self.NULL_BYTE.join(list_data)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return list_data[0]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        def get_mutagen_object(self, field):
 | 
					 | 
				
			||||||
            return Mapping.get_mutagen_instance(field, self.get_id3_value(field))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        def __str__(self) -> str:
 | 
					 | 
				
			||||||
            rows = []
 | 
					 | 
				
			||||||
            for key, value in self.id3_dict.items():
 | 
					 | 
				
			||||||
                rows.append(f"{key} - {str(value)}")
 | 
					 | 
				
			||||||
            return "\n".join(rows)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        def __iter__(self):
 | 
					 | 
				
			||||||
            """
 | 
					 | 
				
			||||||
            returns a generator, you can iterate through,
 | 
					 | 
				
			||||||
            to directly tagg a file with id3 container.
 | 
					 | 
				
			||||||
            """
 | 
					 | 
				
			||||||
            # set the tagging timestamp to the current time
 | 
					 | 
				
			||||||
            self.__setitem__(Mapping.TAGGING_TIME, [ID3Timestamp.now()])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            for field in self.id3_dict:
 | 
					 | 
				
			||||||
                yield self.get_mutagen_object(field)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_metadata(self) -> Metadata:
 | 
					 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        this is intendet to be overwritten by the child class
 | 
					        adds the values of another metadata obj to this one
 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        return MetadataAttribute.Metadata()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    metadata = property(fget=lambda self: self.get_metadata())
 | 
					        other is a value of the type MetadataAttribute.Metadata
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.add_metadata_dict(other.id3_dict, override_existing=override_existing)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def merge_many(self, many_other):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        adds the values of many other metadata objects to this one
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for other in many_other:
 | 
				
			||||||
 | 
					            self.merge(other)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_id3_value(self, field):
 | 
				
			||||||
 | 
					        if field not in self.id3_dict:
 | 
				
			||||||
 | 
					            return None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        list_data = self.id3_dict[field]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # convert for example the time objects to timestamps
 | 
				
			||||||
 | 
					        for i, element in enumerate(list_data):
 | 
				
			||||||
 | 
					            # for performance’s sake I don't do other checks if it is already the right type
 | 
				
			||||||
 | 
					            if type(element) == str:
 | 
				
			||||||
 | 
					                continue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if type(element) in {int}:
 | 
				
			||||||
 | 
					                list_data[i] = str(element)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if type(element) == ID3Timestamp:
 | 
				
			||||||
 | 
					                list_data[i] = element.timestamp
 | 
				
			||||||
 | 
					                continue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Version 2.4 of the specification prescribes that all text fields (the fields that start with a T, except for TXXX) can contain multiple values separated by a null character. 
 | 
				
			||||||
 | 
					        Thus if above conditions are met, I concatenate the list,
 | 
				
			||||||
 | 
					        else I take the first element
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        if field.value[0].upper() == "T" and field.value.upper() != "TXXX":
 | 
				
			||||||
 | 
					            return self.NULL_BYTE.join(list_data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return list_data[0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_mutagen_object(self, field):
 | 
				
			||||||
 | 
					        return Mapping.get_mutagen_instance(field, self.get_id3_value(field))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __str__(self) -> str:
 | 
				
			||||||
 | 
					        rows = []
 | 
				
			||||||
 | 
					        for key, value in self.id3_dict.items():
 | 
				
			||||||
 | 
					            rows.append(f"{key} - {str(value)}")
 | 
				
			||||||
 | 
					        return "\n".join(rows)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __iter__(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        returns a generator, you can iterate through,
 | 
				
			||||||
 | 
					        to directly tagg a file with id3 container.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        # set the tagging timestamp to the current time
 | 
				
			||||||
 | 
					        self.__setitem__(Mapping.TAGGING_TIME, [ID3Timestamp.now()])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for field in self.id3_dict:
 | 
				
			||||||
 | 
					            yield self.get_mutagen_object(field)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,6 +5,7 @@ import uuid
 | 
				
			|||||||
from ..utils.shared import (
 | 
					from ..utils.shared import (
 | 
				
			||||||
    SONG_LOGGER as LOGGER
 | 
					    SONG_LOGGER as LOGGER
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					from .metadata import Metadata
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class DatabaseObject:
 | 
					class DatabaseObject:
 | 
				
			||||||
@@ -69,6 +70,10 @@ class DatabaseObject:
 | 
				
			|||||||
            if override or getattr(self, simple_attribute) is None:
 | 
					            if override or getattr(self, simple_attribute) is None:
 | 
				
			||||||
                setattr(self, simple_attribute, getattr(other, simple_attribute))
 | 
					                setattr(self, simple_attribute, getattr(other, simple_attribute))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def metadata(self) -> Metadata:
 | 
				
			||||||
 | 
					        return Metadata()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class MainObject(DatabaseObject):
 | 
					class MainObject(DatabaseObject):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,7 +5,7 @@ import pycountry
 | 
				
			|||||||
from .metadata import (
 | 
					from .metadata import (
 | 
				
			||||||
    Mapping as id3Mapping,
 | 
					    Mapping as id3Mapping,
 | 
				
			||||||
    ID3Timestamp,
 | 
					    ID3Timestamp,
 | 
				
			||||||
    MetadataAttribute
 | 
					    Metadata
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from ..utils.shared import (
 | 
					from ..utils.shared import (
 | 
				
			||||||
    MUSIC_DIR,
 | 
					    MUSIC_DIR,
 | 
				
			||||||
@@ -36,12 +36,10 @@ All Objects dependent
 | 
				
			|||||||
CountryTyping = type(list(pycountry.countries)[0])
 | 
					CountryTyping = type(list(pycountry.countries)[0])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Song(MainObject, MetadataAttribute):
 | 
					class Song(MainObject):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    Class representing a song object, with attributes id, mb_id, title, album_name, isrc, length,
 | 
					    Class representing a song object, with attributes id, mb_id, title, album_name, isrc, length,
 | 
				
			||||||
    tracksort, genre, source_list, target, lyrics_list, album, main_artist_list, and feature_artist_list.
 | 
					    tracksort, genre, source_list, target, lyrics_list, album, main_artist_list, and feature_artist_list.
 | 
				
			||||||
 | 
					 | 
				
			||||||
    Inherits from DatabaseObject, SourceAttribute, and MetadataAttribute classes.
 | 
					 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    COLLECTION_ATTRIBUTES = ("lyrics_collection", "album_collection", "main_artist_collection", "feature_artist_collection", "source_collection")
 | 
					    COLLECTION_ATTRIBUTES = ("lyrics_collection", "album_collection", "main_artist_collection", "feature_artist_collection", "source_collection")
 | 
				
			||||||
@@ -65,9 +63,6 @@ class Song(MainObject, MetadataAttribute):
 | 
				
			|||||||
            feature_artist_list: List[Type['Artist']] = None,
 | 
					            feature_artist_list: List[Type['Artist']] = None,
 | 
				
			||||||
            **kwargs
 | 
					            **kwargs
 | 
				
			||||||
    ) -> None:
 | 
					    ) -> None:
 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Initializes the Song object with the following attributes:
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        MainObject.__init__(self, _id=_id, dynamic=dynamic, **kwargs)
 | 
					        MainObject.__init__(self, _id=_id, dynamic=dynamic, **kwargs)
 | 
				
			||||||
        # attributes
 | 
					        # attributes
 | 
				
			||||||
        self.title: str = title
 | 
					        self.title: str = title
 | 
				
			||||||
@@ -90,6 +85,33 @@ class Song(MainObject, MetadataAttribute):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        self.feature_artist_collection = Collection(data=feature_artist_list, element_type=Artist)
 | 
					        self.feature_artist_collection = Collection(data=feature_artist_list, element_type=Artist)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def indexing_values(self) -> List[Tuple[str, object]]:
 | 
				
			||||||
 | 
					        return [
 | 
				
			||||||
 | 
					            ('id', self.id),
 | 
				
			||||||
 | 
					            ('title', self.unified_title),
 | 
				
			||||||
 | 
					            ('isrc', self.isrc.strip()),
 | 
				
			||||||
 | 
					            *[('url', source.url) for source in self.source_list]
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def metadata(self) -> Metadata:
 | 
				
			||||||
 | 
					        metadata = Metadata({
 | 
				
			||||||
 | 
					            id3Mapping.TITLE: [self.title],
 | 
				
			||||||
 | 
					            id3Mapping.ISRC: [self.isrc],
 | 
				
			||||||
 | 
					            id3Mapping.LENGTH: [self.length],
 | 
				
			||||||
 | 
					            id3Mapping.GENRE: [self.genre],
 | 
				
			||||||
 | 
					            id3Mapping.TRACKNUMBER: [self.tracksort_str]
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        metadata.merge_many([s.get_song_metadata() for s in self.source_list])
 | 
				
			||||||
 | 
					        metadata.merge_many([a.metadata for a in self.album_collection])
 | 
				
			||||||
 | 
					        metadata.merge_many([a.metadata for a in self.main_artist_collection])
 | 
				
			||||||
 | 
					        metadata.merge_many([a.metadata for a in self.feature_artist_collection])
 | 
				
			||||||
 | 
					        metadata.merge_many([lyrics.metadata for lyrics in self.lyrics_collection])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return metadata
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __eq__(self, other):
 | 
					    def __eq__(self, other):
 | 
				
			||||||
        if type(other) != type(self):
 | 
					        if type(other) != type(self):
 | 
				
			||||||
            return False
 | 
					            return False
 | 
				
			||||||
@@ -114,39 +136,14 @@ class Song(MainObject, MetadataAttribute):
 | 
				
			|||||||
    def __repr__(self) -> str:
 | 
					    def __repr__(self) -> str:
 | 
				
			||||||
        return f"Song(\"{self.title}\")"
 | 
					        return f"Song(\"{self.title}\")"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_tracksort_str(self):
 | 
					    @property
 | 
				
			||||||
 | 
					    def tracksort_str(self) -> str:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        if the album tracklist is empty, it sets it length to 1, this song has to be in the Album
 | 
					        if the album tracklist is empty, it sets it length to 1, this song has to be in the Album
 | 
				
			||||||
        :returns id3_tracksort: {song_position}/{album.length_of_tracklist} 
 | 
					        :returns id3_tracksort: {song_position}/{album.length_of_tracklist} 
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        return f"{self.tracksort}/{len(self.album.tracklist) or 1}"
 | 
					        return f"{self.tracksort}/{len(self.album.tracklist) or 1}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					 | 
				
			||||||
    def indexing_values(self) -> List[Tuple[str, object]]:
 | 
					 | 
				
			||||||
        return [
 | 
					 | 
				
			||||||
            ('id', self.id),
 | 
					 | 
				
			||||||
            ('title', self.unified_title),
 | 
					 | 
				
			||||||
            ('isrc', self.isrc.strip()),
 | 
					 | 
				
			||||||
            *[('url', source.url) for source in self.source_list]
 | 
					 | 
				
			||||||
        ]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_metadata(self) -> MetadataAttribute.Metadata:
 | 
					 | 
				
			||||||
        metadata = MetadataAttribute.Metadata({
 | 
					 | 
				
			||||||
            id3Mapping.TITLE: [self.title],
 | 
					 | 
				
			||||||
            id3Mapping.ISRC: [self.isrc],
 | 
					 | 
				
			||||||
            id3Mapping.LENGTH: [self.length],
 | 
					 | 
				
			||||||
            id3Mapping.GENRE: [self.genre],
 | 
					 | 
				
			||||||
            id3Mapping.TRACKNUMBER: [self.tracksort_str]
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        metadata.merge_many([s.get_song_metadata() for s in self.source_list])
 | 
					 | 
				
			||||||
        metadata.merge_many([a.metadata for a in self.album_collection])
 | 
					 | 
				
			||||||
        metadata.merge_many([a.metadata for a in self.main_artist_collection])
 | 
					 | 
				
			||||||
        metadata.merge_many([a.metadata for a in self.feature_artist_collection])
 | 
					 | 
				
			||||||
        metadata.merge_many([lyrics.metadata for lyrics in self.lyrics_collection])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return metadata
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_options(self) -> list:
 | 
					    def get_options(self) -> list:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Return a list of related objects including the song object, album object, main artist objects, and feature artist objects.
 | 
					        Return a list of related objects including the song object, album object, main artist objects, and feature artist objects.
 | 
				
			||||||
@@ -162,15 +159,13 @@ class Song(MainObject, MetadataAttribute):
 | 
				
			|||||||
    def get_option_string(self) -> str:
 | 
					    def get_option_string(self) -> str:
 | 
				
			||||||
        return f"Song({self.title}) of Album({self.album.title}) from Artists({self.get_artist_credits()})"
 | 
					        return f"Song({self.title}) of Album({self.album.title}) from Artists({self.get_artist_credits()})"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    tracksort_str: List[Type['Album']] = property(fget=get_tracksort_str)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
"""
 | 
					"""
 | 
				
			||||||
All objects dependent on Album
 | 
					All objects dependent on Album
 | 
				
			||||||
"""
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Album(MainObject, MetadataAttribute):
 | 
					class Album(MainObject):
 | 
				
			||||||
    COLLECTION_ATTRIBUTES = ("label_collection", "artist_collection", "song_collection")
 | 
					    COLLECTION_ATTRIBUTES = ("label_collection", "artist_collection", "song_collection")
 | 
				
			||||||
    SIMPLE_ATTRIBUTES = ("title", "album_status", "album_type", "language", "date", "barcode", "albumsort")
 | 
					    SIMPLE_ATTRIBUTES = ("title", "album_status", "album_type", "language", "date", "barcode", "albumsort")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -233,6 +228,16 @@ class Album(MainObject, MetadataAttribute):
 | 
				
			|||||||
            *[('url', source.url) for source in self.source_list]
 | 
					            *[('url', source.url) for source in self.source_list]
 | 
				
			||||||
        ]
 | 
					        ]
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def metadata(self) -> Metadata:
 | 
				
			||||||
 | 
					        return Metadata({
 | 
				
			||||||
 | 
					            id3Mapping.ALBUM: [self.title],
 | 
				
			||||||
 | 
					            id3Mapping.COPYRIGHT: [self.copyright],
 | 
				
			||||||
 | 
					            id3Mapping.LANGUAGE: [self.iso_639_2_language],
 | 
				
			||||||
 | 
					            id3Mapping.ALBUM_ARTIST: [a.name for a in self.artist_collection],
 | 
				
			||||||
 | 
					            id3Mapping.DATE: [self.date.timestamp]
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __repr__(self):
 | 
					    def __repr__(self):
 | 
				
			||||||
        return f"Album(\"{self.title}\")"
 | 
					        return f"Album(\"{self.title}\")"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -268,16 +273,10 @@ class Album(MainObject, MetadataAttribute):
 | 
				
			|||||||
                continue
 | 
					                continue
 | 
				
			||||||
            song.tracksort = i + 1
 | 
					            song.tracksort = i + 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_metadata(self) -> MetadataAttribute.Metadata:
 | 
					 | 
				
			||||||
        return MetadataAttribute.Metadata({
 | 
					 | 
				
			||||||
            id3Mapping.ALBUM: [self.title],
 | 
					 | 
				
			||||||
            id3Mapping.COPYRIGHT: [self.copyright],
 | 
					 | 
				
			||||||
            id3Mapping.LANGUAGE: [self.iso_639_2_language],
 | 
					 | 
				
			||||||
            id3Mapping.ALBUM_ARTIST: [a.name for a in self.artist_collection],
 | 
					 | 
				
			||||||
            id3Mapping.DATE: [self.date.timestamp]
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_copyright(self) -> str:
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def copyright(self) -> str:
 | 
				
			||||||
        if self.date is None:
 | 
					        if self.date is None:
 | 
				
			||||||
            return ""
 | 
					            return ""
 | 
				
			||||||
        if self.date.has_year or len(self.label_collection) == 0:
 | 
					        if self.date.has_year or len(self.label_collection) == 0:
 | 
				
			||||||
@@ -312,17 +311,13 @@ class Album(MainObject, MetadataAttribute):
 | 
				
			|||||||
    def get_option_string(self) -> str:
 | 
					    def get_option_string(self) -> str:
 | 
				
			||||||
        return f"Album: {self.title}; Artists {', '.join([i.name for i in self.artist_collection])}"
 | 
					        return f"Album: {self.title}; Artists {', '.join([i.name for i in self.artist_collection])}"
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    tracklist: List[Song] = property(fget=lambda self: self.song_collection.copy())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    copyright = property(fget=get_copyright)
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
"""
 | 
					"""
 | 
				
			||||||
All objects dependent on Artist
 | 
					All objects dependent on Artist
 | 
				
			||||||
"""
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Artist(MainObject, MetadataAttribute):
 | 
					class Artist(MainObject):
 | 
				
			||||||
    COLLECTION_ATTRIBUTES = ("feature_song_collection", "main_album_collection", "label_collection")
 | 
					    COLLECTION_ATTRIBUTES = ("feature_song_collection", "main_album_collection", "label_collection")
 | 
				
			||||||
    SIMPLE_ATTRIBUTES = ("name", "name", "country", "formed_in", "notes", "lyrical_themes", "general_genre")
 | 
					    SIMPLE_ATTRIBUTES = ("name", "name", "country", "formed_in", "notes", "lyrical_themes", "general_genre")
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
@@ -382,6 +377,15 @@ class Artist(MainObject, MetadataAttribute):
 | 
				
			|||||||
            *[('url', source.url) for source in self.source_list]
 | 
					            *[('url', source.url) for source in self.source_list]
 | 
				
			||||||
        ]
 | 
					        ]
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def metadata(self) -> Metadata:
 | 
				
			||||||
 | 
					        metadata = Metadata({
 | 
				
			||||||
 | 
					            id3Mapping.ARTIST: [self.name]
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        metadata.merge_many([s.get_artist_metadata() for s in self.source_list])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return metadata
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __str__(self):
 | 
					    def __str__(self):
 | 
				
			||||||
        string = self.name or ""
 | 
					        string = self.name or ""
 | 
				
			||||||
        plaintext_notes = self.notes.get_plaintext()
 | 
					        plaintext_notes = self.notes.get_plaintext()
 | 
				
			||||||
@@ -425,14 +429,6 @@ class Artist(MainObject, MetadataAttribute):
 | 
				
			|||||||
            song_list=self.feature_song_collection.copy()
 | 
					            song_list=self.feature_song_collection.copy()
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_metadata(self) -> MetadataAttribute.Metadata:
 | 
					 | 
				
			||||||
        metadata = MetadataAttribute.Metadata({
 | 
					 | 
				
			||||||
            id3Mapping.ARTIST: [self.name]
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
        metadata.merge_many([s.get_artist_metadata() for s in self.source_list])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return metadata
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_options(self) -> list:
 | 
					    def get_options(self) -> list:
 | 
				
			||||||
        options = [self]
 | 
					        options = [self]
 | 
				
			||||||
        options.extend(self.main_album_collection)
 | 
					        options.extend(self.main_album_collection)
 | 
				
			||||||
@@ -466,7 +462,7 @@ Label
 | 
				
			|||||||
"""
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Label(MainObject, SourceAttribute, MetadataAttribute):
 | 
					class Label(MainObject, SourceAttribute):
 | 
				
			||||||
    COLLECTION_ATTRIBUTES = ("album_collection", "current_artist_collection")
 | 
					    COLLECTION_ATTRIBUTES = ("album_collection", "current_artist_collection")
 | 
				
			||||||
    SIMPLE_ATTRIBUTES = ("name",)
 | 
					    SIMPLE_ATTRIBUTES = ("name",)
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,7 @@ from collections import defaultdict
 | 
				
			|||||||
from enum import Enum
 | 
					from enum import Enum
 | 
				
			||||||
from typing import List, Dict, Tuple
 | 
					from typing import List, Dict, Tuple
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from .metadata import Mapping, MetadataAttribute
 | 
					from .metadata import Mapping, Metadata
 | 
				
			||||||
from .parents import DatabaseObject
 | 
					from .parents import DatabaseObject
 | 
				
			||||||
from .collection import Collection
 | 
					from .collection import Collection
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -106,14 +106,15 @@ class Source(DatabaseObject, MetadataAttribute):
 | 
				
			|||||||
            Mapping.ARTIST_WEBPAGE_URL: [self.url]
 | 
					            Mapping.ARTIST_WEBPAGE_URL: [self.url]
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_metadata(self) -> MetadataAttribute.Metadata:
 | 
					    @property
 | 
				
			||||||
 | 
					    def metadata(self) -> Metadata:
 | 
				
			||||||
        if self.type_enum == SourceTypes.SONG:
 | 
					        if self.type_enum == SourceTypes.SONG:
 | 
				
			||||||
            return self.get_song_metadata()
 | 
					            return self.get_song_metadata()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if self.type_enum == SourceTypes.ARTIST:
 | 
					        if self.type_enum == SourceTypes.ARTIST:
 | 
				
			||||||
            return self.get_artist_metadata()
 | 
					            return self.get_artist_metadata()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return super().get_metadata()
 | 
					        return super().metadata
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def indexing_values(self) -> List[Tuple[str, object]]:
 | 
					    def indexing_values(self) -> List[Tuple[str, object]]:
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user