From 5a699c3937e27229e50db15f1424f5affc61c3ca Mon Sep 17 00:00:00 2001 From: Hellow Date: Tue, 14 Feb 2023 23:07:16 +0100 Subject: [PATCH] STARTED IMPLEMENTING DB STARTED IMPLEMENTING DB --- .VSCodeCounter/2022-12-07_13-25-59/details.md | 12 +- .../2022-12-07_13-25-59/diff-details.md | 12 +- .VSCodeCounter/2022-12-13_13-57-09/details.md | 12 +- .../2022-12-13_13-57-09/diff-details.md | 8 +- .VSCodeCounter/2023-01-17_16-43-06/details.md | 16 +- .../2023-01-17_16-43-06/diff-details.md | 14 +- .VSCodeCounter/2023-01-19_15-23-38/details.md | 16 +- .../2023-01-19_15-23-38/diff-details.md | 4 +- .VSCodeCounter/2023-02-06_10-33-12/details.md | 16 +- .../2023-02-06_10-33-12/diff-details.md | 14 +- requirements.txt | 6 +- src/create_custom_objects.py | 2 +- src/music_kraken/database/__init__.py | 4 +- src/music_kraken/database/data_models.py | 113 +++ src/music_kraken/database/database.py | 743 ++---------------- src/music_kraken/database/object_cache.py | 2 +- src/music_kraken/database/old_database.py | 700 +++++++++++++++++ src/music_kraken/database/temp_database.py | 2 +- .../{database => }/objects/__init__.py | 0 .../{database => }/objects/artist.py | 2 +- .../{database => }/objects/collection.py | 2 +- .../{database => }/objects/formatted_text.py | 0 .../{database => }/objects/metadata.py | 0 .../{database => }/objects/parents.py | 2 +- .../{database => }/objects/song.py | 2 +- .../{database => }/objects/source.py | 0 src/try_python.py | 41 + 27 files changed, 990 insertions(+), 755 deletions(-) create mode 100644 src/music_kraken/database/data_models.py create mode 100644 src/music_kraken/database/old_database.py rename src/music_kraken/{database => }/objects/__init__.py (100%) rename src/music_kraken/{database => }/objects/artist.py (91%) rename src/music_kraken/{database => }/objects/collection.py (97%) rename src/music_kraken/{database => }/objects/formatted_text.py (100%) rename src/music_kraken/{database => }/objects/metadata.py (100%) rename src/music_kraken/{database => }/objects/parents.py (98%) rename src/music_kraken/{database => }/objects/song.py (99%) rename src/music_kraken/{database => }/objects/source.py (100%) diff --git a/.VSCodeCounter/2022-12-07_13-25-59/details.md b/.VSCodeCounter/2022-12-07_13-25-59/details.md index 52333da..4a6d709 100644 --- a/.VSCodeCounter/2022-12-07_13-25-59/details.md +++ b/.VSCodeCounter/2022-12-07_13-25-59/details.md @@ -24,13 +24,13 @@ Total : 43 files, 2560 codes, 558 comments, 774 blanks, all 3892 lines | [src/music_kraken/audio_source/sources/source.py](/src/music_kraken/not_used_anymore/sources/source.py) | Python | 11 | 5 | 8 | 24 | | [src/music_kraken/audio_source/sources/youtube.py](/src/music_kraken/not_used_anymore/sources/youtube.py) | Python | 71 | 4 | 24 | 99 | | [src/music_kraken/database/__init__.py](/src/music_kraken/database/__init__.py) | Python | 11 | 1 | 4 | 16 | -| [src/music_kraken/database/database.py](/src/music_kraken/database/database.py) | Python | 191 | 102 | 45 | 338 | +| [src/music_kraken/database/database.py](/src/music_kraken/database/old_database.py) | Python | 191 | 102 | 45 | 338 | | [src/music_kraken/database/get_song.py](/src/music_kraken/database/get_song.py) | Python | 40 | 5 | 11 | 56 | -| [src/music_kraken/database/new_database.py](/src/music_kraken/database/database.py) | Python | 172 | 78 | 55 | 305 | -| [src/music_kraken/database/objects/__init__.py](/src/music_kraken/database/objects/__init__.py) | Python | 11 | 0 | 4 | 15 | -| [src/music_kraken/database/objects/artist.py](/src/music_kraken/database/objects/artist.py) | Python | 18 | 0 | 5 | 23 | -| [src/music_kraken/database/objects/database_object.py](/src/music_kraken/database/objects/database_object.py) | Python | 21 | 5 | 11 | 37 | -| [src/music_kraken/database/objects/song.py](/src/music_kraken/database/objects/song.py) | Python | 179 | 52 | 60 | 291 | +| [src/music_kraken/database/new_database.py](/src/music_kraken/database/old_database.py) | Python | 172 | 78 | 55 | 305 | +| [src/music_kraken/database/objects/__init__.py](/src/music_kraken/objects/__init__.py) | Python | 11 | 0 | 4 | 15 | +| [src/music_kraken/database/objects/artist.py](/src/music_kraken/objects/artist.py) | Python | 18 | 0 | 5 | 23 | +| [src/music_kraken/database/objects/database_object.py](/src/music_kraken/objects/database_object.py) | Python | 21 | 5 | 11 | 37 | +| [src/music_kraken/database/objects/song.py](/src/music_kraken/objects/song.py) | Python | 179 | 52 | 60 | 291 | | [src/music_kraken/database/song.py](/src/music_kraken/database/song.py) | Python | 125 | 20 | 45 | 190 | | [src/music_kraken/database/temp_database.py](/src/music_kraken/database/temp_database.py) | Python | 10 | 0 | 7 | 17 | | [src/music_kraken/lyrics/__init__.py](/src/music_kraken/lyrics/__init__.py) | Python | 0 | 0 | 1 | 1 | diff --git a/.VSCodeCounter/2022-12-07_13-25-59/diff-details.md b/.VSCodeCounter/2022-12-07_13-25-59/diff-details.md index c430920..4a25677 100644 --- a/.VSCodeCounter/2022-12-07_13-25-59/diff-details.md +++ b/.VSCodeCounter/2022-12-07_13-25-59/diff-details.md @@ -16,14 +16,14 @@ Total : 20 files, 700 codes, 165 comments, 162 blanks, all 1027 lines | [src/music_kraken/audio_source/fetch_source.py](/src/music_kraken/not_used_anymore/fetch_source.py) | Python | 0 | 0 | -1 | -1 | | [src/music_kraken/database/__init__.py](/src/music_kraken/database/__init__.py) | Python | -4 | 1 | 1 | -2 | | [src/music_kraken/database/artist.py](/src/music_kraken/database/artist.py) | Python | -11 | 0 | -5 | -16 | -| [src/music_kraken/database/database.py](/src/music_kraken/database/database.py) | Python | 25 | 22 | 4 | 51 | +| [src/music_kraken/database/database.py](/src/music_kraken/database/old_database.py) | Python | 25 | 22 | 4 | 51 | | [src/music_kraken/database/get_song.py](/src/music_kraken/database/get_song.py) | Python | 40 | 5 | 11 | 56 | | [src/music_kraken/database/metadata.py](/src/music_kraken/database/metadata.py) | Python | -13 | 0 | -5 | -18 | -| [src/music_kraken/database/new_database.py](/src/music_kraken/database/database.py) | Python | 172 | 78 | 55 | 305 | -| [src/music_kraken/database/objects/__init__.py](/src/music_kraken/database/objects/__init__.py) | Python | 11 | 0 | 4 | 15 | -| [src/music_kraken/database/objects/artist.py](/src/music_kraken/database/objects/artist.py) | Python | 18 | 0 | 5 | 23 | -| [src/music_kraken/database/objects/database_object.py](/src/music_kraken/database/objects/database_object.py) | Python | 21 | 5 | 11 | 37 | -| [src/music_kraken/database/objects/song.py](/src/music_kraken/database/objects/song.py) | Python | 179 | 52 | 60 | 291 | +| [src/music_kraken/database/new_database.py](/src/music_kraken/database/old_database.py) | Python | 172 | 78 | 55 | 305 | +| [src/music_kraken/database/objects/__init__.py](/src/music_kraken/objects/__init__.py) | Python | 11 | 0 | 4 | 15 | +| [src/music_kraken/database/objects/artist.py](/src/music_kraken/objects/artist.py) | Python | 18 | 0 | 5 | 23 | +| [src/music_kraken/database/objects/database_object.py](/src/music_kraken/objects/database_object.py) | Python | 21 | 5 | 11 | 37 | +| [src/music_kraken/database/objects/song.py](/src/music_kraken/objects/song.py) | Python | 179 | 52 | 60 | 291 | | [src/music_kraken/database/song.py](/src/music_kraken/database/song.py) | Python | 39 | -5 | 12 | 46 | | [src/music_kraken/database/source.py](/src/music_kraken/database/source.py) | Python | -5 | 0 | -2 | -7 | | [src/music_kraken/database/target.py](/src/music_kraken/database/target.py) | Python | -22 | 0 | -9 | -31 | diff --git a/.VSCodeCounter/2022-12-13_13-57-09/details.md b/.VSCodeCounter/2022-12-13_13-57-09/details.md index e093dc8..348ce7f 100644 --- a/.VSCodeCounter/2022-12-13_13-57-09/details.md +++ b/.VSCodeCounter/2022-12-13_13-57-09/details.md @@ -24,13 +24,13 @@ Total : 45 files, 2886 codes, 594 comments, 854 blanks, all 4334 lines | [src/music_kraken/audio_source/sources/source.py](/src/music_kraken/not_used_anymore/sources/source.py) | Python | 11 | 5 | 8 | 24 | | [src/music_kraken/audio_source/sources/youtube.py](/src/music_kraken/not_used_anymore/sources/youtube.py) | Python | 71 | 4 | 24 | 99 | | [src/music_kraken/database/__init__.py](/src/music_kraken/database/__init__.py) | Python | 11 | 1 | 4 | 16 | -| [src/music_kraken/database/database.py](/src/music_kraken/database/database.py) | Python | 191 | 102 | 45 | 338 | +| [src/music_kraken/database/database.py](/src/music_kraken/database/old_database.py) | Python | 191 | 102 | 45 | 338 | | [src/music_kraken/database/get_song.py](/src/music_kraken/database/get_song.py) | Python | 40 | 5 | 11 | 56 | -| [src/music_kraken/database/new_database.py](/src/music_kraken/database/database.py) | Python | 327 | 98 | 89 | 514 | -| [src/music_kraken/database/objects/__init__.py](/src/music_kraken/database/objects/__init__.py) | Python | 10 | 0 | 3 | 13 | -| [src/music_kraken/database/objects/artist.py](/src/music_kraken/database/objects/artist.py) | Python | 18 | 0 | 5 | 23 | -| [src/music_kraken/database/objects/database_object.py](/src/music_kraken/database/objects/database_object.py) | Python | 28 | 7 | 13 | 48 | -| [src/music_kraken/database/objects/song.py](/src/music_kraken/database/objects/song.py) | Python | 255 | 61 | 86 | 402 | +| [src/music_kraken/database/new_database.py](/src/music_kraken/database/old_database.py) | Python | 327 | 98 | 89 | 514 | +| [src/music_kraken/database/objects/__init__.py](/src/music_kraken/objects/__init__.py) | Python | 10 | 0 | 3 | 13 | +| [src/music_kraken/database/objects/artist.py](/src/music_kraken/objects/artist.py) | Python | 18 | 0 | 5 | 23 | +| [src/music_kraken/database/objects/database_object.py](/src/music_kraken/objects/database_object.py) | Python | 28 | 7 | 13 | 48 | +| [src/music_kraken/database/objects/song.py](/src/music_kraken/objects/song.py) | Python | 255 | 61 | 86 | 402 | | [src/music_kraken/database/song.py](/src/music_kraken/database/song.py) | Python | 125 | 20 | 45 | 190 | | [src/music_kraken/database/temp_database.py](/src/music_kraken/database/temp_database.py) | Python | 12 | 0 | 8 | 20 | | [src/music_kraken/lyrics/__init__.py](/src/music_kraken/lyrics/__init__.py) | Python | 0 | 0 | 1 | 1 | diff --git a/.VSCodeCounter/2022-12-13_13-57-09/diff-details.md b/.VSCodeCounter/2022-12-13_13-57-09/diff-details.md index acdb891..598e039 100644 --- a/.VSCodeCounter/2022-12-13_13-57-09/diff-details.md +++ b/.VSCodeCounter/2022-12-13_13-57-09/diff-details.md @@ -12,10 +12,10 @@ Total : 10 files, 326 codes, 36 comments, 80 blanks, all 442 lines | filename | language | code | comment | blank | total | | :--- | :--- | ---: | ---: | ---: | ---: | | [src/goof.py](/src/goof.py) | Python | 30 | -1 | 7 | 36 | -| [src/music_kraken/database/new_database.py](/src/music_kraken/database/database.py) | Python | 155 | 20 | 34 | 209 | -| [src/music_kraken/database/objects/__init__.py](/src/music_kraken/database/objects/__init__.py) | Python | -1 | 0 | -1 | -2 | -| [src/music_kraken/database/objects/database_object.py](/src/music_kraken/database/objects/database_object.py) | Python | 7 | 2 | 2 | 11 | -| [src/music_kraken/database/objects/song.py](/src/music_kraken/database/objects/song.py) | Python | 76 | 9 | 26 | 111 | +| [src/music_kraken/database/new_database.py](/src/music_kraken/database/old_database.py) | Python | 155 | 20 | 34 | 209 | +| [src/music_kraken/database/objects/__init__.py](/src/music_kraken/objects/__init__.py) | Python | -1 | 0 | -1 | -2 | +| [src/music_kraken/database/objects/database_object.py](/src/music_kraken/objects/database_object.py) | Python | 7 | 2 | 2 | 11 | +| [src/music_kraken/database/objects/song.py](/src/music_kraken/objects/song.py) | Python | 76 | 9 | 26 | 111 | | [src/music_kraken/database/temp_database.py](/src/music_kraken/database/temp_database.py) | Python | 2 | 0 | 1 | 3 | | [src/music_kraken/metadata/sources/__init__.py](/src/music_kraken/not_used_anymore/metadata/sources/__init__.py) | Python | 3 | 0 | 2 | 5 | | [src/music_kraken/metadata/sources/musicbrainz.py](/src/music_kraken/not_used_anymore/metadata/sources/musicbrainz.py) | Python | 42 | 6 | 9 | 57 | diff --git a/.VSCodeCounter/2023-01-17_16-43-06/details.md b/.VSCodeCounter/2023-01-17_16-43-06/details.md index 60972ec..ba904eb 100644 --- a/.VSCodeCounter/2023-01-17_16-43-06/details.md +++ b/.VSCodeCounter/2023-01-17_16-43-06/details.md @@ -24,15 +24,15 @@ Total : 49 files, 3402 codes, 663 comments, 973 blanks, all 5038 lines | [src/music_kraken/audio_source/sources/source.py](/src/music_kraken/not_used_anymore/sources/source.py) | Python | 11 | 5 | 8 | 24 | | [src/music_kraken/audio_source/sources/youtube.py](/src/music_kraken/not_used_anymore/sources/youtube.py) | Python | 71 | 4 | 24 | 99 | | [src/music_kraken/database/__init__.py](/src/music_kraken/database/__init__.py) | Python | 12 | 1 | 4 | 17 | -| [src/music_kraken/database/database.py](/src/music_kraken/database/database.py) | Python | 191 | 102 | 45 | 338 | +| [src/music_kraken/database/database.py](/src/music_kraken/database/old_database.py) | Python | 191 | 102 | 45 | 338 | | [src/music_kraken/database/get_song.py](/src/music_kraken/database/get_song.py) | Python | 40 | 5 | 11 | 56 | -| [src/music_kraken/database/new_database.py](/src/music_kraken/database/database.py) | Python | 401 | 109 | 107 | 617 | -| [src/music_kraken/database/objects/__init__.py](/src/music_kraken/database/objects/__init__.py) | Python | 14 | 0 | 4 | 18 | -| [src/music_kraken/database/objects/artist.py](/src/music_kraken/database/objects/artist.py) | Python | 18 | 0 | 5 | 23 | -| [src/music_kraken/database/objects/metadata.py](/src/music_kraken/database/objects/metadata.py) | Python | 245 | 52 | 50 | 347 | -| [src/music_kraken/database/objects/parents.py](/src/music_kraken/database/objects/parents.py) | Python | 46 | 8 | 23 | 77 | -| [src/music_kraken/database/objects/song.py](/src/music_kraken/database/objects/song.py) | Python | 258 | 52 | 76 | 386 | -| [src/music_kraken/database/objects/source.py](/src/music_kraken/database/objects/source.py) | Python | 46 | 7 | 13 | 66 | +| [src/music_kraken/database/new_database.py](/src/music_kraken/database/old_database.py) | Python | 401 | 109 | 107 | 617 | +| [src/music_kraken/database/objects/__init__.py](/src/music_kraken/objects/__init__.py) | Python | 14 | 0 | 4 | 18 | +| [src/music_kraken/database/objects/artist.py](/src/music_kraken/objects/artist.py) | Python | 18 | 0 | 5 | 23 | +| [src/music_kraken/database/objects/metadata.py](/src/music_kraken/objects/metadata.py) | Python | 245 | 52 | 50 | 347 | +| [src/music_kraken/database/objects/parents.py](/src/music_kraken/objects/parents.py) | Python | 46 | 8 | 23 | 77 | +| [src/music_kraken/database/objects/song.py](/src/music_kraken/objects/song.py) | Python | 258 | 52 | 76 | 386 | +| [src/music_kraken/database/objects/source.py](/src/music_kraken/objects/source.py) | Python | 46 | 7 | 13 | 66 | | [src/music_kraken/database/song.py](/src/music_kraken/database/song.py) | Python | 125 | 20 | 45 | 190 | | [src/music_kraken/database/temp_database.py](/src/music_kraken/database/temp_database.py) | Python | 12 | 0 | 8 | 20 | | [src/music_kraken/lyrics/__init__.py](/src/music_kraken/lyrics/__init__.py) | Python | 0 | 0 | 1 | 1 | diff --git a/.VSCodeCounter/2023-01-17_16-43-06/diff-details.md b/.VSCodeCounter/2023-01-17_16-43-06/diff-details.md index 019f4a7..a0e71fa 100644 --- a/.VSCodeCounter/2023-01-17_16-43-06/diff-details.md +++ b/.VSCodeCounter/2023-01-17_16-43-06/diff-details.md @@ -14,13 +14,13 @@ Total : 16 files, 516 codes, 69 comments, 119 blanks, all 704 lines | [src/goof.py](/src/goof.py) | Python | 42 | 2 | 10 | 54 | | [src/music_kraken/__init__.py](/src/music_kraken/__init__.py) | Python | 1 | 0 | 0 | 1 | | [src/music_kraken/database/__init__.py](/src/music_kraken/database/__init__.py) | Python | 1 | 0 | 0 | 1 | -| [src/music_kraken/database/new_database.py](/src/music_kraken/database/database.py) | Python | 74 | 11 | 18 | 103 | -| [src/music_kraken/database/objects/__init__.py](/src/music_kraken/database/objects/__init__.py) | Python | 4 | 0 | 1 | 5 | -| [src/music_kraken/database/objects/database_object.py](/src/music_kraken/database/objects/database_object.py) | Python | -28 | -7 | -13 | -48 | -| [src/music_kraken/database/objects/metadata.py](/src/music_kraken/database/objects/metadata.py) | Python | 245 | 52 | 50 | 347 | -| [src/music_kraken/database/objects/parents.py](/src/music_kraken/database/objects/parents.py) | Python | 46 | 8 | 23 | 77 | -| [src/music_kraken/database/objects/song.py](/src/music_kraken/database/objects/song.py) | Python | 3 | -9 | -10 | -16 | -| [src/music_kraken/database/objects/source.py](/src/music_kraken/database/objects/source.py) | Python | 46 | 7 | 13 | 66 | +| [src/music_kraken/database/new_database.py](/src/music_kraken/database/old_database.py) | Python | 74 | 11 | 18 | 103 | +| [src/music_kraken/database/objects/__init__.py](/src/music_kraken/objects/__init__.py) | Python | 4 | 0 | 1 | 5 | +| [src/music_kraken/database/objects/database_object.py](/src/music_kraken/objects/database_object.py) | Python | -28 | -7 | -13 | -48 | +| [src/music_kraken/database/objects/metadata.py](/src/music_kraken/objects/metadata.py) | Python | 245 | 52 | 50 | 347 | +| [src/music_kraken/database/objects/parents.py](/src/music_kraken/objects/parents.py) | Python | 46 | 8 | 23 | 77 | +| [src/music_kraken/database/objects/song.py](/src/music_kraken/objects/song.py) | Python | 3 | -9 | -10 | -16 | +| [src/music_kraken/database/objects/source.py](/src/music_kraken/objects/source.py) | Python | 46 | 7 | 13 | 66 | | [src/music_kraken/static_files/new_db.sql](/src/music_kraken/static_files/new_db.sql) | SQLite | 1 | 0 | 0 | 1 | | [src/music_kraken/tagging/__init__.py](/src/music_kraken/tagging/__init__.py) | Python | 8 | 0 | 1 | 9 | | [src/music_kraken/tagging/id3.py](/src/music_kraken/tagging/id3.py) | Python | 51 | 4 | 20 | 75 | diff --git a/.VSCodeCounter/2023-01-19_15-23-38/details.md b/.VSCodeCounter/2023-01-19_15-23-38/details.md index 2b0e98f..c5eca01 100644 --- a/.VSCodeCounter/2023-01-19_15-23-38/details.md +++ b/.VSCodeCounter/2023-01-19_15-23-38/details.md @@ -24,15 +24,15 @@ Total : 49 files, 3404 codes, 664 comments, 974 blanks, all 5042 lines | [src/music_kraken/audio_source/sources/source.py](/src/music_kraken/not_used_anymore/sources/source.py) | Python | 11 | 5 | 8 | 24 | | [src/music_kraken/audio_source/sources/youtube.py](/src/music_kraken/not_used_anymore/sources/youtube.py) | Python | 71 | 4 | 24 | 99 | | [src/music_kraken/database/__init__.py](/src/music_kraken/database/__init__.py) | Python | 12 | 1 | 4 | 17 | -| [src/music_kraken/database/database.py](/src/music_kraken/database/database.py) | Python | 191 | 102 | 45 | 338 | +| [src/music_kraken/database/database.py](/src/music_kraken/database/old_database.py) | Python | 191 | 102 | 45 | 338 | | [src/music_kraken/database/get_song.py](/src/music_kraken/database/get_song.py) | Python | 40 | 5 | 11 | 56 | -| [src/music_kraken/database/new_database.py](/src/music_kraken/database/database.py) | Python | 402 | 110 | 107 | 619 | -| [src/music_kraken/database/objects/__init__.py](/src/music_kraken/database/objects/__init__.py) | Python | 15 | 0 | 5 | 20 | -| [src/music_kraken/database/objects/artist.py](/src/music_kraken/database/objects/artist.py) | Python | 18 | 0 | 5 | 23 | -| [src/music_kraken/database/objects/metadata.py](/src/music_kraken/database/objects/metadata.py) | Python | 245 | 52 | 50 | 347 | -| [src/music_kraken/database/objects/parents.py](/src/music_kraken/database/objects/parents.py) | Python | 46 | 8 | 23 | 77 | -| [src/music_kraken/database/objects/song.py](/src/music_kraken/database/objects/song.py) | Python | 258 | 52 | 76 | 386 | -| [src/music_kraken/database/objects/source.py](/src/music_kraken/database/objects/source.py) | Python | 46 | 7 | 13 | 66 | +| [src/music_kraken/database/new_database.py](/src/music_kraken/database/old_database.py) | Python | 402 | 110 | 107 | 619 | +| [src/music_kraken/database/objects/__init__.py](/src/music_kraken/objects/__init__.py) | Python | 15 | 0 | 5 | 20 | +| [src/music_kraken/database/objects/artist.py](/src/music_kraken/objects/artist.py) | Python | 18 | 0 | 5 | 23 | +| [src/music_kraken/database/objects/metadata.py](/src/music_kraken/objects/metadata.py) | Python | 245 | 52 | 50 | 347 | +| [src/music_kraken/database/objects/parents.py](/src/music_kraken/objects/parents.py) | Python | 46 | 8 | 23 | 77 | +| [src/music_kraken/database/objects/song.py](/src/music_kraken/objects/song.py) | Python | 258 | 52 | 76 | 386 | +| [src/music_kraken/database/objects/source.py](/src/music_kraken/objects/source.py) | Python | 46 | 7 | 13 | 66 | | [src/music_kraken/database/song.py](/src/music_kraken/database/song.py) | Python | 125 | 20 | 45 | 190 | | [src/music_kraken/database/temp_database.py](/src/music_kraken/database/temp_database.py) | Python | 12 | 0 | 8 | 20 | | [src/music_kraken/lyrics/__init__.py](/src/music_kraken/lyrics/__init__.py) | Python | 0 | 0 | 1 | 1 | diff --git a/.VSCodeCounter/2023-01-19_15-23-38/diff-details.md b/.VSCodeCounter/2023-01-19_15-23-38/diff-details.md index ae93768..4b711e7 100644 --- a/.VSCodeCounter/2023-01-19_15-23-38/diff-details.md +++ b/.VSCodeCounter/2023-01-19_15-23-38/diff-details.md @@ -11,7 +11,7 @@ Total : 2 files, 2 codes, 1 comments, 1 blanks, all 4 lines ## Files | filename | language | code | comment | blank | total | | :--- | :--- | ---: | ---: | ---: | ---: | -| [src/music_kraken/database/new_database.py](/src/music_kraken/database/database.py) | Python | 1 | 1 | 0 | 2 | -| [src/music_kraken/database/objects/__init__.py](/src/music_kraken/database/objects/__init__.py) | Python | 1 | 0 | 1 | 2 | +| [src/music_kraken/database/new_database.py](/src/music_kraken/database/old_database.py) | Python | 1 | 1 | 0 | 2 | +| [src/music_kraken/database/objects/__init__.py](/src/music_kraken/objects/__init__.py) | Python | 1 | 0 | 1 | 2 | [Summary](results.md) / [Details](details.md) / [Diff Summary](diff.md) / Diff Details \ No newline at end of file diff --git a/.VSCodeCounter/2023-02-06_10-33-12/details.md b/.VSCodeCounter/2023-02-06_10-33-12/details.md index 9252dcf..360e261 100644 --- a/.VSCodeCounter/2023-02-06_10-33-12/details.md +++ b/.VSCodeCounter/2023-02-06_10-33-12/details.md @@ -17,14 +17,14 @@ Total : 50 files, 3575 codes, 775 comments, 1028 blanks, all 5378 lines | [src/music_kraken/__init__.py](/src/music_kraken/__init__.py) | Python | 63 | 26 | 33 | 122 | | [src/music_kraken/__main__.py](/src/music_kraken/__main__.py) | Python | 3 | 2 | 3 | 8 | | [src/music_kraken/database/__init__.py](/src/music_kraken/database/__init__.py) | Python | 18 | 0 | 5 | 23 | -| [src/music_kraken/database/database.py](/src/music_kraken/database/database.py) | Python | 429 | 112 | 111 | 652 | -| [src/music_kraken/database/objects/__init__.py](/src/music_kraken/database/objects/__init__.py) | Python | 20 | 0 | 7 | 27 | -| [src/music_kraken/database/objects/artist.py](/src/music_kraken/database/objects/artist.py) | Python | 18 | 0 | 5 | 23 | -| [src/music_kraken/database/objects/formatted_text.py](/src/music_kraken/database/objects/formatted_text.py) | Python | 48 | 57 | 16 | 121 | -| [src/music_kraken/database/objects/metadata.py](/src/music_kraken/database/objects/metadata.py) | Python | 251 | 68 | 61 | 380 | -| [src/music_kraken/database/objects/parents.py](/src/music_kraken/database/objects/parents.py) | Python | 40 | 8 | 19 | 67 | -| [src/music_kraken/database/objects/song.py](/src/music_kraken/database/objects/song.py) | Python | 323 | 64 | 85 | 472 | -| [src/music_kraken/database/objects/source.py](/src/music_kraken/database/objects/source.py) | Python | 116 | 38 | 41 | 195 | +| [src/music_kraken/database/database.py](/src/music_kraken/database/old_database.py) | Python | 429 | 112 | 111 | 652 | +| [src/music_kraken/database/objects/__init__.py](/src/music_kraken/objects/__init__.py) | Python | 20 | 0 | 7 | 27 | +| [src/music_kraken/database/objects/artist.py](/src/music_kraken/objects/artist.py) | Python | 18 | 0 | 5 | 23 | +| [src/music_kraken/database/objects/formatted_text.py](/src/music_kraken/objects/formatted_text.py) | Python | 48 | 57 | 16 | 121 | +| [src/music_kraken/database/objects/metadata.py](/src/music_kraken/objects/metadata.py) | Python | 251 | 68 | 61 | 380 | +| [src/music_kraken/database/objects/parents.py](/src/music_kraken/objects/parents.py) | Python | 40 | 8 | 19 | 67 | +| [src/music_kraken/database/objects/song.py](/src/music_kraken/objects/song.py) | Python | 323 | 64 | 85 | 472 | +| [src/music_kraken/database/objects/source.py](/src/music_kraken/objects/source.py) | Python | 116 | 38 | 41 | 195 | | [src/music_kraken/database/temp_database.py](/src/music_kraken/database/temp_database.py) | Python | 12 | 0 | 8 | 20 | | [src/music_kraken/not_used_anymore/__init__.py](/src/music_kraken/not_used_anymore/__init__.py) | Python | 0 | 0 | 3 | 3 | | [src/music_kraken/not_used_anymore/fetch_audio.py](/src/music_kraken/not_used_anymore/fetch_audio.py) | Python | 75 | 12 | 20 | 107 | diff --git a/.VSCodeCounter/2023-02-06_10-33-12/diff-details.md b/.VSCodeCounter/2023-02-06_10-33-12/diff-details.md index ec8d8db..4d95c45 100644 --- a/.VSCodeCounter/2023-02-06_10-33-12/diff-details.md +++ b/.VSCodeCounter/2023-02-06_10-33-12/diff-details.md @@ -24,15 +24,15 @@ Total : 55 files, 171 codes, 111 comments, 54 blanks, all 336 lines | [src/music_kraken/audio_source/sources/source.py](/src/music_kraken/audio_source/sources/source.py) | Python | -11 | -5 | -8 | -24 | | [src/music_kraken/audio_source/sources/youtube.py](/src/music_kraken/audio_source/sources/youtube.py) | Python | -71 | -4 | -24 | -99 | | [src/music_kraken/database/__init__.py](/src/music_kraken/database/__init__.py) | Python | 6 | -1 | 1 | 6 | -| [src/music_kraken/database/database.py](/src/music_kraken/database/database.py) | Python | 238 | 10 | 66 | 314 | +| [src/music_kraken/database/database.py](/src/music_kraken/database/old_database.py) | Python | 238 | 10 | 66 | 314 | | [src/music_kraken/database/get_song.py](/src/music_kraken/database/get_song.py) | Python | -40 | -5 | -11 | -56 | | [src/music_kraken/database/new_database.py](/src/music_kraken/database/new_database.py) | Python | -402 | -110 | -107 | -619 | -| [src/music_kraken/database/objects/__init__.py](/src/music_kraken/database/objects/__init__.py) | Python | 5 | 0 | 2 | 7 | -| [src/music_kraken/database/objects/formatted_text.py](/src/music_kraken/database/objects/formatted_text.py) | Python | 48 | 57 | 16 | 121 | -| [src/music_kraken/database/objects/metadata.py](/src/music_kraken/database/objects/metadata.py) | Python | 6 | 16 | 11 | 33 | -| [src/music_kraken/database/objects/parents.py](/src/music_kraken/database/objects/parents.py) | Python | -6 | 0 | -4 | -10 | -| [src/music_kraken/database/objects/song.py](/src/music_kraken/database/objects/song.py) | Python | 65 | 12 | 9 | 86 | -| [src/music_kraken/database/objects/source.py](/src/music_kraken/database/objects/source.py) | Python | 70 | 31 | 28 | 129 | +| [src/music_kraken/database/objects/__init__.py](/src/music_kraken/objects/__init__.py) | Python | 5 | 0 | 2 | 7 | +| [src/music_kraken/database/objects/formatted_text.py](/src/music_kraken/objects/formatted_text.py) | Python | 48 | 57 | 16 | 121 | +| [src/music_kraken/database/objects/metadata.py](/src/music_kraken/objects/metadata.py) | Python | 6 | 16 | 11 | 33 | +| [src/music_kraken/database/objects/parents.py](/src/music_kraken/objects/parents.py) | Python | -6 | 0 | -4 | -10 | +| [src/music_kraken/database/objects/song.py](/src/music_kraken/objects/song.py) | Python | 65 | 12 | 9 | 86 | +| [src/music_kraken/database/objects/source.py](/src/music_kraken/objects/source.py) | Python | 70 | 31 | 28 | 129 | | [src/music_kraken/database/song.py](/src/music_kraken/database/song.py) | Python | -125 | -20 | -45 | -190 | | [src/music_kraken/lyrics/__init__.py](/src/music_kraken/lyrics/__init__.py) | Python | 0 | 0 | -1 | -1 | | [src/music_kraken/lyrics/genius.py](/src/music_kraken/lyrics/genius.py) | Python | -115 | -16 | -42 | -173 | diff --git a/requirements.txt b/requirements.txt index c2747d3..7665321 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,4 +5,8 @@ jellyfish~=0.9.0 pydub~=0.25.1 youtube_dl beautifulsoup4~=4.11.1 -pycountry~=22.3.5 \ No newline at end of file +pycountry~=22.3.5 +python-dateutil~=2.8.2 +pandoc~=2.3 +peewee~=3.15.4 +setuptools~=60.2.0 \ No newline at end of file diff --git a/src/create_custom_objects.py b/src/create_custom_objects.py index 32abf58..6760f83 100644 --- a/src/create_custom_objects.py +++ b/src/create_custom_objects.py @@ -17,7 +17,7 @@ from music_kraken.tagging import ( write_many_metadata ) -import music_kraken.database.database as db +import music_kraken.database.old_database as db import pycountry import logging diff --git a/src/music_kraken/database/__init__.py b/src/music_kraken/database/__init__.py index f04651a..0ac7e4b 100644 --- a/src/music_kraken/database/__init__.py +++ b/src/music_kraken/database/__init__.py @@ -1,8 +1,8 @@ from . import ( temp_database, - objects, - database + old_database ) +from .. import objects MusicObject = objects.MusicObject diff --git a/src/music_kraken/database/data_models.py b/src/music_kraken/database/data_models.py new file mode 100644 index 0000000..572b8c6 --- /dev/null +++ b/src/music_kraken/database/data_models.py @@ -0,0 +1,113 @@ +from typing import List, Union, Type +from peewee import ( + SqliteDatabase, + PostgresqlDatabase, + MySQLDatabase, + Model, + CharField, + IntegerField, + BooleanField, + ForeignKeyField, + TextField +) + + +class Album(Model): + """A class representing an album in the music database.""" + + title: str = CharField() + label: str = CharField() + album_status: str = CharField() + language: str = CharField() + date: str = CharField() + date_format: str = CharField() + country: str = CharField() + barcode: str = CharField() + albumsort: int = IntegerField() + is_split: bool = BooleanField(default=False) + + +class Artist(Model): + """A class representing an artist in the music database.""" + + name: str = CharField() + + +class Song(Model): + """A class representing a song in the music database.""" + + name: str = CharField() + isrc: str = CharField() + length: int = IntegerField() + tracksort: int = IntegerField() + genre: str = CharField() + album: ForeignKeyField = ForeignKeyField(Album, backref='songs') + + +class Source(Model): + """A class representing a source of a song in the music database.""" + + type: str = CharField() + src: str = CharField() + url: str = CharField() + + content_type: str = CharField() + content_id: int = IntegerField() + content: ForeignKeyField = ForeignKeyField('self', backref='content_items', null=True) + + @property + def content_object(self) -> Union[Song, Album, Artist, None]: + """Get the content associated with the source as an object.""" + if self.content_type == 'Song': + return Song.get(Song.id == self.content_id) + elif self.content_type == 'Album': + return Album.get(Album.id == self.content_id) + elif self.content_type == 'Artist': + return Artist.get(Artist.id == self.content_id) + else: + return None + + @content_object.setter + def content_object(self, value: Union[Song, Album, Artist]) -> None: + """Set the content associated with the source as an object.""" + self.content_type = value.__class__.__name__ + self.content_id = value.id + + +class Target(Model): + """A class representing a target of a song in the music database.""" + + file: str = CharField() + path: str = CharField() + song = ForeignKeyField(Song, backref='targets') + + +class Lyrics(Model): + """A class representing lyrics of a song in the music database.""" + + text: str = TextField() + language: str = CharField() + song = ForeignKeyField(Song, backref='lyrics') + + +class SongArtist(Model): + """A class representing the relationship between a song and an artist.""" + + song: ForeignKeyField = ForeignKeyField(Song, backref='song_artists') + artist: ForeignKeyField = ForeignKeyField(Artist, backref='song_artists') + is_feature: bool = BooleanField(default=False) + + +class AlbumArtist(Model): + """A class representing the relationship between an album and an artist.""" + + album: ForeignKeyField = ForeignKeyField(Album, backref='album_artists') + artist: ForeignKeyField = ForeignKeyField(Artist, backref='album_artists') + + +class Models: + def __init__(self, database: Union[SqliteDatabase, PostgresqlDatabase, MySQLDatabase]): + self.database: Union[SqliteDatabase, PostgresqlDatabase, MySQLDatabase] = database + + def get_obj(self, _model: Model): + _model._meta.database = self.database diff --git a/src/music_kraken/database/database.py b/src/music_kraken/database/database.py index 627c48a..f77ff60 100644 --- a/src/music_kraken/database/database.py +++ b/src/music_kraken/database/database.py @@ -1,702 +1,79 @@ -import sqlite3 -import os -import logging -from typing import List, Tuple -from pkg_resources import resource_string -import datetime -import pycountry - -from .objects.parents import Reference -from .objects.source import Source -from .objects import ( - Song, - Lyrics, - Target, - Artist, - Album, - ID3Timestamp, - SourceTypes, - SourcePages, - SourceAttribute +from typing import Optional, Union +from enum import Enum +from peewee import ( + SqliteDatabase, + MySQLDatabase, + PostgresqlDatabase, ) -""" -import peewee +from . import data_models -db = peewee.SqliteDatabase('music.db') - -class BaseModel(peewee.Model): - class Meta: - database = db - -class Artist(BaseModel): - name = peewee.CharField() - -class Song(BaseModel): - title = peewee.CharField() - artist = peewee.ManyToManyField(Artist, backref='songs') - -db.connect() -db.create_tables([Artist, Song, Song.artist.get_through_model()], safe=True) - -# Adding a song and its artists -beatles = Artist.create(name='The Beatles') -rolling_stones = Artist.create(name='The Rolling Stones') -song = Song.create(title='Hey Jude') -song.artist.add(beatles, rolling_stones) - -# Querying songs by artist -songs = Song.select().join(Song.artist).where(Artist.name == 'The Beatles') -for song in songs: - print(song.title) - -""" - -logger = logging.getLogger("database") - -# Due to this not being deployed on a Server **HOPEFULLY** -# I don't need to parameterize stuff like the where and -# use complicated query builder -SONG_QUERY = """ -SELECT -Song.id AS song_id, Song.name AS title, Song.isrc AS isrc, Song.length AS length, Song.album_id as album_id, Song.tracksort, -Target.id AS target_id, Target.file AS file, Target.path AS path, Song.genre AS genre -FROM Song -LEFT JOIN Target ON Song.id=Target.song_id -WHERE {where}; -""" -SOURCE_QUERY = """ -SELECT id, type, src, url, song_id -FROM Source -WHERE {where}; -""" -LYRICS_QUERY = """ -SELECT id, text, language, song_id -FROM Lyrics -WHERE {where}; -""" -ALBUM_QUERY_UNJOINED = """ -SELECT Album.id AS album_id, title, label, album_status, language, date, date_format, country, barcode, albumsort, is_split -FROM Album -WHERE {where}; -""" -ALBUM_QUERY_JOINED = """ -SELECT a.id AS album_id, a.title, a.label, a.album_status, a.language, a.date, a.date_format, a.country, a.barcode, a.albumsort, a.is_split -FROM Song -INNER JOIN Album a ON Song.album_id=a.id -WHERE {where}; -""" -ARTIST_QUERY = """ -SELECT id as artist_id, name as artist_name -FROM Artist -WHERE {where}; -""" +class DatabaseType(Enum): + SQLITE = "sqlite" + POSTGRESQL = "postgresql" + MYSQL = "mysql" class Database: - def __init__(self, database_file: str): - self.database_file: str = database_file - self.connection, self.cursor = self.reset_cursor() + database: Union[SqliteDatabase, PostgresqlDatabase, MySQLDatabase] - self.cursor = self.connection.cursor() + def __init__( + self, + db_type: DatabaseType, + db_name: str, + db_user: Optional[str] = None, + db_password: Optional[str] = None, + db_host: Optional[str] = None, + db_port: Optional[int] = None + ): + self.db_type = db_type + self.db_name = db_name + self.db_user = db_user + self.db_password = db_password + self.db_host = db_host + self.db_port = db_port - def reset(self): - """ - Deletes all Data from the database if it exists - and resets the schema defined in self.structure_file - """ - logger.info(f"resetting the database") + self.initialize_database() - # deleting the database - del self.connection - del self.cursor - os.remove(self.database_file) + def create_database(self) -> Union[SqliteDatabase, PostgresqlDatabase, MySQLDatabase]: + """Create a database instance based on the configured database type and parameters. - # newly creating the database - self.reset_cursor() - query = resource_string("music_kraken", "static_files/new_db.sql").decode('utf-8') - - # fill the database with the schematic - self.cursor.executescript(query) - self.connection.commit() - - def reset_cursor(self) -> Tuple[sqlite3.Connection, sqlite3.Cursor]: - self.connection = sqlite3.connect(self.database_file) - # This is necessary that fetching rows returns dicts instead of tuple - self.connection.row_factory = sqlite3.Row - - self.cursor = self.connection.cursor() - return self.connection, self.cursor - - def push_one(self, db_object: Song | Lyrics | Target | Artist | Source | Album): - if db_object.dynamic: - return - - if type(db_object) == Song: - return self.push_song(song=db_object, pushed=set()) - - """ - if type(db_object) == Lyrics: - return self.push_lyrics(lyrics=db_object) - - if type(db_object) == Target: - return self.push_target(target=db_object) + Returns: + The created database instance, or None if an invalid database type was specified. """ - if type(db_object) == Artist: - return self.push_artist(artist=db_object, pushed=set()) - - """ - if type(db_object) == Source: - # needs to have the property type_enum or type_str set - return self.push_source(source=db_object) - """ - - if type(db_object) == Album: - return self.push_album(album=db_object, pushed=set()) - - logger.warning(f"type {type(db_object)} isn't yet supported by the db") - - def push(self, db_object_list: List[Song | Lyrics | Target | Artist | Source | Album]): - """ - This function is used to Write the data of any db_object to the database - - It syncs a whole list of db_objects to the database and is meant - as the primary method to add to the database. - - :param db_object_list: - """ - - for db_object in db_object_list: - self.push_one(db_object) - - def push_album(self, album: Album, pushed: set): - table = "Album" - query = f"INSERT OR REPLACE INTO {table} (id, title, label, album_status, language, date, date_format, country, barcode, albumsort, is_split) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);" - - if album.id in pushed: - return - pushed.add(album.id) - - date_format, date = album.date.get_timestamp_w_format() - - values = ( - album.id, - album.title, - album.label, - album.album_status, - album.iso_639_2_language, - date, - date_format, - album.country, - album.barcode, - album.albumsort, - album.is_split - ) - self.cursor.execute(query, values) - self.connection.commit() - - for song in album.tracklist: - self.push_song(song, pushed=pushed) - for artist in album.artists: - self.push_artist_album(artist_ref=artist.reference, album_ref=album.reference) - self.push_artist(artist, pushed=pushed) - - for source in album.source_list: - source.type_enum = SourceTypes.ALBUM - source.add_song(album) - self.push_source(source=source) - - def push_song(self, song: Song, pushed: set): - if song.dynamic: - return - - if song.id in pushed: - return - pushed.add(song.id) - - # ADDING THE DATA FOR THE SONG OBJECT - """ - db_field - object attribute - ------------------------------- - id - id - name - title - """ - table = "Song" - - values = ( - song.id, - song.title, - song.isrc, - song.length, - song.get_album_id(), - song.tracksort, - song.genre - ) - query = f"INSERT OR REPLACE INTO {table} (id, name, isrc, length, album_id, tracksort, genre) VALUES (?, ?, ?, ?, ?, ?, ?);" - - self.cursor.execute(query, values) - self.connection.commit() - - # add sources - for source in song.source_list: - source.add_song(song) - source.type_enum = SourceTypes.SONG - self.push_source(source=source) - - # add lyrics - for single_lyrics in song.lyrics: - single_lyrics.add_song(song) - self.push_lyrics(lyrics=single_lyrics) - - # add target - song.target.add_song(song) - self.push_target(target=song.target) - - for main_artist in song.main_artist_list: - self.push_artist_song(artist_ref=Reference(main_artist.id), song_ref=Reference(song.id), is_feature=False) - self.push_artist(artist=main_artist, pushed=pushed) - - for feature_artist in song.feature_artist_list: - self.push_artist_song(artist_ref=Reference(feature_artist.id), song_ref=Reference(song.id), is_feature=True) - self.push_artist(artist=feature_artist, pushed=pushed) - - if song.album is not None: - self.push_album(song.album, pushed=pushed) - - def push_lyrics(self, lyrics: Lyrics): - if lyrics.dynamic: - return - - table = "Lyrics" - query = f"INSERT OR REPLACE INTO {table} (id, song_id, text, language) VALUES (?, ?, ?, ?);" - values = ( - lyrics.id, - lyrics.song_ref_id, - lyrics.text, - lyrics.language - ) - - self.cursor.execute(query, values) - self.connection.commit() - - def push_source(self, source: Source): - if source.dynamic: - return - - table = "Source" - query = f"INSERT OR REPLACE INTO {table} (id, type, song_id, src, url) VALUES (?, ?, ?, ?, ?);" - values = ( - source.id, - source.type_str, - source.song_ref_id, - source.page_str, - source.url - ) - - self.cursor.execute(query, values) - self.connection.commit() - - def push_target(self, target: Target): - if target.dynamic: - return - - table = "Target" - query = f"INSERT OR REPLACE INTO {table} (id, song_id, file, path) VALUES (?, ?, ?, ?);" - values = ( - target.id, - target.song_ref_id, - target.file, - target.path - ) - - self.cursor.execute(query, values) - self.connection.commit() - - def push_artist_song(self, artist_ref: Reference, song_ref: Reference, is_feature: bool): - table = "SongArtist" - # checking if already exists - query = f"SELECT * FROM {table} WHERE song_id=\"{song_ref.id}\" AND artist_id=\"{artist_ref.id}\"" - self.cursor.execute(query) - if len(self.cursor.fetchall()) > 0: - # join already exists - return - - query = f"INSERT OR REPLACE INTO {table} (song_id, artist_id, is_feature) VALUES (?, ?, ?);" - values = ( - song_ref.id, - artist_ref.id, - is_feature - ) - - self.cursor.execute(query, values) - self.connection.commit() - - def push_artist_album(self, artist_ref: Reference, album_ref: Reference): - table = "AlbumArtist" - # checking if already exists - query = f"SELECT * FROM {table} WHERE album_id=\"{album_ref.id}\" AND artist_id=\"{artist_ref.id}\"" - self.cursor.execute(query) - if len(self.cursor.fetchall()) > 0: - # join already exists - return - - query = f"INSERT OR REPLACE INTO {table} (album_id, artist_id) VALUES (?, ?);" - values = ( - album_ref.id, - artist_ref.id - ) - - self.cursor.execute(query, values) - self.connection.commit() - - def push_artist(self, artist: Artist, pushed: set): - if artist.dynamic: - return - if artist.id in pushed: - return - pushed.add(artist.id) - - table = "Artist" - query = f"INSERT OR REPLACE INTO {table} (id, name) VALUES (?, ?);" - values = ( - artist.id, - artist.name - ) - - self.cursor.execute(query, values) - self.connection.commit() - - for song in artist.feature_songs: - self.push_artist_song(artist_ref=artist.reference, song_ref=song.reference, is_feature=True) - self.push_song(song=song, pushed=pushed) - - for song in artist.main_songs: - self.push_artist_song(artist_ref=artist.reference, song_ref=song.reference, is_feature=False) - self.push_song(song=song, pushed=pushed) - - for album in artist.main_albums: - self.push_artist_album(artist_ref=artist.reference, album_ref=album.reference) - - for source in artist.source_list: - source.type_enum = SourceTypes.ARTIST - source.add_song(artist) - self.push_source(source) - - def pull_lyrics(self, song_ref: Reference = None, lyrics_ref: Reference = None) -> List[Lyrics]: - """ - Gets a list of sources. if lyrics_ref is passed in the List will most likely only - contain one Element if everything goes accordingly. - **If neither song_ref nor lyrics_ref are passed in it will return ALL lyrics** - :param song_ref: - :param lyrics_ref: - :return: - """ - - where = "1=1" - if song_ref is not None: - where = f"song_id=\"{song_ref.id}\"" - elif lyrics_ref is not None: - where = f"id=\"{lyrics_ref.id}\"" - - query = LYRICS_QUERY.format(where=where) - self.cursor.execute(query) - - lyrics_rows = self.cursor.fetchall() - return [Lyrics( - id_=lyrics_row['id'], - text=lyrics_row['text'], - language=lyrics_row['language'] - ) for lyrics_row in lyrics_rows] - - def pull_sources(self, artist_ref: Reference = None, song_ref: Reference = None, source_ref: Reference = None, album_ref: Reference = None) -> List[Source]: - """ - Gets a list of sources. if source_ref is passed in the List will most likely only - contain one Element if everything goes accordingly. - **If neither song_ref nor source_ref are passed in it will return ALL sources** - :param artist_ref: - :param song_ref: - :param source_ref: - :param type_str: the thing the source belongs to like eg. "song" or "album" - :return: - """ - - where = "1=1" - if song_ref is not None: - where = f"song_id=\"{song_ref.id}\"" - elif source_ref is not None: - where = f"id=\"{source_ref.id}\" AND type=\"{SourceTypes.SONG.value}\"" - elif artist_ref is not None: - where = f"song_id=\"{artist_ref.id}\" AND type=\"{SourceTypes.ARTIST.value}\"" - elif album_ref is not None: - where = f"song_id=\"{album_ref.id}\" AND type=\"{SourceTypes.ALBUM.value}\"" - - query = SOURCE_QUERY.format(where=where) - self.cursor.execute(query) - - source_rows = self.cursor.fetchall() - - return [ - Source( - page_enum=SourcePages(source_row['src']), - type_enum=SourceTypes(source_row['type']), - url=source_row['url'], - id_=source_row['id'] - ) for source_row in source_rows - ] - - def pull_artist_song(self, song_ref: Reference = None, artist_ref: Reference = None) -> List[tuple]: - table = "SongArtist" - wheres = [] - if song_ref is not None: - wheres.append(f"song_id=\"{song_ref.id}\"") - if artist_ref is not None: - wheres.append(f"artist_id=\"{artist_ref.id}\"") - where_str = "" - if len(wheres) > 0: - where_str = "WHERE " + " AND ".join(wheres) - - query = f"SELECT * FROM {table} {where_str};" - self.cursor.execute(query) - joins = self.cursor.fetchall() - - return [( - Reference(join["song_id"]), - Reference(join["artist_id"]), - bool(join["is_feature"]) - ) for join in joins] - - def pull_artist_album(self, album_ref: Reference = None, artist_ref: Reference = None) -> List[tuple]: - table = "AlbumArtist" - wheres = [] - if album_ref is not None: - wheres.append(f"album_id=\"{album_ref.id}\"") - if artist_ref is not None: - wheres.append(f"artist_id=\"{artist_ref.id}\"") - where_str = "" - if len(wheres) > 0: - where_str = "WHERE " + " AND ".join(wheres) - - query = f"SELECT * FROM {table} {where_str};" - self.cursor.execute(query) - joins = self.cursor.fetchall() - - return [( - Reference(join["album_id"]), - Reference(join["artist_id"]) - ) for join in joins] - - def get_artist_from_row(self, artist_row, exclude_relations: set = None, flat: bool = False) -> Artist: - if exclude_relations is None: - exclude_relations = set() - new_exclude_relations: set = set(exclude_relations) - new_exclude_relations.add(Artist) - - artist_id = artist_row['artist_id'] - - artist_obj = Artist( - id_=artist_id, - name=artist_row['artist_name'], - source_list=self.pull_sources(artist_ref=Reference(id_=artist_id)) - ) - if flat: - return artist_obj - - # fetch songs :D - for song_ref, _, is_feature in self.pull_artist_song(artist_ref=Reference(id_=artist_id)): - new_songs = self.pull_songs(song_ref=song_ref, exclude_relations=new_exclude_relations) - if len(new_songs) < 1: - continue - new_song = new_songs[0] - - if is_feature: - artist_obj.feature_songs.append(new_song) - else: - artist_obj.main_songs.append(new_song) - - # fetch albums - for album_ref, _ in self.pull_artist_album(artist_ref=Reference(id_=artist_id)): - new_albums = self.pull_albums(album_ref=album_ref, exclude_relations=new_exclude_relations) - if len(new_albums) < 1: - continue - artist_obj.main_albums.append(new_albums[0]) - - return artist_obj - - def pull_artists(self, artist_ref: Reference = None, exclude_relations: set = None, flat: bool = False) -> List[Artist]: - """ - - :param artist_ref: - :param exclude_relations: - :param flat: if it is true it ONLY fetches the artist data - :return: - """ - - where = "1=1" - if artist_ref is not None: - where = f"Artist.id=\"{artist_ref.id}\"" - - query = ARTIST_QUERY.format(where=where) - self.cursor.execute(query) - - artist_rows = self.cursor.fetchall() - return [( - self.get_artist_from_row(artist_row, exclude_relations=exclude_relations, flat=flat) - ) for artist_row in artist_rows] - - def get_song_from_row(self, song_result, exclude_relations: set = None) -> Song: - if exclude_relations is None: - exclude_relations = set() - new_exclude_relations: set = set(exclude_relations) - new_exclude_relations.add(Song) - - song_id = song_result['song_id'] - - # maybee fetch album - - song_obj = Song( - id_=song_id, - title=song_result['title'], - isrc=song_result['isrc'], - length=song_result['length'], - tracksort=song_result['tracksort'], - genre=song_result['genre'], - target=Target( - id_=song_result['target_id'], - file=song_result['file'], - path=song_result['path'] - ), - source_list=self.pull_sources(song_ref=Reference(id_=song_id)), - lyrics=self.pull_lyrics(song_ref=Reference(id_=song_id)), - ) - - if Album not in exclude_relations and song_result['album_id'] is not None: - album_obj = self.pull_albums(album_ref=Reference(song_result['album_id']), - exclude_relations=new_exclude_relations) - if len(album_obj) > 0: - song_obj.album = album_obj[0] - - flat_artist = Artist in exclude_relations - - main_artists = [] - feature_artists = [] - for song_ref, artist_ref, is_feature in self.pull_artist_song(song_ref=Reference(song_id)): - if is_feature: - feature_artists.extend(self.pull_artists(artist_ref=artist_ref, flat=flat_artist)) - else: - main_artists.extend(self.pull_artists(artist_ref=artist_ref, flat=flat_artist)) - - song_obj.main_artist_list = main_artists - song_obj.feature_artist_list = feature_artists - - return song_obj - - def pull_songs(self, song_ref: Reference = None, album_ref: Reference = None, exclude_relations: set = set()) -> \ - List[Song]: - """ - This function is used to get one song (including its children like Sources etc) - from one song id (a reference object) - :param exclude_relations: - By default all relations are pulled by this funktion. If the class object of for - example the Artists is in the set it won't get fetched. - This is done to prevent an infinite recursion. - :param song_ref: - :param album_ref: - :return requested_song: - """ - - where = "1=1" - if song_ref is not None: - where = f"Song.id=\"{song_ref.id}\"" - elif album_ref is not None: - where = f"Song.album_id=\"{album_ref.id}\"" - - query = SONG_QUERY.format(where=where) - self.cursor.execute(query) - - song_rows = self.cursor.fetchall() - - return [self.get_song_from_row( - song_result=song_result, - exclude_relations=exclude_relations - ) for song_result in song_rows] - - def get_album_from_row(self, album_result, exclude_relations=None) -> Album: - if exclude_relations is None: - exclude_relations = set() - new_exclude_relations: set = exclude_relations.copy() - new_exclude_relations.add(Album) - - album_id = album_result['album_id'] - language = album_result['language'] - if language is not None: - language = pycountry.languages.get(alpha_3=album_result['language']) - - album_obj = Album( - id_=album_id, - title=album_result['title'], - label=album_result['label'], - album_status=album_result['album_status'], - language=language, - date=ID3Timestamp.strptime(album_result['date'], album_result['date_format']), - country=album_result['country'], - barcode=album_result['barcode'], - is_split=album_result['is_split'], - albumsort=album_result['albumsort'], - source_list=self.pull_sources(album_ref=Reference(id_=album_id)) - ) - - if Song not in exclude_relations: - # getting the tracklist - tracklist: List[Song] = self.pull_songs( - album_ref=Reference(id_=album_id), - exclude_relations=new_exclude_relations + # SQLITE + if self.db_type == DatabaseType.SQLITE: + return SqliteDatabase(self.db_name) + + # POSTGRES + if self.db_type == DatabaseType.POSTGRESQL: + return PostgresqlDatabase( + self.db_name, + user=self.db_user, + password=self.db_password, + host=self.db_host, + port=self.db_port, ) - album_obj.set_tracklist(tracklist=tracklist) - flat_artist = Artist in exclude_relations - for _, artist_ref in self.pull_artist_album(album_ref=Reference(id_=album_id)): - artists = self.pull_artists(artist_ref, flat=flat_artist, exclude_relations=new_exclude_relations) - if len(artists) < 1: - continue - album_obj.artists.append(artists[0]) + # MYSQL + if self.db_type == DatabaseType.MYSQL: + return MySQLDatabase( + self.db_name, + user=self.db_user, + password=self.db_password, + host=self.db_host, + port=self.db_port, + ) - return album_obj + raise ValueError("define a Valid database type") - def pull_albums(self, album_ref: Reference = None, song_ref: Reference = None, exclude_relations: set = None) -> \ - List[Album]: + def initialize_database(self): """ - This function is used to get matching albums/releses - from one song id (a reference object) - :param exclude_relations: - By default all relations are pulled by this funktion. If the class object of for - example the Artists is in the set it won't get fetched. - This is done to prevent an infinite recursion. - :param album_ref: - :return requested_album_list: + Connect to the database + initialize the previously defined databases + create tables if they don't exist. """ - if exclude_relations is None: - exclude_relations = set() + self.database = self.create_database() - query = ALBUM_QUERY_UNJOINED - where = "1=1" - if album_ref is not None: - query = ALBUM_QUERY_UNJOINED - where = f"Album.id=\"{album_ref.id}\"" - elif song_ref is not None: - query = ALBUM_QUERY_JOINED - where = f"Song.id=\"{song_ref.id}\"" - - query = query.format(where=where) - self.cursor.execute(query) - - album_rows = self.cursor.fetchall() - - return [self.get_album_from_row( - album_result=album_row, - exclude_relations=exclude_relations - ) for album_row in album_rows] - - -if __name__ == "__main__": - cache = Database("") + self.database.connect() diff --git a/src/music_kraken/database/object_cache.py b/src/music_kraken/database/object_cache.py index b82fb25..a9c924c 100644 --- a/src/music_kraken/database/object_cache.py +++ b/src/music_kraken/database/object_cache.py @@ -2,7 +2,7 @@ from collections import defaultdict from typing import Dict, List, Optional import weakref -from .objects import MusicObject +from src.music_kraken.objects import MusicObject """ This is a cache for the objects, that et pulled out of the database. diff --git a/src/music_kraken/database/old_database.py b/src/music_kraken/database/old_database.py new file mode 100644 index 0000000..c0d068d --- /dev/null +++ b/src/music_kraken/database/old_database.py @@ -0,0 +1,700 @@ +import sqlite3 +import os +import logging +from typing import List, Tuple +from pkg_resources import resource_string +import pycountry + +from src.music_kraken.objects.parents import Reference +from src.music_kraken.objects.source import Source +from src.music_kraken.objects import ( + Song, + Lyrics, + Target, + Artist, + Album, + ID3Timestamp, + SourceTypes, + SourcePages +) + +""" +import peewee + +db = peewee.SqliteDatabase('music.db') + +class BaseModel(peewee.Model): + class Meta: + database = db + +class Artist(BaseModel): + name = peewee.CharField() + +class Song(BaseModel): + title = peewee.CharField() + artist = peewee.ManyToManyField(Artist, backref='songs') + +db.connect() +db.create_tables([Artist, Song, Song.artist.get_through_model()], safe=True) + +# Adding a song and its artists +beatles = Artist.create(name='The Beatles') +rolling_stones = Artist.create(name='The Rolling Stones') +song = Song.create(title='Hey Jude') +song.artist.add(beatles, rolling_stones) + +# Querying songs by artist +songs = Song.select().join(Song.artist).where(Artist.name == 'The Beatles') +for song in songs: + print(song.title) + +""" + +logger = logging.getLogger("database") + +# Due to this not being deployed on a Server **HOPEFULLY** +# I don't need to parameterize stuff like the where and +# use complicated query builder +SONG_QUERY = """ +SELECT +Song.id AS song_id, Song.name AS title, Song.isrc AS isrc, Song.length AS length, Song.album_id as album_id, Song.tracksort, +Target.id AS target_id, Target.file AS file, Target.path AS path, Song.genre AS genre +FROM Song +LEFT JOIN Target ON Song.id=Target.song_id +WHERE {where}; +""" +SOURCE_QUERY = """ +SELECT id, type, src, url, song_id +FROM Source +WHERE {where}; +""" +LYRICS_QUERY = """ +SELECT id, text, language, song_id +FROM Lyrics +WHERE {where}; +""" +ALBUM_QUERY_UNJOINED = """ +SELECT Album.id AS album_id, title, label, album_status, language, date, date_format, country, barcode, albumsort, is_split +FROM Album +WHERE {where}; +""" +ALBUM_QUERY_JOINED = """ +SELECT a.id AS album_id, a.title, a.label, a.album_status, a.language, a.date, a.date_format, a.country, a.barcode, a.albumsort, a.is_split +FROM Song +INNER JOIN Album a ON Song.album_id=a.id +WHERE {where}; +""" +ARTIST_QUERY = """ +SELECT id as artist_id, name as artist_name +FROM Artist +WHERE {where}; +""" + + +class Database: + def __init__(self, database_file: str): + self.database_file: str = database_file + self.connection, self.cursor = self.reset_cursor() + + self.cursor = self.connection.cursor() + + def reset(self): + """ + Deletes all Data from the database if it exists + and resets the schema defined in self.structure_file + """ + logger.info(f"resetting the database") + + # deleting the database + del self.connection + del self.cursor + os.remove(self.database_file) + + # newly creating the database + self.reset_cursor() + query = resource_string("music_kraken", "static_files/new_db.sql").decode('utf-8') + + # fill the database with the schematic + self.cursor.executescript(query) + self.connection.commit() + + def reset_cursor(self) -> Tuple[sqlite3.Connection, sqlite3.Cursor]: + self.connection = sqlite3.connect(self.database_file) + # This is necessary that fetching rows returns dicts instead of tuple + self.connection.row_factory = sqlite3.Row + + self.cursor = self.connection.cursor() + return self.connection, self.cursor + + def push_one(self, db_object: Song | Lyrics | Target | Artist | Source | Album): + if db_object.dynamic: + return + + if type(db_object) == Song: + return self.push_song(song=db_object, pushed=set()) + + """ + if type(db_object) == Lyrics: + return self.push_lyrics(lyrics=db_object) + + if type(db_object) == Target: + return self.push_target(target=db_object) + """ + + if type(db_object) == Artist: + return self.push_artist(artist=db_object, pushed=set()) + + """ + if type(db_object) == Source: + # needs to have the property type_enum or type_str set + return self.push_source(source=db_object) + """ + + if type(db_object) == Album: + return self.push_album(album=db_object, pushed=set()) + + logger.warning(f"type {type(db_object)} isn't yet supported by the db") + + def push(self, db_object_list: List[Song | Lyrics | Target | Artist | Source | Album]): + """ + This function is used to Write the data of any db_object to the database + + It syncs a whole list of db_objects to the database and is meant + as the primary method to add to the database. + + :param db_object_list: + """ + + for db_object in db_object_list: + self.push_one(db_object) + + def push_album(self, album: Album, pushed: set): + table = "Album" + query = f"INSERT OR REPLACE INTO {table} (id, title, label, album_status, language, date, date_format, country, barcode, albumsort, is_split) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);" + + if album.id in pushed: + return + pushed.add(album.id) + + date_format, date = album.date.get_timestamp_w_format() + + values = ( + album.id, + album.title, + album.label, + album.album_status, + album.iso_639_2_language, + date, + date_format, + album.country, + album.barcode, + album.albumsort, + album.is_split + ) + self.cursor.execute(query, values) + self.connection.commit() + + for song in album.tracklist: + self.push_song(song, pushed=pushed) + for artist in album.artists: + self.push_artist_album(artist_ref=artist.reference, album_ref=album.reference) + self.push_artist(artist, pushed=pushed) + + for source in album.source_list: + source.type_enum = SourceTypes.ALBUM + source.add_song(album) + self.push_source(source=source) + + def push_song(self, song: Song, pushed: set): + if song.dynamic: + return + + if song.id in pushed: + return + pushed.add(song.id) + + # ADDING THE DATA FOR THE SONG OBJECT + """ + db_field - object attribute + ------------------------------- + id - id + name - title + """ + table = "Song" + + values = ( + song.id, + song.title, + song.isrc, + song.length, + song.get_album_id(), + song.tracksort, + song.genre + ) + query = f"INSERT OR REPLACE INTO {table} (id, name, isrc, length, album_id, tracksort, genre) VALUES (?, ?, ?, ?, ?, ?, ?);" + + self.cursor.execute(query, values) + self.connection.commit() + + # add sources + for source in song.source_list: + source.add_song(song) + source.type_enum = SourceTypes.SONG + self.push_source(source=source) + + # add lyrics + for single_lyrics in song.lyrics: + single_lyrics.add_song(song) + self.push_lyrics(lyrics=single_lyrics) + + # add target + song.target.add_song(song) + self.push_target(target=song.target) + + for main_artist in song.main_artist_list: + self.push_artist_song(artist_ref=Reference(main_artist.id), song_ref=Reference(song.id), is_feature=False) + self.push_artist(artist=main_artist, pushed=pushed) + + for feature_artist in song.feature_artist_list: + self.push_artist_song(artist_ref=Reference(feature_artist.id), song_ref=Reference(song.id), is_feature=True) + self.push_artist(artist=feature_artist, pushed=pushed) + + if song.album is not None: + self.push_album(song.album, pushed=pushed) + + def push_lyrics(self, lyrics: Lyrics): + if lyrics.dynamic: + return + + table = "Lyrics" + query = f"INSERT OR REPLACE INTO {table} (id, song_id, text, language) VALUES (?, ?, ?, ?);" + values = ( + lyrics.id, + lyrics.song_ref_id, + lyrics.text, + lyrics.language + ) + + self.cursor.execute(query, values) + self.connection.commit() + + def push_source(self, source: Source): + if source.dynamic: + return + + table = "Source" + query = f"INSERT OR REPLACE INTO {table} (id, type, song_id, src, url) VALUES (?, ?, ?, ?, ?);" + values = ( + source.id, + source.type_str, + source.song_ref_id, + source.page_str, + source.url + ) + + self.cursor.execute(query, values) + self.connection.commit() + + def push_target(self, target: Target): + if target.dynamic: + return + + table = "Target" + query = f"INSERT OR REPLACE INTO {table} (id, song_id, file, path) VALUES (?, ?, ?, ?);" + values = ( + target.id, + target.song_ref_id, + target.file, + target.path + ) + + self.cursor.execute(query, values) + self.connection.commit() + + def push_artist_song(self, artist_ref: Reference, song_ref: Reference, is_feature: bool): + table = "SongArtist" + # checking if already exists + query = f"SELECT * FROM {table} WHERE song_id=\"{song_ref.id}\" AND artist_id=\"{artist_ref.id}\"" + self.cursor.execute(query) + if len(self.cursor.fetchall()) > 0: + # join already exists + return + + query = f"INSERT OR REPLACE INTO {table} (song_id, artist_id, is_feature) VALUES (?, ?, ?);" + values = ( + song_ref.id, + artist_ref.id, + is_feature + ) + + self.cursor.execute(query, values) + self.connection.commit() + + def push_artist_album(self, artist_ref: Reference, album_ref: Reference): + table = "AlbumArtist" + # checking if already exists + query = f"SELECT * FROM {table} WHERE album_id=\"{album_ref.id}\" AND artist_id=\"{artist_ref.id}\"" + self.cursor.execute(query) + if len(self.cursor.fetchall()) > 0: + # join already exists + return + + query = f"INSERT OR REPLACE INTO {table} (album_id, artist_id) VALUES (?, ?);" + values = ( + album_ref.id, + artist_ref.id + ) + + self.cursor.execute(query, values) + self.connection.commit() + + def push_artist(self, artist: Artist, pushed: set): + if artist.dynamic: + return + if artist.id in pushed: + return + pushed.add(artist.id) + + table = "Artist" + query = f"INSERT OR REPLACE INTO {table} (id, name) VALUES (?, ?);" + values = ( + artist.id, + artist.name + ) + + self.cursor.execute(query, values) + self.connection.commit() + + for song in artist.feature_songs: + self.push_artist_song(artist_ref=artist.reference, song_ref=song.reference, is_feature=True) + self.push_song(song=song, pushed=pushed) + + for song in artist.main_songs: + self.push_artist_song(artist_ref=artist.reference, song_ref=song.reference, is_feature=False) + self.push_song(song=song, pushed=pushed) + + for album in artist.main_albums: + self.push_artist_album(artist_ref=artist.reference, album_ref=album.reference) + + for source in artist.source_list: + source.type_enum = SourceTypes.ARTIST + source.add_song(artist) + self.push_source(source) + + def pull_lyrics(self, song_ref: Reference = None, lyrics_ref: Reference = None) -> List[Lyrics]: + """ + Gets a list of sources. if lyrics_ref is passed in the List will most likely only + contain one Element if everything goes accordingly. + **If neither song_ref nor lyrics_ref are passed in it will return ALL lyrics** + :param song_ref: + :param lyrics_ref: + :return: + """ + + where = "1=1" + if song_ref is not None: + where = f"song_id=\"{song_ref.id}\"" + elif lyrics_ref is not None: + where = f"id=\"{lyrics_ref.id}\"" + + query = LYRICS_QUERY.format(where=where) + self.cursor.execute(query) + + lyrics_rows = self.cursor.fetchall() + return [Lyrics( + id_=lyrics_row['id'], + text=lyrics_row['text'], + language=lyrics_row['language'] + ) for lyrics_row in lyrics_rows] + + def pull_sources(self, artist_ref: Reference = None, song_ref: Reference = None, source_ref: Reference = None, album_ref: Reference = None) -> List[Source]: + """ + Gets a list of sources. if source_ref is passed in the List will most likely only + contain one Element if everything goes accordingly. + **If neither song_ref nor source_ref are passed in it will return ALL sources** + :param artist_ref: + :param song_ref: + :param source_ref: + :param type_str: the thing the source belongs to like eg. "song" or "album" + :return: + """ + + where = "1=1" + if song_ref is not None: + where = f"song_id=\"{song_ref.id}\"" + elif source_ref is not None: + where = f"id=\"{source_ref.id}\" AND type=\"{SourceTypes.SONG.value}\"" + elif artist_ref is not None: + where = f"song_id=\"{artist_ref.id}\" AND type=\"{SourceTypes.ARTIST.value}\"" + elif album_ref is not None: + where = f"song_id=\"{album_ref.id}\" AND type=\"{SourceTypes.ALBUM.value}\"" + + query = SOURCE_QUERY.format(where=where) + self.cursor.execute(query) + + source_rows = self.cursor.fetchall() + + return [ + Source( + page_enum=SourcePages(source_row['src']), + type_enum=SourceTypes(source_row['type']), + url=source_row['url'], + id_=source_row['id'] + ) for source_row in source_rows + ] + + def pull_artist_song(self, song_ref: Reference = None, artist_ref: Reference = None) -> List[tuple]: + table = "SongArtist" + wheres = [] + if song_ref is not None: + wheres.append(f"song_id=\"{song_ref.id}\"") + if artist_ref is not None: + wheres.append(f"artist_id=\"{artist_ref.id}\"") + where_str = "" + if len(wheres) > 0: + where_str = "WHERE " + " AND ".join(wheres) + + query = f"SELECT * FROM {table} {where_str};" + self.cursor.execute(query) + joins = self.cursor.fetchall() + + return [( + Reference(join["song_id"]), + Reference(join["artist_id"]), + bool(join["is_feature"]) + ) for join in joins] + + def pull_artist_album(self, album_ref: Reference = None, artist_ref: Reference = None) -> List[tuple]: + table = "AlbumArtist" + wheres = [] + if album_ref is not None: + wheres.append(f"album_id=\"{album_ref.id}\"") + if artist_ref is not None: + wheres.append(f"artist_id=\"{artist_ref.id}\"") + where_str = "" + if len(wheres) > 0: + where_str = "WHERE " + " AND ".join(wheres) + + query = f"SELECT * FROM {table} {where_str};" + self.cursor.execute(query) + joins = self.cursor.fetchall() + + return [( + Reference(join["album_id"]), + Reference(join["artist_id"]) + ) for join in joins] + + def get_artist_from_row(self, artist_row, exclude_relations: set = None, flat: bool = False) -> Artist: + if exclude_relations is None: + exclude_relations = set() + new_exclude_relations: set = set(exclude_relations) + new_exclude_relations.add(Artist) + + artist_id = artist_row['artist_id'] + + artist_obj = Artist( + id_=artist_id, + name=artist_row['artist_name'], + source_list=self.pull_sources(artist_ref=Reference(id_=artist_id)) + ) + if flat: + return artist_obj + + # fetch songs :D + for song_ref, _, is_feature in self.pull_artist_song(artist_ref=Reference(id_=artist_id)): + new_songs = self.pull_songs(song_ref=song_ref, exclude_relations=new_exclude_relations) + if len(new_songs) < 1: + continue + new_song = new_songs[0] + + if is_feature: + artist_obj.feature_songs.append(new_song) + else: + artist_obj.main_songs.append(new_song) + + # fetch albums + for album_ref, _ in self.pull_artist_album(artist_ref=Reference(id_=artist_id)): + new_albums = self.pull_albums(album_ref=album_ref, exclude_relations=new_exclude_relations) + if len(new_albums) < 1: + continue + artist_obj.main_albums.append(new_albums[0]) + + return artist_obj + + def pull_artists(self, artist_ref: Reference = None, exclude_relations: set = None, flat: bool = False) -> List[Artist]: + """ + + :param artist_ref: + :param exclude_relations: + :param flat: if it is true it ONLY fetches the artist data + :return: + """ + + where = "1=1" + if artist_ref is not None: + where = f"Artist.id=\"{artist_ref.id}\"" + + query = ARTIST_QUERY.format(where=where) + self.cursor.execute(query) + + artist_rows = self.cursor.fetchall() + return [( + self.get_artist_from_row(artist_row, exclude_relations=exclude_relations, flat=flat) + ) for artist_row in artist_rows] + + def get_song_from_row(self, song_result, exclude_relations: set = None) -> Song: + if exclude_relations is None: + exclude_relations = set() + new_exclude_relations: set = set(exclude_relations) + new_exclude_relations.add(Song) + + song_id = song_result['song_id'] + + # maybee fetch album + + song_obj = Song( + id_=song_id, + title=song_result['title'], + isrc=song_result['isrc'], + length=song_result['length'], + tracksort=song_result['tracksort'], + genre=song_result['genre'], + target=Target( + id_=song_result['target_id'], + file=song_result['file'], + path=song_result['path'] + ), + source_list=self.pull_sources(song_ref=Reference(id_=song_id)), + lyrics=self.pull_lyrics(song_ref=Reference(id_=song_id)), + ) + + if Album not in exclude_relations and song_result['album_id'] is not None: + album_obj = self.pull_albums(album_ref=Reference(song_result['album_id']), + exclude_relations=new_exclude_relations) + if len(album_obj) > 0: + song_obj.album = album_obj[0] + + flat_artist = Artist in exclude_relations + + main_artists = [] + feature_artists = [] + for song_ref, artist_ref, is_feature in self.pull_artist_song(song_ref=Reference(song_id)): + if is_feature: + feature_artists.extend(self.pull_artists(artist_ref=artist_ref, flat=flat_artist)) + else: + main_artists.extend(self.pull_artists(artist_ref=artist_ref, flat=flat_artist)) + + song_obj.main_artist_list = main_artists + song_obj.feature_artist_list = feature_artists + + return song_obj + + def pull_songs(self, song_ref: Reference = None, album_ref: Reference = None, exclude_relations: set = set()) -> \ + List[Song]: + """ + This function is used to get one song (including its children like Sources etc) + from one song id (a reference object) + :param exclude_relations: + By default all relations are pulled by this funktion. If the class object of for + example the Artists is in the set it won't get fetched. + This is done to prevent an infinite recursion. + :param song_ref: + :param album_ref: + :return requested_song: + """ + + where = "1=1" + if song_ref is not None: + where = f"Song.id=\"{song_ref.id}\"" + elif album_ref is not None: + where = f"Song.album_id=\"{album_ref.id}\"" + + query = SONG_QUERY.format(where=where) + self.cursor.execute(query) + + song_rows = self.cursor.fetchall() + + return [self.get_song_from_row( + song_result=song_result, + exclude_relations=exclude_relations + ) for song_result in song_rows] + + def get_album_from_row(self, album_result, exclude_relations=None) -> Album: + if exclude_relations is None: + exclude_relations = set() + new_exclude_relations: set = exclude_relations.copy() + new_exclude_relations.add(Album) + + album_id = album_result['album_id'] + language = album_result['language'] + if language is not None: + language = pycountry.languages.get(alpha_3=album_result['language']) + + album_obj = Album( + id_=album_id, + title=album_result['title'], + label=album_result['label'], + album_status=album_result['album_status'], + language=language, + date=ID3Timestamp.strptime(album_result['date'], album_result['date_format']), + country=album_result['country'], + barcode=album_result['barcode'], + is_split=album_result['is_split'], + albumsort=album_result['albumsort'], + source_list=self.pull_sources(album_ref=Reference(id_=album_id)) + ) + + if Song not in exclude_relations: + # getting the tracklist + tracklist: List[Song] = self.pull_songs( + album_ref=Reference(id_=album_id), + exclude_relations=new_exclude_relations + ) + album_obj.set_tracklist(tracklist=tracklist) + + flat_artist = Artist in exclude_relations + for _, artist_ref in self.pull_artist_album(album_ref=Reference(id_=album_id)): + artists = self.pull_artists(artist_ref, flat=flat_artist, exclude_relations=new_exclude_relations) + if len(artists) < 1: + continue + album_obj.artists.append(artists[0]) + + return album_obj + + def pull_albums(self, album_ref: Reference = None, song_ref: Reference = None, exclude_relations: set = None) -> \ + List[Album]: + """ + This function is used to get matching albums/releses + from one song id (a reference object) + :param exclude_relations: + By default all relations are pulled by this funktion. If the class object of for + example the Artists is in the set it won't get fetched. + This is done to prevent an infinite recursion. + :param album_ref: + :return requested_album_list: + """ + if exclude_relations is None: + exclude_relations = set() + + query = ALBUM_QUERY_UNJOINED + where = "1=1" + if album_ref is not None: + query = ALBUM_QUERY_UNJOINED + where = f"Album.id=\"{album_ref.id}\"" + elif song_ref is not None: + query = ALBUM_QUERY_JOINED + where = f"Song.id=\"{song_ref.id}\"" + + query = query.format(where=where) + self.cursor.execute(query) + + album_rows = self.cursor.fetchall() + + return [self.get_album_from_row( + album_result=album_row, + exclude_relations=exclude_relations + ) for album_row in album_rows] + + +if __name__ == "__main__": + cache = Database("") diff --git a/src/music_kraken/database/temp_database.py b/src/music_kraken/database/temp_database.py index 4055b89..048b1bd 100644 --- a/src/music_kraken/database/temp_database.py +++ b/src/music_kraken/database/temp_database.py @@ -1,4 +1,4 @@ -from .database import Database +from .old_database import Database from ..utils.shared import ( TEMP_DATABASE_PATH, diff --git a/src/music_kraken/database/objects/__init__.py b/src/music_kraken/objects/__init__.py similarity index 100% rename from src/music_kraken/database/objects/__init__.py rename to src/music_kraken/objects/__init__.py diff --git a/src/music_kraken/database/objects/artist.py b/src/music_kraken/objects/artist.py similarity index 91% rename from src/music_kraken/database/objects/artist.py rename to src/music_kraken/objects/artist.py index e06ea05..b035903 100644 --- a/src/music_kraken/database/objects/artist.py +++ b/src/music_kraken/objects/artist.py @@ -1,4 +1,4 @@ -from ...utils.shared import ( +from src.music_kraken.utils.shared import ( DATABASE_LOGGER as logger ) from .parents import ( diff --git a/src/music_kraken/database/objects/collection.py b/src/music_kraken/objects/collection.py similarity index 97% rename from src/music_kraken/database/objects/collection.py rename to src/music_kraken/objects/collection.py index 8592a45..19d7a5a 100644 --- a/src/music_kraken/database/objects/collection.py +++ b/src/music_kraken/objects/collection.py @@ -1,7 +1,7 @@ from typing import List from .source import SourceAttribute -from ...utils import string_processing +from src.music_kraken.utils import string_processing class Collection: """ diff --git a/src/music_kraken/database/objects/formatted_text.py b/src/music_kraken/objects/formatted_text.py similarity index 100% rename from src/music_kraken/database/objects/formatted_text.py rename to src/music_kraken/objects/formatted_text.py diff --git a/src/music_kraken/database/objects/metadata.py b/src/music_kraken/objects/metadata.py similarity index 100% rename from src/music_kraken/database/objects/metadata.py rename to src/music_kraken/objects/metadata.py diff --git a/src/music_kraken/database/objects/parents.py b/src/music_kraken/objects/parents.py similarity index 98% rename from src/music_kraken/database/objects/parents.py rename to src/music_kraken/objects/parents.py index b20f620..d3701db 100644 --- a/src/music_kraken/database/objects/parents.py +++ b/src/music_kraken/objects/parents.py @@ -1,6 +1,6 @@ import uuid -from ...utils.shared import ( +from src.music_kraken.utils.shared import ( SONG_LOGGER as logger ) diff --git a/src/music_kraken/database/objects/song.py b/src/music_kraken/objects/song.py similarity index 99% rename from src/music_kraken/database/objects/song.py rename to src/music_kraken/objects/song.py index c7cd9de..fc4a526 100644 --- a/src/music_kraken/database/objects/song.py +++ b/src/music_kraken/objects/song.py @@ -8,7 +8,7 @@ from .metadata import ( ID3Timestamp, MetadataAttribute ) -from ...utils.shared import ( +from src.music_kraken.utils.shared import ( MUSIC_DIR, DATABASE_LOGGER as logger ) diff --git a/src/music_kraken/database/objects/source.py b/src/music_kraken/objects/source.py similarity index 100% rename from src/music_kraken/database/objects/source.py rename to src/music_kraken/objects/source.py diff --git a/src/try_python.py b/src/try_python.py index e69de29..d44e67a 100644 --- a/src/try_python.py +++ b/src/try_python.py @@ -0,0 +1,41 @@ +from typing import Optional + + +class TreeNode: + """A class representing a binary tree node.""" + + def __init__(self, val: int = 0, left: Optional['TreeNode'] = None, right: Optional['TreeNode'] = None): + """ + Initializes a new instance of the TreeNode class. + + Args: + val: The value of the node. + left: The left child of the node. + right: The right child of the node. + """ + self.val = val + self.left = left + self.right = right + + +def invert_tree(root: Optional[TreeNode]) -> Optional[TreeNode]: + """ + Inverts a binary tree. + + Args: + root: The root node of the binary tree. + + Returns: + The root node of the inverted binary tree. + """ + if root is None: + return None + + # Swap left and right children of the root node + root.left, root.right = root.right, root.left + + # Recursively invert the left and right subtrees + invert_tree(root.left) + invert_tree(root.right) + + return root