24 Commits

Author SHA1 Message Date
d374ca324d Merge pull request 'fix/metal_archives' (#10) from fix/metal_archives into experimental
All checks were successful
ci/woodpecker/manual/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
ci/woodpecker/push/woodpecker Pipeline was successful
Reviewed-on: #10
2024-04-18 15:50:48 +00:00
81eb43c8ef Merge branch 'experimental' into fix/metal_archives
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pr/woodpecker Pipeline was successful
ci/woodpecker/pull_request_closed/woodpecker Pipeline was successful
2024-04-18 15:48:04 +00:00
ba94e38a2d feat: disabled object trace 2024-04-18 17:47:33 +02:00
3532fea36c feat: syncing artists between song and album 2024-04-18 17:20:30 +02:00
3cd9daf512 feat: swaped the artist syncing 2024-04-18 15:43:01 +02:00
662f207529 fix: recursion depth error 2024-04-18 15:30:04 +02:00
85923e2a79 feat: improved logging with traceback 2024-04-18 14:37:20 +02:00
f000ad4484 fix: some crashes 2024-04-17 18:13:03 +02:00
7e4ba0b1a0 fix: clean mapping 2024-04-17 18:00:41 +02:00
56101d4a31 fix: clean mapping 2024-04-17 17:56:16 +02:00
cde3b3dbbb fix: correct unmaping of values 2024-04-17 17:52:59 +02:00
e47e22428d ci: force tags to be fetched
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-04-16 11:07:27 -07:00
64c95aabfa Merge pull request 'ci: add pipeline' (#8) from ci/add-pipeline into experimental
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Reviewed-on: #8
2024-04-16 17:56:47 +00:00
329aa39271 ci: disable local version in config
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/manual/woodpecker Pipeline was successful
ci/woodpecker/pr/woodpecker Pipeline was successful
ci/woodpecker/pull_request_closed/woodpecker Pipeline was successful
2024-04-16 10:39:15 -07:00
4367c2274d ci: change version format because pypi won't allow local versions
Some checks failed
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/manual/woodpecker Pipeline failed
2024-04-16 10:30:06 -07:00
5e04c480bd docs: add readme status badge
Some checks failed
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/manual/woodpecker Pipeline failed
2024-04-16 10:09:10 -07:00
eab19304e1 ci: make step names more clear
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-04-16 10:07:26 -07:00
d0fe0c3f86 ci: fix indentation
Some checks failed
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/manual/woodpecker Pipeline failed
2024-04-16 10:02:16 -07:00
9dbe34de88 ci: publish to pypi
Some checks failed
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/manual/woodpecker Pipeline failed
2024-04-16 10:01:15 -07:00
9783e3f24e ci: use new twine plugin
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/manual/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2024-04-15 13:30:31 -07:00
eb94a328c7 ci: fix secret names
Some checks failed
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/manual/woodpecker Pipeline failed
2024-04-15 12:48:56 -07:00
2f4a9f9801 ci: remove unstable suffix from experimental and add stable suffix to tagged builds 2024-04-15 12:45:10 -07:00
92a03355c3 ci: add _version.py to gitignore 2024-04-15 12:42:57 -07:00
2f29ad2415 ci: add pipeline 2024-04-15 11:44:19 -07:00
11 changed files with 144 additions and 62 deletions

3
.gitignore vendored
View File

@@ -18,3 +18,6 @@ venv
windows
.env
# setuptools_scm
_version.py

57
.woodpecker.yml Normal file
View File

@@ -0,0 +1,57 @@
labels:
platform: linux/amd64
clone:
git:
image: woodpeckerci/plugin-git
settings:
tags: true
steps:
build-stable:
image: python
commands:
- sed -i 's/name = "music-kraken"/name = "music-kraken-stable"/' pyproject.toml
- python -m pip install -r requirements-dev.txt
- python3 -m build
environment:
- SETUPTOOLS_SCM_PRETEND_VERSION=${CI_COMMIT_TAG}
when:
- event: tag
build-dev:
image: python
commands:
- python -m pip install -r requirements-dev.txt
- python3 -m build
when:
- event: manual
- event: push
branch: experimental
publish-gitea:
image: gitea.elara.ws/music-kraken/plugin-twine
settings:
repository_url: "https://gitea.elara.ws/api/packages/music-kraken/pypi"
username:
from_secret: gitea_username
password:
from_secret: gitea_password
when:
- event: manual
- event: tag
- event: push
branch: experimental
publish-pypi:
image: gitea.elara.ws/music-kraken/plugin-twine
settings:
username:
from_secret: pypi_username
password:
from_secret: pypi_password
when:
- event: manual
- event: tag
- event: push
branch: experimental

View File

@@ -1,5 +1,7 @@
# Music Kraken
[![Woodpecker CI Status](https://ci.elara.ws/api/badges/59/status.svg)](https://ci.elara.ws/repos/59)
<img src="assets/logo.svg" width=300 alt="music kraken logo"/>
- [Music Kraken](#music-kraken)

2
build
View File

@@ -45,4 +45,4 @@ then
exit
fi
twine upload dist/music_kraken*
twine upload dist/music_kraken*

View File

@@ -9,8 +9,6 @@ from rich.console import Console
from .utils.shared import DEBUG, DEBUG_LOGGING
from .utils.config import logging_settings, main_settings, read_config
__version__ = "1.15.0"
read_config()
console: Console = Console()

View File

@@ -1,7 +1,7 @@
from __future__ import annotations
from collections import defaultdict
from typing import TypeVar, Generic, Dict, Optional, Iterable, List, Iterator, Tuple, Generator
from typing import TypeVar, Generic, Dict, Optional, Iterable, List, Iterator, Tuple, Generator, Union
from .parents import OuterProxy
T = TypeVar('T', bound=OuterProxy)
@@ -36,40 +36,39 @@ class Collection(Generic[T]):
# Value: main collection to sync to
self.contain_given_in_attribute: Dict[str, Collection] = contain_given_in_attribute or {}
self.append_object_to_attribute: Dict[str, T] = append_object_to_attribute or {}
self.sync_on_append: Dict[str, Collection] = sync_on_append or {}
self._indexed_values = defaultdict(set)
self._indexed_to_objects = defaultdict(list)
self._id_to_index_values: Dict[int, set] = defaultdict(set)
self._indexed_values = defaultdict(lambda: None)
self._indexed_to_objects = defaultdict(lambda: None)
self.extend(data)
def _map_element(self, __object: T, from_map: bool = False):
__object._inner._mapped_in_collection.add(self)
self._contains_ids.add(__object.id)
for name, value in __object.indexing_values:
for name, value in (*__object.indexing_values, ('id', __object.id)):
if value is None or value == __object._inner._default_values.get(name):
continue
self._indexed_values[name].add(value)
self._indexed_to_objects[value].append(__object)
self._indexed_values[name] = value
self._indexed_to_objects[value] = __object
def _unmap_element(self, __object: T):
if __object.id in self._contains_ids:
self._contains_ids.remove(__object.id)
self._id_to_index_values[__object.id].add((name, value))
for name, value in __object.indexing_values:
if value is None:
continue
if value not in self._indexed_values[name]:
continue
def _unmap_element(self, __object: Union[T, int]):
obj_id = __object.id if isinstance(__object, OuterProxy) else __object
try:
self._indexed_to_objects[value].remove(__object)
except ValueError:
continue
if obj_id in self._contains_ids:
self._contains_ids.remove(obj_id)
if not len(self._indexed_to_objects[value]):
self._indexed_values[name].remove(value)
for name, value in self._id_to_index_values[obj_id]:
if name in self._indexed_values:
del self._indexed_values[name]
if value in self._indexed_to_objects:
del self._indexed_to_objects[value]
del self._id_to_index_values[obj_id]
def _contained_in_self(self, __object: T) -> bool:
if __object.id in self._contains_ids:
@@ -78,7 +77,7 @@ class Collection(Generic[T]):
for name, value in __object.indexing_values:
if value is None:
continue
if value in self._indexed_values[name]:
if value == self._indexed_values[name]:
return True
return False
@@ -150,8 +149,8 @@ class Collection(Generic[T]):
if value is None:
continue
if value in self._indexed_values[name]:
existing_object = self._indexed_to_objects[value][0]
if value == self._indexed_values[name]:
existing_object = self._indexed_to_objects[value]
if existing_object.id == __object.id:
return None
@@ -173,8 +172,8 @@ class Collection(Generic[T]):
def _find_object_in_self(self, __object: T) -> Optional[T]:
for name, value in __object.indexing_values:
if value in self._indexed_values[name]:
return self._indexed_to_objects[value][0]
if value == self._indexed_values[name]:
return self._indexed_to_objects[value]
def _find_object(self, __object: T, no_sibling: bool = False) -> Tuple[Collection[T], Optional[T]]:
other_object = self._find_object_in_self(__object)
@@ -231,15 +230,24 @@ class Collection(Generic[T]):
for attribute, new_object in self.append_object_to_attribute.items():
__object.__getattribute__(attribute).append(new_object)
for attribute, collection in self.sync_on_append.items():
collection.extend(__object.__getattribute__(attribute))
__object.__setattr__(attribute, collection)
else:
# merge only if the two objects are not the same
if existing_object.id == __object.id:
return
append_to._unmap_element(existing_object)
existing_object.merge(__object)
append_to._map_element(existing_object)
old_id = existing_object.id
existing_object.merge(__object)
if existing_object.id != old_id:
append_to._unmap_element(old_id)
append_to._map_element(existing_object)
def extend(self, __iterable: Optional[Generator[T, None, None]]):
if __iterable is None:
@@ -285,7 +293,7 @@ class Collection(Generic[T]):
yield from c.__iter__(finished_ids=finished_ids)
def __merge__(self, __other: Collection, override: bool = False):
self.extend(__other.__iter__())
self.extend(__other)
def __getitem__(self, item: int):
if item < len(self._data):

View File

@@ -3,9 +3,11 @@ from __future__ import annotations
import random
from collections import defaultdict
from functools import lru_cache
from typing import Optional, Dict, Tuple, List, Type, Generic, Any, TypeVar, Set
from pathlib import Path
import inspect
from .metadata import Metadata
from ..utils import get_unix_time, object_trace
from ..utils.config import logging_settings, main_settings
@@ -162,15 +164,7 @@ class OuterProxy:
self._add_other_db_objects(key, value)
def __hash__(self):
"""
:raise: IsDynamicException
:return:
"""
if self.dynamic:
return id(self._inner)
return self.id
return id(self)
def __eq__(self, other: Any):
return self.__hash__() == other.__hash__()
@@ -187,29 +181,39 @@ class OuterProxy:
if __other is None:
return
object_trace(f"merging {type(self).__name__} [{self.title_string} | {self.id}] with {type(__other).__name__} [{__other.title_string} | {__other.id}]")
a = self
b = __other
if a._inner is b._inner:
if a.id == b.id:
return
# switch instances if more efficient
if len(b._inner._refers_to_instances) > len(a._inner._refers_to_instances):
a, b = b, a
a._inner.__merge__(b._inner, override=override)
object_trace(f"merging {type(a).__name__} [{a.title_string} | {a.id}] with {type(b).__name__} [{b.title_string} | {b.id}] called by [{' | '.join(f'{s.function} {Path(s.filename).name}:{str(s.lineno)}' for s in inspect.stack()[1:5])}]")
for collection, child_collection in b._inner._is_collection_child.items():
collection.children.remove(child_collection)
try:
collection.children.remove(child_collection)
except ValueError:
pass
for collection, parent_collection in b._inner._is_collection_parent.items():
collection.parents.remove(parent_collection)
try:
collection.parents.remove(parent_collection)
except ValueError:
pass
a._inner._refers_to_instances.update(b._inner._refers_to_instances)
old_inner = b._inner
for instance in b._inner._refers_to_instances:
for instance in b._inner._refers_to_instances.copy():
instance._inner = a._inner
a._inner._refers_to_instances.add(instance)
a._inner.__merge__(old_inner, override=override)
del old_inner
def __merge__(self, __other: Optional[OuterProxy], override: bool = False):
self.merge(__other, override)

View File

@@ -86,9 +86,15 @@ class Song(Base):
TITEL = "title"
def __init_collections__(self) -> None:
"""
self.album_collection.contain_given_in_attribute = {
"artist_collection": self.main_artist_collection,
}
"""
self.album_collection.sync_on_append = {
"artist_collection": self.main_artist_collection,
}
self.album_collection.append_object_to_attribute = {
"song_collection": self,
}
@@ -245,6 +251,9 @@ class Album(Base):
self.song_collection.append_object_to_attribute = {
"album_collection": self
}
self.song_collection.sync_on_append = {
"main_artist_collection": self.artist_collection
}
self.artist_collection.append_object_to_attribute = {
"main_album_collection": self

View File

@@ -104,14 +104,7 @@ class Source(OuterProxy):
('url', self.url),
('audio_url', self.audio_url),
]
def __merge__(self, __other: Source, override: bool = False):
if override:
self.audio_url = __other.audio_url
if self.audio_url is None or (override and __other.audio_url is not None):
self.audio_url = __other.audio_url
def __str__(self):
return self.__repr__()

View File

@@ -15,7 +15,7 @@ __stage__ = os.getenv("STAGE", "prod")
DEBUG = (__stage__ == "dev") and True
DEBUG_LOGGING = DEBUG and True
DEBUG_TRACE = DEBUG and True
DEBUG_OBJECT_TRACE = DEBUG and True
DEBUG_OBJECT_TRACE = DEBUG and False
DEBUG_YOUTUBE_INITIALIZING = DEBUG and False
DEBUG_PAGES = DEBUG and False
DEBUG_DUMP = DEBUG and True

View File

@@ -1,5 +1,5 @@
[build-system]
requires = ["hatchling", "hatch-requirements-txt" ]
requires = ["hatchling", "hatch-requirements-txt", "hatch-vcs"]
build-backend = "hatchling.build"
[tool.hatch.build]
@@ -15,7 +15,15 @@ packages = ["music_kraken"]
music-kraken = "music_kraken.__main__:cli"
[tool.hatch.version]
path = "music_kraken/__init__.py"
source = "vcs"
path = "music_kraken/_version.py"
fallback-version = "0.0.0"
[tool.hatch.version.raw-options]
local_scheme = "no-local-version"
[tool.hatch.build.hooks.vcs]
version-file = "music_kraken/_version.py"
[project]
name = "music-kraken"