From 9c7bdec840219e5519921a50dba9eb326c4d7fc9 Mon Sep 17 00:00:00 2001 From: Lars Noack Date: Tue, 9 Apr 2024 14:18:34 +0200 Subject: [PATCH] feat: added lyrics metadata --- music_kraken/objects/lyrics.py | 12 ++++ music_kraken/objects/parents.py | 2 +- music_kraken/objects/song.py | 10 --- music_kraken/utils/hacking.py | 105 ++++++++++++++++++++++++++++++++ 4 files changed, 118 insertions(+), 11 deletions(-) diff --git a/music_kraken/objects/lyrics.py b/music_kraken/objects/lyrics.py index 0ca37f0..1d04088 100644 --- a/music_kraken/objects/lyrics.py +++ b/music_kraken/objects/lyrics.py @@ -6,6 +6,11 @@ from .parents import OuterProxy from .source import Source, SourceCollection from .formatted_text import FormattedText from .country import Language +from .metadata import ( + Mapping as id3Mapping, + ID3Timestamp, + Metadata +) class Lyrics(OuterProxy): @@ -25,3 +30,10 @@ class Lyrics(OuterProxy): def __init__(self, text: FormattedText = None, language: Language = None, source_list: SourceCollection = None, **kwargs) -> None: super().__init__(text=text, language=language, source_list=source_list, **kwargs) + + @property + def metadata(self) -> Metadata: + return Metadata({ + id3Mapping.UNSYNCED_LYRICS + }) + diff --git a/music_kraken/objects/parents.py b/music_kraken/objects/parents.py index bdaa960..6079062 100644 --- a/music_kraken/objects/parents.py +++ b/music_kraken/objects/parents.py @@ -9,7 +9,7 @@ from typing import Optional, Dict, Tuple, List, Type, Generic, Any, TypeVar, Set from .metadata import Metadata from ..utils.config import logging_settings from ..utils.shared import HIGHEST_ID -from ..utils.support_classes.hacking import MetaClass +from ..utils.hacking import MetaClass LOGGER = logging_settings["object_logger"] diff --git a/music_kraken/objects/song.py b/music_kraken/objects/song.py index 86a8a45..eea36dc 100644 --- a/music_kraken/objects/song.py +++ b/music_kraken/objects/song.py @@ -146,16 +146,6 @@ class Song(Base): return main_artists return f"{main_artists} feat. {feature_artists}" - """ - def __str__(self) -> str: - artist_credit_str = "" - artist_credits = self.get_artist_credits() - if artist_credits != "": - artist_credit_str = f" by {artist_credits}" - - return f"\"{self.title}\"{artist_credit_str}" - """ - def __repr__(self) -> str: return f"Song(\"{self.title}\")" diff --git a/music_kraken/utils/hacking.py b/music_kraken/utils/hacking.py index 3b60255..e68356e 100644 --- a/music_kraken/utils/hacking.py +++ b/music_kraken/utils/hacking.py @@ -38,6 +38,11 @@ Merge signatures of two functions with Advanced Hackery. Useful for wrappers. Usage: @merge_args(old_function) """ +import weakref +from types import FunctionType +from functools import wraps +from typing import Dict, Set + import inspect import itertools import types @@ -196,3 +201,103 @@ def merge_args( ) except TypeError: pass + + +class Lake: + def __init__(self): + self.redirects: Dict[int, int] = {} + self.id_to_object: Dict[int, object] = {} + + def get_real_object(self, db_object: object) -> object: + _id = id(db_object) + while _id in self.redirects: + _id = self.redirects[_id] + + try: + return self.id_to_object[_id] + except KeyError: + self.add(db_object) + return db_object + + def add(self, db_object: object): + self.id_to_object[id(db_object)] = db_object + + def override(self, to_override: object, new_db_object: object): + _id = id(to_override) + while _id in self.redirects: + _id = self.redirects[_id] + + if id(new_db_object) in self.id_to_object: + print("!!!!!") + + self.add(new_db_object) + self.redirects[_id] = id(new_db_object) + # if _id in self.id_to_object: + # del self.id_to_object[_id] + + def is_same(self, __object: object, other: object) -> bool: + _self_id = id(__object) + while _self_id in self.redirects: + _self_id = self.redirects[_self_id] + + _other_id = id(other) + while _other_id in self.redirects: + _other_id = self.redirects[_other_id] + + return _self_id == _other_id + + +lake = Lake() + + +def wrapper(method): + @wraps(method) + def wrapped(*args, **kwargs): + return method(*(lake.get_real_object(args[0]), *args[1:]), **kwargs) + + return wrapped + + +class BaseClass: + def __new__(cls, *args, **kwargs): + instance = cls(*args, **kwargs) + print("new") + lake.add(instance) + return instance + + def __eq__(self, other): + return lake.is_same(self, other) + + def _risky_merge(self, to_replace): + lake.override(to_replace, self) + + +class MetaClass(type): + def __new__(meta, classname, bases, classDict): + bases = (*bases, BaseClass) + newClassDict = {} + + ignore_functions: Set[str] = {"__new__", "__init__"} + + for attributeName, attribute in classDict.items(): + if isinstance(attribute, FunctionType) and (attributeName not in ignore_functions): + """ + The funktion new and init shouldn't be accounted for because we can assume the class is + independent on initialization. + """ + attribute = wrapper(attribute) + + newClassDict[attributeName] = attribute + + print() + + for key, value in object.__dict__.items(): + # hasattr( value, '__call__' ) and + if hasattr(value, '__call__') and value not in newClassDict and key not in ("__new__", "__init__"): + newClassDict[key] = wrapper(value) + + new_instance = type.__new__(meta, classname, bases, newClassDict) + + lake.add(new_instance) + + return new_instance