diff --git a/.VSCodeCounter/2023-03-28_16-46-52/details.md b/.VSCodeCounter/2023-03-28_16-46-52/details.md deleted file mode 100644 index a2213e0..0000000 --- a/.VSCodeCounter/2023-03-28_16-46-52/details.md +++ /dev/null @@ -1,81 +0,0 @@ -# Details - -Date : 2023-03-28 16:46:52 - -Directory /home/lars/Projects/music-downloader/src - -Total : 66 files, 4673 codes, 1082 comments, 1358 blanks, all 7113 lines - -[Summary](results.md) / Details / [Diff Summary](diff.md) / [Diff Details](diff-details.md) - -## Files -| filename | language | code | comment | blank | total | -| :--- | :--- | ---: | ---: | ---: | ---: | -| [src/__init__.py](/src/__init__.py) | Python | 0 | 0 | 1 | 1 | -| [src/create_custom_objects.py](/src/create_custom_objects.py) | Python | 58 | 0 | 6 | 64 | -| [src/donwload.py](/src/donwload.py) | Python | 9 | 0 | 6 | 15 | -| [src/metal_archives.py](/src/metal_archives.py) | Python | 30 | 0 | 12 | 42 | -| [src/music_kraken/__init__.py](/src/music_kraken/__init__.py) | Python | 24 | 7 | 14 | 45 | -| [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 | 0 | 0 | 1 | 1 | -| [src/music_kraken/database/data_models.py](/src/music_kraken/database/data_models.py) | Python | 122 | 24 | 52 | 198 | -| [src/music_kraken/database/database.py](/src/music_kraken/database/database.py) | Python | 104 | 47 | 38 | 189 | -| [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 | -| [src/music_kraken/not_used_anymore/fetch_source.py](/src/music_kraken/not_used_anymore/fetch_source.py) | Python | 54 | 1 | 16 | 71 | -| [src/music_kraken/not_used_anymore/metadata/__init__.py](/src/music_kraken/not_used_anymore/metadata/__init__.py) | Python | 6 | 0 | 2 | 8 | -| [src/music_kraken/not_used_anymore/metadata/metadata_fetch.py](/src/music_kraken/not_used_anymore/metadata/metadata_fetch.py) | Python | 257 | 24 | 65 | 346 | -| [src/music_kraken/not_used_anymore/metadata/metadata_search.py](/src/music_kraken/not_used_anymore/metadata/metadata_search.py) | Python | 253 | 40 | 72 | 365 | -| [src/music_kraken/not_used_anymore/metadata/sources/__init__.py](/src/music_kraken/not_used_anymore/metadata/sources/__init__.py) | Python | 3 | 0 | 2 | 5 | -| [src/music_kraken/not_used_anymore/metadata/sources/musicbrainz.py](/src/music_kraken/not_used_anymore/metadata/sources/musicbrainz.py) | Python | 42 | 6 | 12 | 60 | -| [src/music_kraken/not_used_anymore/sources/__init__.py](/src/music_kraken/not_used_anymore/sources/__init__.py) | Python | 0 | 0 | 1 | 1 | -| [src/music_kraken/not_used_anymore/sources/genius.py](/src/music_kraken/not_used_anymore/sources/genius.py) | Python | 115 | 16 | 42 | 173 | -| [src/music_kraken/not_used_anymore/sources/local_files.py](/src/music_kraken/not_used_anymore/sources/local_files.py) | Python | 40 | 0 | 18 | 58 | -| [src/music_kraken/not_used_anymore/sources/musify.py](/src/music_kraken/not_used_anymore/sources/musify.py) | Python | 136 | 9 | 37 | 182 | -| [src/music_kraken/not_used_anymore/sources/source.py](/src/music_kraken/not_used_anymore/sources/source.py) | Python | 11 | 5 | 8 | 24 | -| [src/music_kraken/not_used_anymore/sources/youtube.py](/src/music_kraken/not_used_anymore/sources/youtube.py) | Python | 71 | 4 | 24 | 99 | -| [src/music_kraken/objects/__init__.py](/src/music_kraken/objects/__init__.py) | Python | 28 | 0 | 8 | 36 | -| [src/music_kraken/objects/album.py](/src/music_kraken/objects/album.py) | Python | 16 | 6 | 5 | 27 | -| [src/music_kraken/objects/cache.py](/src/music_kraken/objects/cache.py) | Python | 37 | 56 | 18 | 111 | -| [src/music_kraken/objects/collection.py](/src/music_kraken/objects/collection.py) | Python | 85 | 30 | 37 | 152 | -| [src/music_kraken/objects/formatted_text.py](/src/music_kraken/objects/formatted_text.py) | Python | 50 | 10 | 19 | 79 | -| [src/music_kraken/objects/lyrics.py](/src/music_kraken/objects/lyrics.py) | Python | 25 | 0 | 7 | 32 | -| [src/music_kraken/objects/metadata.py](/src/music_kraken/objects/metadata.py) | Python | 259 | 62 | 60 | 381 | -| [src/music_kraken/objects/option.py](/src/music_kraken/objects/option.py) | Python | 23 | 0 | 11 | 34 | -| [src/music_kraken/objects/parents.py](/src/music_kraken/objects/parents.py) | Python | 66 | 31 | 31 | 128 | -| [src/music_kraken/objects/song.py](/src/music_kraken/objects/song.py) | Python | 445 | 83 | 95 | 623 | -| [src/music_kraken/objects/source.py](/src/music_kraken/objects/source.py) | Python | 118 | 17 | 40 | 175 | -| [src/music_kraken/objects/target.py](/src/music_kraken/objects/target.py) | Python | 29 | 7 | 9 | 45 | -| [src/music_kraken/pages/__init__.py](/src/music_kraken/pages/__init__.py) | Python | 6 | 0 | 5 | 11 | -| [src/music_kraken/pages/abstract.py](/src/music_kraken/pages/abstract.py) | Python | 180 | 32 | 53 | 265 | -| [src/music_kraken/pages/download_center/__init__.py](/src/music_kraken/pages/download_center/__init__.py) | Python | 2 | 0 | 2 | 4 | -| [src/music_kraken/pages/download_center/page_attributes.py](/src/music_kraken/pages/download_center/page_attributes.py) | Python | 14 | 0 | 6 | 20 | -| [src/music_kraken/pages/download_center/search.py](/src/music_kraken/pages/download_center/search.py) | Python | 98 | 8 | 42 | 148 | -| [src/music_kraken/pages/encyclopaedia_metallum.py](/src/music_kraken/pages/encyclopaedia_metallum.py) | Python | 441 | 86 | 111 | 638 | -| [src/music_kraken/pages/musify.py](/src/music_kraken/pages/musify.py) | Python | 500 | 248 | 135 | 883 | -| [src/music_kraken/pages/youtube.py](/src/music_kraken/pages/youtube.py) | Python | 25 | 16 | 6 | 47 | -| [src/music_kraken/static_files/new_db.sql](/src/music_kraken/static_files/new_db.sql) | SQLite | 72 | 0 | 10 | 82 | -| [src/music_kraken/static_files/temp_database_structure.sql](/src/music_kraken/static_files/temp_database_structure.sql) | SQLite | 135 | 0 | 10 | 145 | -| [src/music_kraken/tagging/__init__.py](/src/music_kraken/tagging/__init__.py) | Python | 8 | 0 | 2 | 10 | -| [src/music_kraken/tagging/id3.py](/src/music_kraken/tagging/id3.py) | Python | 51 | 4 | 20 | 75 | -| [src/music_kraken/utils/__init__.py](/src/music_kraken/utils/__init__.py) | Python | 1 | 1 | 1 | 3 | -| [src/music_kraken/utils/functions.py](/src/music_kraken/utils/functions.py) | Python | 3 | 0 | 1 | 4 | -| [src/music_kraken/utils/object_handeling.py](/src/music_kraken/utils/object_handeling.py) | Python | 19 | 0 | 6 | 25 | -| [src/music_kraken/utils/phonetic_compares.py](/src/music_kraken/utils/phonetic_compares.py) | Python | 39 | 2 | 17 | 58 | -| [src/music_kraken/utils/shared.py](/src/music_kraken/utils/shared.py) | Python | 62 | 3 | 10 | 75 | -| [src/music_kraken/utils/string_processing.py](/src/music_kraken/utils/string_processing.py) | Python | 2 | 5 | 2 | 9 | -| [src/music_kraken_cli.py](/src/music_kraken_cli.py) | Python | 3 | 0 | 3 | 6 | -| [src/music_kraken_gtk.py](/src/music_kraken_gtk.py) | Python | 3 | 0 | 2 | 5 | -| [src/musify_search.py](/src/musify_search.py) | Python | 26 | 0 | 11 | 37 | -| [src/python.py](/src/python.py) | Python | 12 | 43 | 6 | 61 | -| [src/test.db](/src/test.db) | Database | 91 | 0 | 1 | 92 | -| [src/test.py](/src/test.py) | Python | 1 | 0 | 0 | 1 | -| [src/tests/__init__.py](/src/tests/__init__.py) | Python | 0 | 0 | 1 | 1 | -| [src/tests/conftest.py](/src/tests/conftest.py) | Python | 3 | 1 | 2 | 6 | -| [src/tests/test_building_objects.py](/src/tests/test_building_objects.py) | Python | 81 | 1 | 13 | 95 | -| [src/tests/test_objects.py](/src/tests/test_objects.py) | Python | 173 | 15 | 51 | 239 | -| [src/try-programming-interface.py](/src/try-programming-interface.py) | Python | 14 | 98 | 22 | 134 | -| [src/try.py](/src/try.py) | Python | 1 | 0 | 3 | 4 | -| [src/try_python.py](/src/try_python.py) | Python | 13 | 20 | 9 | 42 | - -[Summary](results.md) / Details / [Diff Summary](diff.md) / [Diff Details](diff-details.md) \ No newline at end of file diff --git a/.VSCodeCounter/2023-03-28_16-46-52/diff-details.md b/.VSCodeCounter/2023-03-28_16-46-52/diff-details.md deleted file mode 100644 index c347a85..0000000 --- a/.VSCodeCounter/2023-03-28_16-46-52/diff-details.md +++ /dev/null @@ -1,55 +0,0 @@ -# Diff Details - -Date : 2023-03-28 16:46:52 - -Directory /home/lars/Projects/music-downloader/src - -Total : 40 files, 523 codes, 22 comments, 130 blanks, all 675 lines - -[Summary](results.md) / [Details](details.md) / [Diff Summary](diff.md) / Diff Details - -## Files -| filename | language | code | comment | blank | total | -| :--- | :--- | ---: | ---: | ---: | ---: | -| [src/create_custom_objects.py](/src/create_custom_objects.py) | Python | -22 | -3 | -12 | -37 | -| [src/donwload.py](/src/donwload.py) | Python | 9 | 0 | 6 | 15 | -| [src/metal_archives.py](/src/metal_archives.py) | Python | -18 | -4 | -3 | -25 | -| [src/music_kraken/__init__.py](/src/music_kraken/__init__.py) | Python | -33 | -1 | -11 | -45 | -| [src/music_kraken/database/__init__.py](/src/music_kraken/database/__init__.py) | Python | -18 | 0 | -4 | -22 | -| [src/music_kraken/database/data_models.py](/src/music_kraken/database/data_models.py) | Python | -1 | 1 | 0 | 0 | -| [src/music_kraken/database/database.py](/src/music_kraken/database/database.py) | Python | 20 | -1 | 12 | 31 | -| [src/music_kraken/database/object_cache.py](/src/music_kraken/database/object_cache.py) | Python | -35 | -56 | -16 | -107 | -| [src/music_kraken/database/old_database.py](/src/music_kraken/database/old_database.py) | Python | -432 | -154 | -115 | -701 | -| [src/music_kraken/database/read.py](/src/music_kraken/database/read.py) | Python | 0 | 0 | -1 | -1 | -| [src/music_kraken/database/temp_database.py](/src/music_kraken/database/temp_database.py) | Python | -12 | 0 | -8 | -20 | -| [src/music_kraken/database/write.py](/src/music_kraken/database/write.py) | Python | -210 | -62 | -63 | -335 | -| [src/music_kraken/objects/__init__.py](/src/music_kraken/objects/__init__.py) | Python | 4 | 0 | 1 | 5 | -| [src/music_kraken/objects/album.py](/src/music_kraken/objects/album.py) | Python | 1 | 0 | 0 | 1 | -| [src/music_kraken/objects/artist.py](/src/music_kraken/objects/artist.py) | Python | -18 | 0 | -5 | -23 | -| [src/music_kraken/objects/cache.py](/src/music_kraken/objects/cache.py) | Python | 37 | 56 | 18 | 111 | -| [src/music_kraken/objects/collection.py](/src/music_kraken/objects/collection.py) | Python | 33 | 15 | 13 | 61 | -| [src/music_kraken/objects/formatted_text.py](/src/music_kraken/objects/formatted_text.py) | Python | -3 | -47 | -1 | -51 | -| [src/music_kraken/objects/lyrics.py](/src/music_kraken/objects/lyrics.py) | Python | 4 | 0 | 0 | 4 | -| [src/music_kraken/objects/metadata.py](/src/music_kraken/objects/metadata.py) | Python | -3 | -6 | -3 | -12 | -| [src/music_kraken/objects/option.py](/src/music_kraken/objects/option.py) | Python | 23 | 0 | 11 | 34 | -| [src/music_kraken/objects/parents.py](/src/music_kraken/objects/parents.py) | Python | 44 | 16 | 19 | 79 | -| [src/music_kraken/objects/song.py](/src/music_kraken/objects/song.py) | Python | 108 | 2 | 5 | 115 | -| [src/music_kraken/objects/source.py](/src/music_kraken/objects/source.py) | Python | 2 | -21 | -1 | -20 | -| [src/music_kraken/objects/target.py](/src/music_kraken/objects/target.py) | Python | 7 | 0 | 2 | 9 | -| [src/music_kraken/pages/__init__.py](/src/music_kraken/pages/__init__.py) | Python | -1 | 0 | 0 | -1 | -| [src/music_kraken/pages/abstract.py](/src/music_kraken/pages/abstract.py) | Python | 107 | -36 | 26 | 97 | -| [src/music_kraken/pages/download_center/__init__.py](/src/music_kraken/pages/download_center/__init__.py) | Python | 2 | 0 | 2 | 4 | -| [src/music_kraken/pages/download_center/page_attributes.py](/src/music_kraken/pages/download_center/page_attributes.py) | Python | 14 | 0 | 6 | 20 | -| [src/music_kraken/pages/download_center/search.py](/src/music_kraken/pages/download_center/search.py) | Python | 98 | 8 | 42 | 148 | -| [src/music_kraken/pages/encyclopaedia_metallum.py](/src/music_kraken/pages/encyclopaedia_metallum.py) | Python | 100 | 20 | 20 | 140 | -| [src/music_kraken/pages/musify.py](/src/music_kraken/pages/musify.py) | Python | 500 | 248 | 135 | 883 | -| [src/music_kraken/target/__init__.py](/src/music_kraken/target/__init__.py) | Python | -4 | 0 | -2 | -6 | -| [src/music_kraken/target/set_target.py](/src/music_kraken/target/set_target.py) | Python | -37 | -7 | -18 | -62 | -| [src/musify_search.py](/src/musify_search.py) | Python | 26 | 0 | 11 | 37 | -| [src/python.py](/src/python.py) | Python | 12 | 43 | 6 | 61 | -| [src/test.py](/src/test.py) | Python | 1 | 0 | 0 | 1 | -| [src/tests/example_data_objects.py](/src/tests/example_data_objects.py) | Python | -36 | -5 | -6 | -47 | -| [src/tests/test_building_objects.py](/src/tests/test_building_objects.py) | Python | 81 | 1 | 13 | 95 | -| [src/tests/test_objects.py](/src/tests/test_objects.py) | Python | 173 | 15 | 51 | 239 | - -[Summary](results.md) / [Details](details.md) / [Diff Summary](diff.md) / Diff Details \ No newline at end of file diff --git a/.VSCodeCounter/2023-03-28_16-46-52/diff.csv b/.VSCodeCounter/2023-03-28_16-46-52/diff.csv deleted file mode 100644 index a6be3a6..0000000 --- a/.VSCodeCounter/2023-03-28_16-46-52/diff.csv +++ /dev/null @@ -1,42 +0,0 @@ -"filename", "language", "Python", "comment", "blank", "total" -"/home/lars/Projects/music-downloader/src/create_custom_objects.py", "Python", -22, -3, -12, -37 -"/home/lars/Projects/music-downloader/src/donwload.py", "Python", 9, 0, 6, 15 -"/home/lars/Projects/music-downloader/src/metal_archives.py", "Python", -18, -4, -3, -25 -"/home/lars/Projects/music-downloader/src/music_kraken/__init__.py", "Python", -33, -1, -11, -45 -"/home/lars/Projects/music-downloader/src/music_kraken/database/__init__.py", "Python", -18, 0, -4, -22 -"/home/lars/Projects/music-downloader/src/music_kraken/database/data_models.py", "Python", -1, 1, 0, 0 -"/home/lars/Projects/music-downloader/src/music_kraken/database/database.py", "Python", 20, -1, 12, 31 -"/home/lars/Projects/music-downloader/src/music_kraken/database/object_cache.py", "Python", -35, -56, -16, -107 -"/home/lars/Projects/music-downloader/src/music_kraken/database/old_database.py", "Python", -432, -154, -115, -701 -"/home/lars/Projects/music-downloader/src/music_kraken/database/read.py", "Python", 0, 0, -1, -1 -"/home/lars/Projects/music-downloader/src/music_kraken/database/temp_database.py", "Python", -12, 0, -8, -20 -"/home/lars/Projects/music-downloader/src/music_kraken/database/write.py", "Python", -210, -62, -63, -335 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/__init__.py", "Python", 4, 0, 1, 5 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/album.py", "Python", 1, 0, 0, 1 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/artist.py", "Python", -18, 0, -5, -23 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/cache.py", "Python", 37, 56, 18, 111 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/collection.py", "Python", 33, 15, 13, 61 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/formatted_text.py", "Python", -3, -47, -1, -51 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/lyrics.py", "Python", 4, 0, 0, 4 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/metadata.py", "Python", -3, -6, -3, -12 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/option.py", "Python", 23, 0, 11, 34 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/parents.py", "Python", 44, 16, 19, 79 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/song.py", "Python", 108, 2, 5, 115 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/source.py", "Python", 2, -21, -1, -20 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/target.py", "Python", 7, 0, 2, 9 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/__init__.py", "Python", -1, 0, 0, -1 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/abstract.py", "Python", 107, -36, 26, 97 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/__init__.py", "Python", 2, 0, 2, 4 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/page_attributes.py", "Python", 14, 0, 6, 20 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/search.py", "Python", 98, 8, 42, 148 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/encyclopaedia_metallum.py", "Python", 100, 20, 20, 140 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/musify.py", "Python", 500, 248, 135, 883 -"/home/lars/Projects/music-downloader/src/music_kraken/target/__init__.py", "Python", -4, 0, -2, -6 -"/home/lars/Projects/music-downloader/src/music_kraken/target/set_target.py", "Python", -37, -7, -18, -62 -"/home/lars/Projects/music-downloader/src/musify_search.py", "Python", 26, 0, 11, 37 -"/home/lars/Projects/music-downloader/src/python.py", "Python", 12, 43, 6, 61 -"/home/lars/Projects/music-downloader/src/test.py", "Python", 1, 0, 0, 1 -"/home/lars/Projects/music-downloader/src/tests/example_data_objects.py", "Python", -36, -5, -6, -47 -"/home/lars/Projects/music-downloader/src/tests/test_building_objects.py", "Python", 81, 1, 13, 95 -"/home/lars/Projects/music-downloader/src/tests/test_objects.py", "Python", 173, 15, 51, 239 -"Total", "-", 523, 22, 130, 675 \ No newline at end of file diff --git a/.VSCodeCounter/2023-03-28_16-46-52/diff.md b/.VSCodeCounter/2023-03-28_16-46-52/diff.md deleted file mode 100644 index 0753e94..0000000 --- a/.VSCodeCounter/2023-03-28_16-46-52/diff.md +++ /dev/null @@ -1,31 +0,0 @@ -# Diff Summary - -Date : 2023-03-28 16:46:52 - -Directory /home/lars/Projects/music-downloader/src - -Total : 40 files, 523 codes, 22 comments, 130 blanks, all 675 lines - -[Summary](results.md) / [Details](details.md) / Diff Summary / [Diff Details](diff-details.md) - -## Languages -| language | files | code | comment | blank | total | -| :--- | ---: | ---: | ---: | ---: | ---: | -| Python | 40 | 523 | 22 | 130 | 675 | - -## Directories -| path | files | code | comment | blank | total | -| :--- | ---: | ---: | ---: | ---: | ---: | -| . | 40 | 523 | 22 | 130 | 675 | -| . (Files) | 6 | 8 | 36 | 8 | 52 | -| music_kraken | 31 | 297 | -25 | 64 | 336 | -| music_kraken (Files) | 1 | -33 | -1 | -11 | -45 | -| music_kraken/database | 8 | -688 | -272 | -195 | -1,155 | -| music_kraken/objects | 13 | 239 | 15 | 59 | 313 | -| music_kraken/pages | 7 | 820 | 240 | 231 | 1,291 | -| music_kraken/pages (Files) | 4 | 706 | 232 | 181 | 1,119 | -| music_kraken/pages/download_center | 3 | 114 | 8 | 50 | 172 | -| music_kraken/target | 2 | -41 | -7 | -20 | -68 | -| tests | 3 | 218 | 11 | 58 | 287 | - -[Summary](results.md) / [Details](details.md) / Diff Summary / [Diff Details](diff-details.md) \ No newline at end of file diff --git a/.VSCodeCounter/2023-03-28_16-46-52/diff.txt b/.VSCodeCounter/2023-03-28_16-46-52/diff.txt deleted file mode 100644 index e8216f3..0000000 --- a/.VSCodeCounter/2023-03-28_16-46-52/diff.txt +++ /dev/null @@ -1,74 +0,0 @@ -Date : 2023-03-28 16:46:52 -Directory : /home/lars/Projects/music-downloader/src -Total : 40 files, 523 codes, 22 comments, 130 blanks, all 675 lines - -Languages -+----------+------------+------------+------------+------------+------------+ -| language | files | code | comment | blank | total | -+----------+------------+------------+------------+------------+------------+ -| Python | 40 | 523 | 22 | 130 | 675 | -+----------+------------+------------+------------+------------+------------+ - -Directories -+------------------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ -| path | files | code | comment | blank | total | -+------------------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ -| . | 40 | 523 | 22 | 130 | 675 | -| . (Files) | 6 | 8 | 36 | 8 | 52 | -| music_kraken | 31 | 297 | -25 | 64 | 336 | -| music_kraken (Files) | 1 | -33 | -1 | -11 | -45 | -| music_kraken/database | 8 | -688 | -272 | -195 | -1,155 | -| music_kraken/objects | 13 | 239 | 15 | 59 | 313 | -| music_kraken/pages | 7 | 820 | 240 | 231 | 1,291 | -| music_kraken/pages (Files) | 4 | 706 | 232 | 181 | 1,119 | -| music_kraken/pages/download_center | 3 | 114 | 8 | 50 | 172 | -| music_kraken/target | 2 | -41 | -7 | -20 | -68 | -| tests | 3 | 218 | 11 | 58 | 287 | -+------------------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ - -Files -+------------------------------------------------------------------------------------------------+----------+------------+------------+------------+------------+ -| filename | language | code | comment | blank | total | -+------------------------------------------------------------------------------------------------+----------+------------+------------+------------+------------+ -| /home/lars/Projects/music-downloader/src/create_custom_objects.py | Python | -22 | -3 | -12 | -37 | -| /home/lars/Projects/music-downloader/src/donwload.py | Python | 9 | 0 | 6 | 15 | -| /home/lars/Projects/music-downloader/src/metal_archives.py | Python | -18 | -4 | -3 | -25 | -| /home/lars/Projects/music-downloader/src/music_kraken/__init__.py | Python | -33 | -1 | -11 | -45 | -| /home/lars/Projects/music-downloader/src/music_kraken/database/__init__.py | Python | -18 | 0 | -4 | -22 | -| /home/lars/Projects/music-downloader/src/music_kraken/database/data_models.py | Python | -1 | 1 | 0 | 0 | -| /home/lars/Projects/music-downloader/src/music_kraken/database/database.py | Python | 20 | -1 | 12 | 31 | -| /home/lars/Projects/music-downloader/src/music_kraken/database/object_cache.py | Python | -35 | -56 | -16 | -107 | -| /home/lars/Projects/music-downloader/src/music_kraken/database/old_database.py | Python | -432 | -154 | -115 | -701 | -| /home/lars/Projects/music-downloader/src/music_kraken/database/read.py | Python | 0 | 0 | -1 | -1 | -| /home/lars/Projects/music-downloader/src/music_kraken/database/temp_database.py | Python | -12 | 0 | -8 | -20 | -| /home/lars/Projects/music-downloader/src/music_kraken/database/write.py | Python | -210 | -62 | -63 | -335 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/__init__.py | Python | 4 | 0 | 1 | 5 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/album.py | Python | 1 | 0 | 0 | 1 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/artist.py | Python | -18 | 0 | -5 | -23 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/cache.py | Python | 37 | 56 | 18 | 111 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/collection.py | Python | 33 | 15 | 13 | 61 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/formatted_text.py | Python | -3 | -47 | -1 | -51 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/lyrics.py | Python | 4 | 0 | 0 | 4 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/metadata.py | Python | -3 | -6 | -3 | -12 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/option.py | Python | 23 | 0 | 11 | 34 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/parents.py | Python | 44 | 16 | 19 | 79 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/song.py | Python | 108 | 2 | 5 | 115 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/source.py | Python | 2 | -21 | -1 | -20 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/target.py | Python | 7 | 0 | 2 | 9 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/__init__.py | Python | -1 | 0 | 0 | -1 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/abstract.py | Python | 107 | -36 | 26 | 97 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/__init__.py | Python | 2 | 0 | 2 | 4 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/page_attributes.py | Python | 14 | 0 | 6 | 20 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/search.py | Python | 98 | 8 | 42 | 148 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/encyclopaedia_metallum.py | Python | 100 | 20 | 20 | 140 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/musify.py | Python | 500 | 248 | 135 | 883 | -| /home/lars/Projects/music-downloader/src/music_kraken/target/__init__.py | Python | -4 | 0 | -2 | -6 | -| /home/lars/Projects/music-downloader/src/music_kraken/target/set_target.py | Python | -37 | -7 | -18 | -62 | -| /home/lars/Projects/music-downloader/src/musify_search.py | Python | 26 | 0 | 11 | 37 | -| /home/lars/Projects/music-downloader/src/python.py | Python | 12 | 43 | 6 | 61 | -| /home/lars/Projects/music-downloader/src/test.py | Python | 1 | 0 | 0 | 1 | -| /home/lars/Projects/music-downloader/src/tests/example_data_objects.py | Python | -36 | -5 | -6 | -47 | -| /home/lars/Projects/music-downloader/src/tests/test_building_objects.py | Python | 81 | 1 | 13 | 95 | -| /home/lars/Projects/music-downloader/src/tests/test_objects.py | Python | 173 | 15 | 51 | 239 | -| Total | | 523 | 22 | 130 | 675 | -+------------------------------------------------------------------------------------------------+----------+------------+------------+------------+------------+ \ No newline at end of file diff --git a/.VSCodeCounter/2023-03-28_16-46-52/results.csv b/.VSCodeCounter/2023-03-28_16-46-52/results.csv deleted file mode 100644 index b6f576d..0000000 --- a/.VSCodeCounter/2023-03-28_16-46-52/results.csv +++ /dev/null @@ -1,68 +0,0 @@ -"filename", "language", "Python", "SQLite", "Database", "comment", "blank", "total" -"/home/lars/Projects/music-downloader/src/__init__.py", "Python", 0, 0, 0, 0, 1, 1 -"/home/lars/Projects/music-downloader/src/create_custom_objects.py", "Python", 58, 0, 0, 0, 6, 64 -"/home/lars/Projects/music-downloader/src/donwload.py", "Python", 9, 0, 0, 0, 6, 15 -"/home/lars/Projects/music-downloader/src/metal_archives.py", "Python", 30, 0, 0, 0, 12, 42 -"/home/lars/Projects/music-downloader/src/music_kraken/__init__.py", "Python", 24, 0, 0, 7, 14, 45 -"/home/lars/Projects/music-downloader/src/music_kraken/__main__.py", "Python", 3, 0, 0, 2, 3, 8 -"/home/lars/Projects/music-downloader/src/music_kraken/database/__init__.py", "Python", 0, 0, 0, 0, 1, 1 -"/home/lars/Projects/music-downloader/src/music_kraken/database/data_models.py", "Python", 122, 0, 0, 24, 52, 198 -"/home/lars/Projects/music-downloader/src/music_kraken/database/database.py", "Python", 104, 0, 0, 47, 38, 189 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/__init__.py", "Python", 0, 0, 0, 0, 3, 3 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/fetch_audio.py", "Python", 75, 0, 0, 12, 20, 107 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/fetch_source.py", "Python", 54, 0, 0, 1, 16, 71 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/__init__.py", "Python", 6, 0, 0, 0, 2, 8 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/metadata_fetch.py", "Python", 257, 0, 0, 24, 65, 346 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/metadata_search.py", "Python", 253, 0, 0, 40, 72, 365 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/sources/__init__.py", "Python", 3, 0, 0, 0, 2, 5 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/sources/musicbrainz.py", "Python", 42, 0, 0, 6, 12, 60 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/__init__.py", "Python", 0, 0, 0, 0, 1, 1 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/genius.py", "Python", 115, 0, 0, 16, 42, 173 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/local_files.py", "Python", 40, 0, 0, 0, 18, 58 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/musify.py", "Python", 136, 0, 0, 9, 37, 182 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/source.py", "Python", 11, 0, 0, 5, 8, 24 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/youtube.py", "Python", 71, 0, 0, 4, 24, 99 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/__init__.py", "Python", 28, 0, 0, 0, 8, 36 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/album.py", "Python", 16, 0, 0, 6, 5, 27 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/cache.py", "Python", 37, 0, 0, 56, 18, 111 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/collection.py", "Python", 85, 0, 0, 30, 37, 152 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/formatted_text.py", "Python", 50, 0, 0, 10, 19, 79 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/lyrics.py", "Python", 25, 0, 0, 0, 7, 32 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/metadata.py", "Python", 259, 0, 0, 62, 60, 381 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/option.py", "Python", 23, 0, 0, 0, 11, 34 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/parents.py", "Python", 66, 0, 0, 31, 31, 128 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/song.py", "Python", 445, 0, 0, 83, 95, 623 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/source.py", "Python", 118, 0, 0, 17, 40, 175 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/target.py", "Python", 29, 0, 0, 7, 9, 45 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/__init__.py", "Python", 6, 0, 0, 0, 5, 11 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/abstract.py", "Python", 180, 0, 0, 32, 53, 265 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/__init__.py", "Python", 2, 0, 0, 0, 2, 4 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/page_attributes.py", "Python", 14, 0, 0, 0, 6, 20 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/search.py", "Python", 98, 0, 0, 8, 42, 148 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/encyclopaedia_metallum.py", "Python", 441, 0, 0, 86, 111, 638 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/musify.py", "Python", 500, 0, 0, 248, 135, 883 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/youtube.py", "Python", 25, 0, 0, 16, 6, 47 -"/home/lars/Projects/music-downloader/src/music_kraken/static_files/new_db.sql", "SQLite", 0, 72, 0, 0, 10, 82 -"/home/lars/Projects/music-downloader/src/music_kraken/static_files/temp_database_structure.sql", "SQLite", 0, 135, 0, 0, 10, 145 -"/home/lars/Projects/music-downloader/src/music_kraken/tagging/__init__.py", "Python", 8, 0, 0, 0, 2, 10 -"/home/lars/Projects/music-downloader/src/music_kraken/tagging/id3.py", "Python", 51, 0, 0, 4, 20, 75 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/__init__.py", "Python", 1, 0, 0, 1, 1, 3 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/functions.py", "Python", 3, 0, 0, 0, 1, 4 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/object_handeling.py", "Python", 19, 0, 0, 0, 6, 25 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/phonetic_compares.py", "Python", 39, 0, 0, 2, 17, 58 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/shared.py", "Python", 62, 0, 0, 3, 10, 75 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/string_processing.py", "Python", 2, 0, 0, 5, 2, 9 -"/home/lars/Projects/music-downloader/src/music_kraken_cli.py", "Python", 3, 0, 0, 0, 3, 6 -"/home/lars/Projects/music-downloader/src/music_kraken_gtk.py", "Python", 3, 0, 0, 0, 2, 5 -"/home/lars/Projects/music-downloader/src/musify_search.py", "Python", 26, 0, 0, 0, 11, 37 -"/home/lars/Projects/music-downloader/src/python.py", "Python", 12, 0, 0, 43, 6, 61 -"/home/lars/Projects/music-downloader/src/test.db", "Database", 0, 0, 91, 0, 1, 92 -"/home/lars/Projects/music-downloader/src/test.py", "Python", 1, 0, 0, 0, 0, 1 -"/home/lars/Projects/music-downloader/src/tests/__init__.py", "Python", 0, 0, 0, 0, 1, 1 -"/home/lars/Projects/music-downloader/src/tests/conftest.py", "Python", 3, 0, 0, 1, 2, 6 -"/home/lars/Projects/music-downloader/src/tests/test_building_objects.py", "Python", 81, 0, 0, 1, 13, 95 -"/home/lars/Projects/music-downloader/src/tests/test_objects.py", "Python", 173, 0, 0, 15, 51, 239 -"/home/lars/Projects/music-downloader/src/try-programming-interface.py", "Python", 14, 0, 0, 98, 22, 134 -"/home/lars/Projects/music-downloader/src/try.py", "Python", 1, 0, 0, 0, 3, 4 -"/home/lars/Projects/music-downloader/src/try_python.py", "Python", 13, 0, 0, 20, 9, 42 -"Total", "-", 4375, 207, 91, 1082, 1358, 7113 \ No newline at end of file diff --git a/.VSCodeCounter/2023-03-28_16-46-52/results.json b/.VSCodeCounter/2023-03-28_16-46-52/results.json deleted file mode 100644 index ffb1ab7..0000000 --- a/.VSCodeCounter/2023-03-28_16-46-52/results.json +++ /dev/null @@ -1 +0,0 @@ -{"file:///home/lars/Projects/music-downloader/src/try-programming-interface.py":{"language":"Python","code":14,"comment":98,"blank":22},"file:///home/lars/Projects/music-downloader/src/try.py":{"language":"Python","code":1,"comment":0,"blank":3},"file:///home/lars/Projects/music-downloader/src/test.py":{"language":"Python","code":1,"comment":0,"blank":0},"file:///home/lars/Projects/music-downloader/src/music_kraken_gtk.py":{"language":"Python","code":3,"comment":0,"blank":2},"file:///home/lars/Projects/music-downloader/src/donwload.py":{"language":"Python","code":9,"comment":0,"blank":6},"file:///home/lars/Projects/music-downloader/src/music_kraken_cli.py":{"language":"Python","code":3,"comment":0,"blank":3},"file:///home/lars/Projects/music-downloader/src/musify_search.py":{"language":"Python","code":26,"comment":0,"blank":11},"file:///home/lars/Projects/music-downloader/src/python.py":{"language":"Python","code":12,"comment":43,"blank":6},"file:///home/lars/Projects/music-downloader/src/metal_archives.py":{"language":"Python","code":30,"comment":0,"blank":12},"file:///home/lars/Projects/music-downloader/src/try_python.py":{"language":"Python","code":13,"comment":20,"blank":9},"file:///home/lars/Projects/music-downloader/src/__init__.py":{"language":"Python","code":0,"comment":0,"blank":1},"file:///home/lars/Projects/music-downloader/src/music_kraken/pages/__init__.py":{"language":"Python","code":6,"comment":0,"blank":5},"file:///home/lars/Projects/music-downloader/src/music_kraken/__init__.py":{"language":"Python","code":24,"comment":7,"blank":14},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/phonetic_compares.py":{"language":"Python","code":39,"comment":2,"blank":17},"file:///home/lars/Projects/music-downloader/src/music_kraken/database/database.py":{"language":"Python","code":104,"comment":47,"blank":38},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/object_handeling.py":{"language":"Python","code":19,"comment":0,"blank":6},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/shared.py":{"language":"Python","code":62,"comment":3,"blank":10},"file:///home/lars/Projects/music-downloader/src/music_kraken/database/__init__.py":{"language":"Python","code":0,"comment":0,"blank":1},"file:///home/lars/Projects/music-downloader/src/music_kraken/database/data_models.py":{"language":"Python","code":122,"comment":24,"blank":52},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/__init__.py":{"language":"Python","code":1,"comment":1,"blank":1},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/string_processing.py":{"language":"Python","code":2,"comment":5,"blank":2},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/functions.py":{"language":"Python","code":3,"comment":0,"blank":1},"file:///home/lars/Projects/music-downloader/src/music_kraken/pages/abstract.py":{"language":"Python","code":180,"comment":32,"blank":53},"file:///home/lars/Projects/music-downloader/src/music_kraken/pages/youtube.py":{"language":"Python","code":25,"comment":16,"blank":6},"file:///home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/page_attributes.py":{"language":"Python","code":14,"comment":0,"blank":6},"file:///home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/search.py":{"language":"Python","code":98,"comment":8,"blank":42},"file:///home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/__init__.py":{"language":"Python","code":2,"comment":0,"blank":2},"file:///home/lars/Projects/music-downloader/src/music_kraken/pages/encyclopaedia_metallum.py":{"language":"Python","code":441,"comment":86,"blank":111},"file:///home/lars/Projects/music-downloader/src/music_kraken/pages/musify.py":{"language":"Python","code":500,"comment":248,"blank":135},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/formatted_text.py":{"language":"Python","code":50,"comment":10,"blank":19},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/lyrics.py":{"language":"Python","code":25,"comment":0,"blank":7},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/option.py":{"language":"Python","code":23,"comment":0,"blank":11},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/collection.py":{"language":"Python","code":85,"comment":30,"blank":37},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/parents.py":{"language":"Python","code":66,"comment":31,"blank":31},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/metadata.py":{"language":"Python","code":259,"comment":62,"blank":60},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/source.py":{"language":"Python","code":118,"comment":17,"blank":40},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/target.py":{"language":"Python","code":29,"comment":7,"blank":9},"file:///home/lars/Projects/music-downloader/src/music_kraken/tagging/__init__.py":{"language":"Python","code":8,"comment":0,"blank":2},"file:///home/lars/Projects/music-downloader/src/music_kraken/static_files/temp_database_structure.sql":{"language":"SQLite","code":135,"comment":0,"blank":10},"file:///home/lars/Projects/music-downloader/src/music_kraken/static_files/new_db.sql":{"language":"SQLite","code":72,"comment":0,"blank":10},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/__init__.py":{"language":"Python","code":28,"comment":0,"blank":8},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/cache.py":{"language":"Python","code":37,"comment":56,"blank":18},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/album.py":{"language":"Python","code":16,"comment":6,"blank":5},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/song.py":{"language":"Python","code":445,"comment":83,"blank":95},"file:///home/lars/Projects/music-downloader/src/music_kraken/tagging/id3.py":{"language":"Python","code":51,"comment":4,"blank":20},"file:///home/lars/Projects/music-downloader/src/music_kraken/__main__.py":{"language":"Python","code":3,"comment":2,"blank":3},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/fetch_source.py":{"language":"Python","code":54,"comment":1,"blank":16},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/__init__.py":{"language":"Python","code":0,"comment":0,"blank":3},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/fetch_audio.py":{"language":"Python","code":75,"comment":12,"blank":20},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/__init__.py":{"language":"Python","code":0,"comment":0,"blank":1},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/source.py":{"language":"Python","code":11,"comment":5,"blank":8},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/genius.py":{"language":"Python","code":115,"comment":16,"blank":42},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/youtube.py":{"language":"Python","code":71,"comment":4,"blank":24},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/musify.py":{"language":"Python","code":136,"comment":9,"blank":37},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/local_files.py":{"language":"Python","code":40,"comment":0,"blank":18},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/__init__.py":{"language":"Python","code":6,"comment":0,"blank":2},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/metadata_search.py":{"language":"Python","code":253,"comment":40,"blank":72},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/metadata_fetch.py":{"language":"Python","code":257,"comment":24,"blank":65},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/sources/__init__.py":{"language":"Python","code":3,"comment":0,"blank":2},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/sources/musicbrainz.py":{"language":"Python","code":42,"comment":6,"blank":12},"file:///home/lars/Projects/music-downloader/src/create_custom_objects.py":{"language":"Python","code":58,"comment":0,"blank":6},"file:///home/lars/Projects/music-downloader/src/tests/conftest.py":{"language":"Python","code":3,"comment":1,"blank":2},"file:///home/lars/Projects/music-downloader/src/tests/test_objects.py":{"language":"Python","code":173,"comment":15,"blank":51},"file:///home/lars/Projects/music-downloader/src/tests/test_building_objects.py":{"language":"Python","code":81,"comment":1,"blank":13},"file:///home/lars/Projects/music-downloader/src/tests/__init__.py":{"language":"Python","code":0,"comment":0,"blank":1},"file:///home/lars/Projects/music-downloader/src/test.db":{"language":"Database","code":91,"comment":0,"blank":1}} \ No newline at end of file diff --git a/.VSCodeCounter/2023-03-28_16-46-52/results.md b/.VSCodeCounter/2023-03-28_16-46-52/results.md deleted file mode 100644 index cb2de19..0000000 --- a/.VSCodeCounter/2023-03-28_16-46-52/results.md +++ /dev/null @@ -1,41 +0,0 @@ -# Summary - -Date : 2023-03-28 16:46:52 - -Directory /home/lars/Projects/music-downloader/src - -Total : 66 files, 4673 codes, 1082 comments, 1358 blanks, all 7113 lines - -Summary / [Details](details.md) / [Diff Summary](diff.md) / [Diff Details](diff-details.md) - -## Languages -| language | files | code | comment | blank | total | -| :--- | ---: | ---: | ---: | ---: | ---: | -| Python | 63 | 4,375 | 1,082 | 1,337 | 6,794 | -| SQLite | 2 | 207 | 0 | 20 | 227 | -| Database | 1 | 91 | 0 | 1 | 92 | - -## Directories -| path | files | code | comment | blank | total | -| :--- | ---: | ---: | ---: | ---: | ---: | -| . | 66 | 4,673 | 1,082 | 1,358 | 7,113 | -| . (Files) | 13 | 261 | 161 | 82 | 504 | -| music_kraken | 49 | 4,155 | 904 | 1,209 | 6,268 | -| music_kraken (Files) | 2 | 27 | 9 | 17 | 53 | -| music_kraken/database | 3 | 226 | 71 | 91 | 388 | -| music_kraken/not_used_anymore | 14 | 1,063 | 117 | 322 | 1,502 | -| music_kraken/not_used_anymore (Files) | 3 | 129 | 13 | 39 | 181 | -| music_kraken/not_used_anymore/metadata | 5 | 561 | 70 | 153 | 784 | -| music_kraken/not_used_anymore/metadata (Files) | 3 | 516 | 64 | 139 | 719 | -| music_kraken/not_used_anymore/metadata/sources | 2 | 45 | 6 | 14 | 65 | -| music_kraken/not_used_anymore/sources | 6 | 373 | 34 | 130 | 537 | -| music_kraken/objects | 12 | 1,181 | 302 | 340 | 1,823 | -| music_kraken/pages | 8 | 1,266 | 390 | 360 | 2,016 | -| music_kraken/pages (Files) | 5 | 1,152 | 382 | 310 | 1,844 | -| music_kraken/pages/download_center | 3 | 114 | 8 | 50 | 172 | -| music_kraken/static_files | 2 | 207 | 0 | 20 | 227 | -| music_kraken/tagging | 2 | 59 | 4 | 22 | 85 | -| music_kraken/utils | 6 | 126 | 11 | 37 | 174 | -| tests | 4 | 257 | 17 | 67 | 341 | - -Summary / [Details](details.md) / [Diff Summary](diff.md) / [Diff Details](diff-details.md) \ No newline at end of file diff --git a/.VSCodeCounter/2023-03-28_16-46-52/results.txt b/.VSCodeCounter/2023-03-28_16-46-52/results.txt deleted file mode 100644 index b60917b..0000000 --- a/.VSCodeCounter/2023-03-28_16-46-52/results.txt +++ /dev/null @@ -1,110 +0,0 @@ -Date : 2023-03-28 16:46:52 -Directory : /home/lars/Projects/music-downloader/src -Total : 66 files, 4673 codes, 1082 comments, 1358 blanks, all 7113 lines - -Languages -+----------+------------+------------+------------+------------+------------+ -| language | files | code | comment | blank | total | -+----------+------------+------------+------------+------------+------------+ -| Python | 63 | 4,375 | 1,082 | 1,337 | 6,794 | -| SQLite | 2 | 207 | 0 | 20 | 227 | -| Database | 1 | 91 | 0 | 1 | 92 | -+----------+------------+------------+------------+------------+------------+ - -Directories -+--------------------------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ -| path | files | code | comment | blank | total | -+--------------------------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ -| . | 66 | 4,673 | 1,082 | 1,358 | 7,113 | -| . (Files) | 13 | 261 | 161 | 82 | 504 | -| music_kraken | 49 | 4,155 | 904 | 1,209 | 6,268 | -| music_kraken (Files) | 2 | 27 | 9 | 17 | 53 | -| music_kraken/database | 3 | 226 | 71 | 91 | 388 | -| music_kraken/not_used_anymore | 14 | 1,063 | 117 | 322 | 1,502 | -| music_kraken/not_used_anymore (Files) | 3 | 129 | 13 | 39 | 181 | -| music_kraken/not_used_anymore/metadata | 5 | 561 | 70 | 153 | 784 | -| music_kraken/not_used_anymore/metadata (Files) | 3 | 516 | 64 | 139 | 719 | -| music_kraken/not_used_anymore/metadata/sources | 2 | 45 | 6 | 14 | 65 | -| music_kraken/not_used_anymore/sources | 6 | 373 | 34 | 130 | 537 | -| music_kraken/objects | 12 | 1,181 | 302 | 340 | 1,823 | -| music_kraken/pages | 8 | 1,266 | 390 | 360 | 2,016 | -| music_kraken/pages (Files) | 5 | 1,152 | 382 | 310 | 1,844 | -| music_kraken/pages/download_center | 3 | 114 | 8 | 50 | 172 | -| music_kraken/static_files | 2 | 207 | 0 | 20 | 227 | -| music_kraken/tagging | 2 | 59 | 4 | 22 | 85 | -| music_kraken/utils | 6 | 126 | 11 | 37 | 174 | -| tests | 4 | 257 | 17 | 67 | 341 | -+--------------------------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ - -Files -+--------------------------------------------------------------------------------------------------------+----------+------------+------------+------------+------------+ -| filename | language | code | comment | blank | total | -+--------------------------------------------------------------------------------------------------------+----------+------------+------------+------------+------------+ -| /home/lars/Projects/music-downloader/src/__init__.py | Python | 0 | 0 | 1 | 1 | -| /home/lars/Projects/music-downloader/src/create_custom_objects.py | Python | 58 | 0 | 6 | 64 | -| /home/lars/Projects/music-downloader/src/donwload.py | Python | 9 | 0 | 6 | 15 | -| /home/lars/Projects/music-downloader/src/metal_archives.py | Python | 30 | 0 | 12 | 42 | -| /home/lars/Projects/music-downloader/src/music_kraken/__init__.py | Python | 24 | 7 | 14 | 45 | -| /home/lars/Projects/music-downloader/src/music_kraken/__main__.py | Python | 3 | 2 | 3 | 8 | -| /home/lars/Projects/music-downloader/src/music_kraken/database/__init__.py | Python | 0 | 0 | 1 | 1 | -| /home/lars/Projects/music-downloader/src/music_kraken/database/data_models.py | Python | 122 | 24 | 52 | 198 | -| /home/lars/Projects/music-downloader/src/music_kraken/database/database.py | Python | 104 | 47 | 38 | 189 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/__init__.py | Python | 0 | 0 | 3 | 3 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/fetch_audio.py | Python | 75 | 12 | 20 | 107 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/fetch_source.py | Python | 54 | 1 | 16 | 71 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/__init__.py | Python | 6 | 0 | 2 | 8 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/metadata_fetch.py | Python | 257 | 24 | 65 | 346 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/metadata_search.py | Python | 253 | 40 | 72 | 365 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/sources/__init__.py | Python | 3 | 0 | 2 | 5 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/sources/musicbrainz.py | Python | 42 | 6 | 12 | 60 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/__init__.py | Python | 0 | 0 | 1 | 1 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/genius.py | Python | 115 | 16 | 42 | 173 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/local_files.py | Python | 40 | 0 | 18 | 58 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/musify.py | Python | 136 | 9 | 37 | 182 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/source.py | Python | 11 | 5 | 8 | 24 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/youtube.py | Python | 71 | 4 | 24 | 99 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/__init__.py | Python | 28 | 0 | 8 | 36 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/album.py | Python | 16 | 6 | 5 | 27 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/cache.py | Python | 37 | 56 | 18 | 111 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/collection.py | Python | 85 | 30 | 37 | 152 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/formatted_text.py | Python | 50 | 10 | 19 | 79 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/lyrics.py | Python | 25 | 0 | 7 | 32 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/metadata.py | Python | 259 | 62 | 60 | 381 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/option.py | Python | 23 | 0 | 11 | 34 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/parents.py | Python | 66 | 31 | 31 | 128 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/song.py | Python | 445 | 83 | 95 | 623 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/source.py | Python | 118 | 17 | 40 | 175 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/target.py | Python | 29 | 7 | 9 | 45 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/__init__.py | Python | 6 | 0 | 5 | 11 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/abstract.py | Python | 180 | 32 | 53 | 265 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/__init__.py | Python | 2 | 0 | 2 | 4 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/page_attributes.py | Python | 14 | 0 | 6 | 20 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/search.py | Python | 98 | 8 | 42 | 148 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/encyclopaedia_metallum.py | Python | 441 | 86 | 111 | 638 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/musify.py | Python | 500 | 248 | 135 | 883 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/youtube.py | Python | 25 | 16 | 6 | 47 | -| /home/lars/Projects/music-downloader/src/music_kraken/static_files/new_db.sql | SQLite | 72 | 0 | 10 | 82 | -| /home/lars/Projects/music-downloader/src/music_kraken/static_files/temp_database_structure.sql | SQLite | 135 | 0 | 10 | 145 | -| /home/lars/Projects/music-downloader/src/music_kraken/tagging/__init__.py | Python | 8 | 0 | 2 | 10 | -| /home/lars/Projects/music-downloader/src/music_kraken/tagging/id3.py | Python | 51 | 4 | 20 | 75 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/__init__.py | Python | 1 | 1 | 1 | 3 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/functions.py | Python | 3 | 0 | 1 | 4 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/object_handeling.py | Python | 19 | 0 | 6 | 25 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/phonetic_compares.py | Python | 39 | 2 | 17 | 58 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/shared.py | Python | 62 | 3 | 10 | 75 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/string_processing.py | Python | 2 | 5 | 2 | 9 | -| /home/lars/Projects/music-downloader/src/music_kraken_cli.py | Python | 3 | 0 | 3 | 6 | -| /home/lars/Projects/music-downloader/src/music_kraken_gtk.py | Python | 3 | 0 | 2 | 5 | -| /home/lars/Projects/music-downloader/src/musify_search.py | Python | 26 | 0 | 11 | 37 | -| /home/lars/Projects/music-downloader/src/python.py | Python | 12 | 43 | 6 | 61 | -| /home/lars/Projects/music-downloader/src/test.db | Database | 91 | 0 | 1 | 92 | -| /home/lars/Projects/music-downloader/src/test.py | Python | 1 | 0 | 0 | 1 | -| /home/lars/Projects/music-downloader/src/tests/__init__.py | Python | 0 | 0 | 1 | 1 | -| /home/lars/Projects/music-downloader/src/tests/conftest.py | Python | 3 | 1 | 2 | 6 | -| /home/lars/Projects/music-downloader/src/tests/test_building_objects.py | Python | 81 | 1 | 13 | 95 | -| /home/lars/Projects/music-downloader/src/tests/test_objects.py | Python | 173 | 15 | 51 | 239 | -| /home/lars/Projects/music-downloader/src/try-programming-interface.py | Python | 14 | 98 | 22 | 134 | -| /home/lars/Projects/music-downloader/src/try.py | Python | 1 | 0 | 3 | 4 | -| /home/lars/Projects/music-downloader/src/try_python.py | Python | 13 | 20 | 9 | 42 | -| Total | | 4,673 | 1,082 | 1,358 | 7,113 | -+--------------------------------------------------------------------------------------------------------+----------+------------+------------+------------+------------+ \ No newline at end of file diff --git a/.VSCodeCounter/2023-03-30_12-56-39/details.md b/.VSCodeCounter/2023-03-30_12-56-39/details.md deleted file mode 100644 index bf8804a..0000000 --- a/.VSCodeCounter/2023-03-30_12-56-39/details.md +++ /dev/null @@ -1,77 +0,0 @@ -# Details - -Date : 2023-03-30 12:56:39 - -Directory /home/lars/Projects/music-downloader/src - -Total : 62 files, 4737 codes, 921 comments, 1383 blanks, all 7041 lines - -[Summary](results.md) / Details / [Diff Summary](diff.md) / [Diff Details](diff-details.md) - -## Files -| filename | language | code | comment | blank | total | -| :--- | :--- | ---: | ---: | ---: | ---: | -| [src/__init__.py](/src/__init__.py) | Python | 0 | 0 | 1 | 1 | -| [src/actual_donwload.py](/src/actual_donwload.py) | Python | 21 | 0 | 12 | 33 | -| [src/create_custom_objects.py](/src/create_custom_objects.py) | Python | 58 | 0 | 6 | 64 | -| [src/metal_archives.py](/src/metal_archives.py) | Python | 30 | 0 | 12 | 42 | -| [src/music_kraken/__init__.py](/src/music_kraken/__init__.py) | Python | 48 | 7 | 19 | 74 | -| [src/music_kraken/__main__.py](/src/music_kraken/__main__.py) | Python | 3 | 0 | 3 | 6 | -| [src/music_kraken/database/__init__.py](/src/music_kraken/database/__init__.py) | Python | 0 | 0 | 1 | 1 | -| [src/music_kraken/database/data_models.py](/src/music_kraken/database/data_models.py) | Python | 122 | 24 | 52 | 198 | -| [src/music_kraken/database/database.py](/src/music_kraken/database/database.py) | Python | 104 | 47 | 38 | 189 | -| [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 | -| [src/music_kraken/not_used_anymore/fetch_source.py](/src/music_kraken/not_used_anymore/fetch_source.py) | Python | 54 | 1 | 16 | 71 | -| [src/music_kraken/not_used_anymore/metadata/__init__.py](/src/music_kraken/not_used_anymore/metadata/__init__.py) | Python | 6 | 0 | 2 | 8 | -| [src/music_kraken/not_used_anymore/metadata/metadata_fetch.py](/src/music_kraken/not_used_anymore/metadata/metadata_fetch.py) | Python | 257 | 24 | 65 | 346 | -| [src/music_kraken/not_used_anymore/metadata/metadata_search.py](/src/music_kraken/not_used_anymore/metadata/metadata_search.py) | Python | 253 | 40 | 72 | 365 | -| [src/music_kraken/not_used_anymore/metadata/sources/__init__.py](/src/music_kraken/not_used_anymore/metadata/sources/__init__.py) | Python | 3 | 0 | 2 | 5 | -| [src/music_kraken/not_used_anymore/metadata/sources/musicbrainz.py](/src/music_kraken/not_used_anymore/metadata/sources/musicbrainz.py) | Python | 42 | 6 | 12 | 60 | -| [src/music_kraken/not_used_anymore/sources/__init__.py](/src/music_kraken/not_used_anymore/sources/__init__.py) | Python | 0 | 0 | 1 | 1 | -| [src/music_kraken/not_used_anymore/sources/genius.py](/src/music_kraken/not_used_anymore/sources/genius.py) | Python | 115 | 16 | 42 | 173 | -| [src/music_kraken/not_used_anymore/sources/local_files.py](/src/music_kraken/not_used_anymore/sources/local_files.py) | Python | 40 | 0 | 18 | 58 | -| [src/music_kraken/not_used_anymore/sources/musify.py](/src/music_kraken/not_used_anymore/sources/musify.py) | Python | 136 | 9 | 37 | 182 | -| [src/music_kraken/not_used_anymore/sources/source.py](/src/music_kraken/not_used_anymore/sources/source.py) | Python | 11 | 5 | 8 | 24 | -| [src/music_kraken/not_used_anymore/sources/youtube.py](/src/music_kraken/not_used_anymore/sources/youtube.py) | Python | 71 | 4 | 24 | 99 | -| [src/music_kraken/objects/__init__.py](/src/music_kraken/objects/__init__.py) | Python | 28 | 0 | 8 | 36 | -| [src/music_kraken/objects/album.py](/src/music_kraken/objects/album.py) | Python | 16 | 6 | 5 | 27 | -| [src/music_kraken/objects/cache.py](/src/music_kraken/objects/cache.py) | Python | 37 | 56 | 18 | 111 | -| [src/music_kraken/objects/collection.py](/src/music_kraken/objects/collection.py) | Python | 84 | 30 | 37 | 151 | -| [src/music_kraken/objects/formatted_text.py](/src/music_kraken/objects/formatted_text.py) | Python | 50 | 10 | 19 | 79 | -| [src/music_kraken/objects/lyrics.py](/src/music_kraken/objects/lyrics.py) | Python | 25 | 0 | 7 | 32 | -| [src/music_kraken/objects/metadata.py](/src/music_kraken/objects/metadata.py) | Python | 259 | 62 | 60 | 381 | -| [src/music_kraken/objects/option.py](/src/music_kraken/objects/option.py) | Python | 23 | 0 | 11 | 34 | -| [src/music_kraken/objects/parents.py](/src/music_kraken/objects/parents.py) | Python | 66 | 31 | 31 | 128 | -| [src/music_kraken/objects/song.py](/src/music_kraken/objects/song.py) | Python | 445 | 83 | 95 | 623 | -| [src/music_kraken/objects/source.py](/src/music_kraken/objects/source.py) | Python | 120 | 17 | 41 | 178 | -| [src/music_kraken/objects/target.py](/src/music_kraken/objects/target.py) | Python | 29 | 7 | 9 | 45 | -| [src/music_kraken/pages/__init__.py](/src/music_kraken/pages/__init__.py) | Python | 6 | 0 | 5 | 11 | -| [src/music_kraken/pages/abstract.py](/src/music_kraken/pages/abstract.py) | Python | 203 | 32 | 61 | 296 | -| [src/music_kraken/pages/download_center/__init__.py](/src/music_kraken/pages/download_center/__init__.py) | Python | 4 | 0 | 2 | 6 | -| [src/music_kraken/pages/download_center/download.py](/src/music_kraken/pages/download_center/download.py) | Python | 33 | 0 | 12 | 45 | -| [src/music_kraken/pages/download_center/page_attributes.py](/src/music_kraken/pages/download_center/page_attributes.py) | Python | 24 | 1 | 9 | 34 | -| [src/music_kraken/pages/download_center/search.py](/src/music_kraken/pages/download_center/search.py) | Python | 136 | 8 | 54 | 198 | -| [src/music_kraken/pages/encyclopaedia_metallum.py](/src/music_kraken/pages/encyclopaedia_metallum.py) | Python | 455 | 86 | 116 | 657 | -| [src/music_kraken/pages/musify.py](/src/music_kraken/pages/musify.py) | Python | 509 | 248 | 137 | 894 | -| [src/music_kraken/pages/youtube.py](/src/music_kraken/pages/youtube.py) | Python | 25 | 16 | 6 | 47 | -| [src/music_kraken/static_files/new_db.sql](/src/music_kraken/static_files/new_db.sql) | SQLite | 72 | 0 | 10 | 82 | -| [src/music_kraken/static_files/temp_database_structure.sql](/src/music_kraken/static_files/temp_database_structure.sql) | SQLite | 135 | 0 | 10 | 145 | -| [src/music_kraken/tagging/__init__.py](/src/music_kraken/tagging/__init__.py) | Python | 8 | 0 | 2 | 10 | -| [src/music_kraken/tagging/id3.py](/src/music_kraken/tagging/id3.py) | Python | 51 | 4 | 20 | 75 | -| [src/music_kraken/utils/__init__.py](/src/music_kraken/utils/__init__.py) | Python | 1 | 1 | 1 | 3 | -| [src/music_kraken/utils/functions.py](/src/music_kraken/utils/functions.py) | Python | 3 | 0 | 1 | 4 | -| [src/music_kraken/utils/object_handeling.py](/src/music_kraken/utils/object_handeling.py) | Python | 19 | 0 | 6 | 25 | -| [src/music_kraken/utils/phonetic_compares.py](/src/music_kraken/utils/phonetic_compares.py) | Python | 39 | 2 | 17 | 58 | -| [src/music_kraken/utils/shared.py](/src/music_kraken/utils/shared.py) | Python | 62 | 3 | 10 | 75 | -| [src/music_kraken/utils/string_processing.py](/src/music_kraken/utils/string_processing.py) | Python | 2 | 5 | 2 | 9 | -| [src/music_kraken_cli.py](/src/music_kraken_cli.py) | Python | 3 | 0 | 3 | 6 | -| [src/music_kraken_gtk.py](/src/music_kraken_gtk.py) | Python | 3 | 0 | 2 | 5 | -| [src/musify_search.py](/src/musify_search.py) | Python | 26 | 0 | 11 | 37 | -| [src/tests/__init__.py](/src/tests/__init__.py) | Python | 0 | 0 | 1 | 1 | -| [src/tests/conftest.py](/src/tests/conftest.py) | Python | 3 | 1 | 2 | 6 | -| [src/tests/test_building_objects.py](/src/tests/test_building_objects.py) | Python | 81 | 1 | 13 | 95 | -| [src/tests/test_download.py](/src/tests/test_download.py) | Python | 30 | 1 | 12 | 43 | -| [src/tests/test_objects.py](/src/tests/test_objects.py) | Python | 173 | 15 | 51 | 239 | - -[Summary](results.md) / Details / [Diff Summary](diff.md) / [Diff Details](diff-details.md) \ No newline at end of file diff --git a/.VSCodeCounter/2023-03-30_12-56-39/diff-details.md b/.VSCodeCounter/2023-03-30_12-56-39/diff-details.md deleted file mode 100644 index 91c3960..0000000 --- a/.VSCodeCounter/2023-03-30_12-56-39/diff-details.md +++ /dev/null @@ -1,35 +0,0 @@ -# Diff Details - -Date : 2023-03-30 12:56:39 - -Directory /home/lars/Projects/music-downloader/src - -Total : 20 files, 64 codes, -161 comments, 25 blanks, all -72 lines - -[Summary](results.md) / [Details](details.md) / [Diff Summary](diff.md) / Diff Details - -## Files -| filename | language | code | comment | blank | total | -| :--- | :--- | ---: | ---: | ---: | ---: | -| [src/actual_donwload.py](/src/actual_donwload.py) | Python | 21 | 0 | 12 | 33 | -| [src/donwload.py](/src/donwload.py) | Python | -9 | 0 | -6 | -15 | -| [src/music_kraken/__init__.py](/src/music_kraken/__init__.py) | Python | 24 | 0 | 5 | 29 | -| [src/music_kraken/__main__.py](/src/music_kraken/__main__.py) | Python | 0 | -2 | 0 | -2 | -| [src/music_kraken/objects/collection.py](/src/music_kraken/objects/collection.py) | Python | -1 | 0 | 0 | -1 | -| [src/music_kraken/objects/source.py](/src/music_kraken/objects/source.py) | Python | 2 | 0 | 1 | 3 | -| [src/music_kraken/pages/abstract.py](/src/music_kraken/pages/abstract.py) | Python | 23 | 0 | 8 | 31 | -| [src/music_kraken/pages/download_center/__init__.py](/src/music_kraken/pages/download_center/__init__.py) | Python | 2 | 0 | 0 | 2 | -| [src/music_kraken/pages/download_center/download.py](/src/music_kraken/pages/download_center/download.py) | Python | 33 | 0 | 12 | 45 | -| [src/music_kraken/pages/download_center/page_attributes.py](/src/music_kraken/pages/download_center/page_attributes.py) | Python | 10 | 1 | 3 | 14 | -| [src/music_kraken/pages/download_center/search.py](/src/music_kraken/pages/download_center/search.py) | Python | 38 | 0 | 12 | 50 | -| [src/music_kraken/pages/encyclopaedia_metallum.py](/src/music_kraken/pages/encyclopaedia_metallum.py) | Python | 14 | 0 | 5 | 19 | -| [src/music_kraken/pages/musify.py](/src/music_kraken/pages/musify.py) | Python | 9 | 0 | 2 | 11 | -| [src/python.py](/src/python.py) | Python | -12 | -43 | -6 | -61 | -| [src/test.db](/src/test.db) | Database | -91 | 0 | -1 | -92 | -| [src/test.py](/src/test.py) | Python | -1 | 0 | 0 | -1 | -| [src/tests/test_download.py](/src/tests/test_download.py) | Python | 30 | 1 | 12 | 43 | -| [src/try-programming-interface.py](/src/try-programming-interface.py) | Python | -14 | -98 | -22 | -134 | -| [src/try.py](/src/try.py) | Python | -1 | 0 | -3 | -4 | -| [src/try_python.py](/src/try_python.py) | Python | -13 | -20 | -9 | -42 | - -[Summary](results.md) / [Details](details.md) / [Diff Summary](diff.md) / Diff Details \ No newline at end of file diff --git a/.VSCodeCounter/2023-03-30_12-56-39/diff.csv b/.VSCodeCounter/2023-03-30_12-56-39/diff.csv deleted file mode 100644 index 725ebf7..0000000 --- a/.VSCodeCounter/2023-03-30_12-56-39/diff.csv +++ /dev/null @@ -1,22 +0,0 @@ -"filename", "language", "Python", "Database", "comment", "blank", "total" -"/home/lars/Projects/music-downloader/src/actual_donwload.py", "Python", 21, 0, 0, 12, 33 -"/home/lars/Projects/music-downloader/src/donwload.py", "Python", -9, 0, 0, -6, -15 -"/home/lars/Projects/music-downloader/src/music_kraken/__init__.py", "Python", 24, 0, 0, 5, 29 -"/home/lars/Projects/music-downloader/src/music_kraken/__main__.py", "Python", 0, 0, -2, 0, -2 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/collection.py", "Python", -1, 0, 0, 0, -1 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/source.py", "Python", 2, 0, 0, 1, 3 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/abstract.py", "Python", 23, 0, 0, 8, 31 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/__init__.py", "Python", 2, 0, 0, 0, 2 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/download.py", "Python", 33, 0, 0, 12, 45 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/page_attributes.py", "Python", 10, 0, 1, 3, 14 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/search.py", "Python", 38, 0, 0, 12, 50 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/encyclopaedia_metallum.py", "Python", 14, 0, 0, 5, 19 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/musify.py", "Python", 9, 0, 0, 2, 11 -"/home/lars/Projects/music-downloader/src/python.py", "Python", -12, 0, -43, -6, -61 -"/home/lars/Projects/music-downloader/src/test.db", "Database", 0, -91, 0, -1, -92 -"/home/lars/Projects/music-downloader/src/test.py", "Python", -1, 0, 0, 0, -1 -"/home/lars/Projects/music-downloader/src/tests/test_download.py", "Python", 30, 0, 1, 12, 43 -"/home/lars/Projects/music-downloader/src/try-programming-interface.py", "Python", -14, 0, -98, -22, -134 -"/home/lars/Projects/music-downloader/src/try.py", "Python", -1, 0, 0, -3, -4 -"/home/lars/Projects/music-downloader/src/try_python.py", "Python", -13, 0, -20, -9, -42 -"Total", "-", 155, -91, -161, 25, -72 \ No newline at end of file diff --git a/.VSCodeCounter/2023-03-30_12-56-39/diff.md b/.VSCodeCounter/2023-03-30_12-56-39/diff.md deleted file mode 100644 index 11a39a5..0000000 --- a/.VSCodeCounter/2023-03-30_12-56-39/diff.md +++ /dev/null @@ -1,30 +0,0 @@ -# Diff Summary - -Date : 2023-03-30 12:56:39 - -Directory /home/lars/Projects/music-downloader/src - -Total : 20 files, 64 codes, -161 comments, 25 blanks, all -72 lines - -[Summary](results.md) / [Details](details.md) / Diff Summary / [Diff Details](diff-details.md) - -## Languages -| language | files | code | comment | blank | total | -| :--- | ---: | ---: | ---: | ---: | ---: | -| Python | 19 | 155 | -161 | 26 | 20 | -| Database | 1 | -91 | 0 | -1 | -92 | - -## Directories -| path | files | code | comment | blank | total | -| :--- | ---: | ---: | ---: | ---: | ---: | -| . | 20 | 64 | -161 | 25 | -72 | -| . (Files) | 8 | -120 | -161 | -35 | -316 | -| music_kraken | 11 | 154 | -1 | 48 | 201 | -| music_kraken (Files) | 2 | 24 | -2 | 5 | 27 | -| music_kraken/objects | 2 | 1 | 0 | 1 | 2 | -| music_kraken/pages | 7 | 129 | 1 | 42 | 172 | -| music_kraken/pages (Files) | 3 | 46 | 0 | 15 | 61 | -| music_kraken/pages/download_center | 4 | 83 | 1 | 27 | 111 | -| tests | 1 | 30 | 1 | 12 | 43 | - -[Summary](results.md) / [Details](details.md) / Diff Summary / [Diff Details](diff-details.md) \ No newline at end of file diff --git a/.VSCodeCounter/2023-03-30_12-56-39/diff.txt b/.VSCodeCounter/2023-03-30_12-56-39/diff.txt deleted file mode 100644 index 0f3dd76..0000000 --- a/.VSCodeCounter/2023-03-30_12-56-39/diff.txt +++ /dev/null @@ -1,53 +0,0 @@ -Date : 2023-03-30 12:56:39 -Directory : /home/lars/Projects/music-downloader/src -Total : 20 files, 64 codes, -161 comments, 25 blanks, all -72 lines - -Languages -+----------+------------+------------+------------+------------+------------+ -| language | files | code | comment | blank | total | -+----------+------------+------------+------------+------------+------------+ -| Python | 19 | 155 | -161 | 26 | 20 | -| Database | 1 | -91 | 0 | -1 | -92 | -+----------+------------+------------+------------+------------+------------+ - -Directories -+------------------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ -| path | files | code | comment | blank | total | -+------------------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ -| . | 20 | 64 | -161 | 25 | -72 | -| . (Files) | 8 | -120 | -161 | -35 | -316 | -| music_kraken | 11 | 154 | -1 | 48 | 201 | -| music_kraken (Files) | 2 | 24 | -2 | 5 | 27 | -| music_kraken/objects | 2 | 1 | 0 | 1 | 2 | -| music_kraken/pages | 7 | 129 | 1 | 42 | 172 | -| music_kraken/pages (Files) | 3 | 46 | 0 | 15 | 61 | -| music_kraken/pages/download_center | 4 | 83 | 1 | 27 | 111 | -| tests | 1 | 30 | 1 | 12 | 43 | -+------------------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ - -Files -+------------------------------------------------------------------------------------------------+----------+------------+------------+------------+------------+ -| filename | language | code | comment | blank | total | -+------------------------------------------------------------------------------------------------+----------+------------+------------+------------+------------+ -| /home/lars/Projects/music-downloader/src/actual_donwload.py | Python | 21 | 0 | 12 | 33 | -| /home/lars/Projects/music-downloader/src/donwload.py | Python | -9 | 0 | -6 | -15 | -| /home/lars/Projects/music-downloader/src/music_kraken/__init__.py | Python | 24 | 0 | 5 | 29 | -| /home/lars/Projects/music-downloader/src/music_kraken/__main__.py | Python | 0 | -2 | 0 | -2 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/collection.py | Python | -1 | 0 | 0 | -1 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/source.py | Python | 2 | 0 | 1 | 3 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/abstract.py | Python | 23 | 0 | 8 | 31 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/__init__.py | Python | 2 | 0 | 0 | 2 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/download.py | Python | 33 | 0 | 12 | 45 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/page_attributes.py | Python | 10 | 1 | 3 | 14 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/search.py | Python | 38 | 0 | 12 | 50 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/encyclopaedia_metallum.py | Python | 14 | 0 | 5 | 19 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/musify.py | Python | 9 | 0 | 2 | 11 | -| /home/lars/Projects/music-downloader/src/python.py | Python | -12 | -43 | -6 | -61 | -| /home/lars/Projects/music-downloader/src/test.db | Database | -91 | 0 | -1 | -92 | -| /home/lars/Projects/music-downloader/src/test.py | Python | -1 | 0 | 0 | -1 | -| /home/lars/Projects/music-downloader/src/tests/test_download.py | Python | 30 | 1 | 12 | 43 | -| /home/lars/Projects/music-downloader/src/try-programming-interface.py | Python | -14 | -98 | -22 | -134 | -| /home/lars/Projects/music-downloader/src/try.py | Python | -1 | 0 | -3 | -4 | -| /home/lars/Projects/music-downloader/src/try_python.py | Python | -13 | -20 | -9 | -42 | -| Total | | 64 | -161 | 25 | -72 | -+------------------------------------------------------------------------------------------------+----------+------------+------------+------------+------------+ \ No newline at end of file diff --git a/.VSCodeCounter/2023-03-30_12-56-39/results.csv b/.VSCodeCounter/2023-03-30_12-56-39/results.csv deleted file mode 100644 index eff133b..0000000 --- a/.VSCodeCounter/2023-03-30_12-56-39/results.csv +++ /dev/null @@ -1,64 +0,0 @@ -"filename", "language", "Python", "SQLite", "comment", "blank", "total" -"/home/lars/Projects/music-downloader/src/__init__.py", "Python", 0, 0, 0, 1, 1 -"/home/lars/Projects/music-downloader/src/actual_donwload.py", "Python", 21, 0, 0, 12, 33 -"/home/lars/Projects/music-downloader/src/create_custom_objects.py", "Python", 58, 0, 0, 6, 64 -"/home/lars/Projects/music-downloader/src/metal_archives.py", "Python", 30, 0, 0, 12, 42 -"/home/lars/Projects/music-downloader/src/music_kraken/__init__.py", "Python", 48, 0, 7, 19, 74 -"/home/lars/Projects/music-downloader/src/music_kraken/__main__.py", "Python", 3, 0, 0, 3, 6 -"/home/lars/Projects/music-downloader/src/music_kraken/database/__init__.py", "Python", 0, 0, 0, 1, 1 -"/home/lars/Projects/music-downloader/src/music_kraken/database/data_models.py", "Python", 122, 0, 24, 52, 198 -"/home/lars/Projects/music-downloader/src/music_kraken/database/database.py", "Python", 104, 0, 47, 38, 189 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/__init__.py", "Python", 0, 0, 0, 3, 3 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/fetch_audio.py", "Python", 75, 0, 12, 20, 107 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/fetch_source.py", "Python", 54, 0, 1, 16, 71 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/__init__.py", "Python", 6, 0, 0, 2, 8 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/metadata_fetch.py", "Python", 257, 0, 24, 65, 346 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/metadata_search.py", "Python", 253, 0, 40, 72, 365 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/sources/__init__.py", "Python", 3, 0, 0, 2, 5 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/sources/musicbrainz.py", "Python", 42, 0, 6, 12, 60 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/__init__.py", "Python", 0, 0, 0, 1, 1 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/genius.py", "Python", 115, 0, 16, 42, 173 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/local_files.py", "Python", 40, 0, 0, 18, 58 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/musify.py", "Python", 136, 0, 9, 37, 182 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/source.py", "Python", 11, 0, 5, 8, 24 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/youtube.py", "Python", 71, 0, 4, 24, 99 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/__init__.py", "Python", 28, 0, 0, 8, 36 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/album.py", "Python", 16, 0, 6, 5, 27 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/cache.py", "Python", 37, 0, 56, 18, 111 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/collection.py", "Python", 84, 0, 30, 37, 151 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/formatted_text.py", "Python", 50, 0, 10, 19, 79 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/lyrics.py", "Python", 25, 0, 0, 7, 32 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/metadata.py", "Python", 259, 0, 62, 60, 381 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/option.py", "Python", 23, 0, 0, 11, 34 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/parents.py", "Python", 66, 0, 31, 31, 128 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/song.py", "Python", 445, 0, 83, 95, 623 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/source.py", "Python", 120, 0, 17, 41, 178 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/target.py", "Python", 29, 0, 7, 9, 45 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/__init__.py", "Python", 6, 0, 0, 5, 11 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/abstract.py", "Python", 203, 0, 32, 61, 296 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/__init__.py", "Python", 4, 0, 0, 2, 6 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/download.py", "Python", 33, 0, 0, 12, 45 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/page_attributes.py", "Python", 24, 0, 1, 9, 34 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/search.py", "Python", 136, 0, 8, 54, 198 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/encyclopaedia_metallum.py", "Python", 455, 0, 86, 116, 657 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/musify.py", "Python", 509, 0, 248, 137, 894 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/youtube.py", "Python", 25, 0, 16, 6, 47 -"/home/lars/Projects/music-downloader/src/music_kraken/static_files/new_db.sql", "SQLite", 0, 72, 0, 10, 82 -"/home/lars/Projects/music-downloader/src/music_kraken/static_files/temp_database_structure.sql", "SQLite", 0, 135, 0, 10, 145 -"/home/lars/Projects/music-downloader/src/music_kraken/tagging/__init__.py", "Python", 8, 0, 0, 2, 10 -"/home/lars/Projects/music-downloader/src/music_kraken/tagging/id3.py", "Python", 51, 0, 4, 20, 75 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/__init__.py", "Python", 1, 0, 1, 1, 3 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/functions.py", "Python", 3, 0, 0, 1, 4 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/object_handeling.py", "Python", 19, 0, 0, 6, 25 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/phonetic_compares.py", "Python", 39, 0, 2, 17, 58 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/shared.py", "Python", 62, 0, 3, 10, 75 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/string_processing.py", "Python", 2, 0, 5, 2, 9 -"/home/lars/Projects/music-downloader/src/music_kraken_cli.py", "Python", 3, 0, 0, 3, 6 -"/home/lars/Projects/music-downloader/src/music_kraken_gtk.py", "Python", 3, 0, 0, 2, 5 -"/home/lars/Projects/music-downloader/src/musify_search.py", "Python", 26, 0, 0, 11, 37 -"/home/lars/Projects/music-downloader/src/tests/__init__.py", "Python", 0, 0, 0, 1, 1 -"/home/lars/Projects/music-downloader/src/tests/conftest.py", "Python", 3, 0, 1, 2, 6 -"/home/lars/Projects/music-downloader/src/tests/test_building_objects.py", "Python", 81, 0, 1, 13, 95 -"/home/lars/Projects/music-downloader/src/tests/test_download.py", "Python", 30, 0, 1, 12, 43 -"/home/lars/Projects/music-downloader/src/tests/test_objects.py", "Python", 173, 0, 15, 51, 239 -"Total", "-", 4530, 207, 921, 1383, 7041 \ No newline at end of file diff --git a/.VSCodeCounter/2023-03-30_12-56-39/results.json b/.VSCodeCounter/2023-03-30_12-56-39/results.json deleted file mode 100644 index 798ad2a..0000000 --- a/.VSCodeCounter/2023-03-30_12-56-39/results.json +++ /dev/null @@ -1 +0,0 @@ -{"file:///home/lars/Projects/music-downloader/src/__init__.py":{"language":"Python","code":0,"comment":0,"blank":1},"file:///home/lars/Projects/music-downloader/src/music_kraken/__init__.py":{"language":"Python","code":48,"comment":7,"blank":19},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/string_processing.py":{"language":"Python","code":2,"comment":5,"blank":2},"file:///home/lars/Projects/music-downloader/src/music_kraken/database/database.py":{"language":"Python","code":104,"comment":47,"blank":38},"file:///home/lars/Projects/music-downloader/src/music_kraken/database/__init__.py":{"language":"Python","code":0,"comment":0,"blank":1},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/__init__.py":{"language":"Python","code":1,"comment":1,"blank":1},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/object_handeling.py":{"language":"Python","code":19,"comment":0,"blank":6},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/phonetic_compares.py":{"language":"Python","code":39,"comment":2,"blank":17},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/shared.py":{"language":"Python","code":62,"comment":3,"blank":10},"file:///home/lars/Projects/music-downloader/src/music_kraken/pages/__init__.py":{"language":"Python","code":6,"comment":0,"blank":5},"file:///home/lars/Projects/music-downloader/src/music_kraken/pages/abstract.py":{"language":"Python","code":203,"comment":32,"blank":61},"file:///home/lars/Projects/music-downloader/src/music_kraken/static_files/temp_database_structure.sql":{"language":"SQLite","code":135,"comment":0,"blank":10},"file:///home/lars/Projects/music-downloader/src/music_kraken/pages/encyclopaedia_metallum.py":{"language":"Python","code":455,"comment":86,"blank":116},"file:///home/lars/Projects/music-downloader/src/music_kraken/static_files/new_db.sql":{"language":"SQLite","code":72,"comment":0,"blank":10},"file:///home/lars/Projects/music-downloader/src/music_kraken/database/data_models.py":{"language":"Python","code":122,"comment":24,"blank":52},"file:///home/lars/Projects/music-downloader/src/music_kraken/pages/musify.py":{"language":"Python","code":509,"comment":248,"blank":137},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/functions.py":{"language":"Python","code":3,"comment":0,"blank":1},"file:///home/lars/Projects/music-downloader/src/music_kraken/pages/youtube.py":{"language":"Python","code":25,"comment":16,"blank":6},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/lyrics.py":{"language":"Python","code":25,"comment":0,"blank":7},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/parents.py":{"language":"Python","code":66,"comment":31,"blank":31},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/song.py":{"language":"Python","code":445,"comment":83,"blank":95},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/album.py":{"language":"Python","code":16,"comment":6,"blank":5},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/cache.py":{"language":"Python","code":37,"comment":56,"blank":18},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/__init__.py":{"language":"Python","code":28,"comment":0,"blank":8},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/source.py":{"language":"Python","code":120,"comment":17,"blank":41},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/collection.py":{"language":"Python","code":84,"comment":30,"blank":37},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/metadata.py":{"language":"Python","code":259,"comment":62,"blank":60},"file:///home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/__init__.py":{"language":"Python","code":4,"comment":0,"blank":2},"file:///home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/page_attributes.py":{"language":"Python","code":24,"comment":1,"blank":9},"file:///home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/download.py":{"language":"Python","code":33,"comment":0,"blank":12},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/option.py":{"language":"Python","code":23,"comment":0,"blank":11},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/formatted_text.py":{"language":"Python","code":50,"comment":10,"blank":19},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/target.py":{"language":"Python","code":29,"comment":7,"blank":9},"file:///home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/search.py":{"language":"Python","code":136,"comment":8,"blank":54},"file:///home/lars/Projects/music-downloader/src/music_kraken/tagging/__init__.py":{"language":"Python","code":8,"comment":0,"blank":2},"file:///home/lars/Projects/music-downloader/src/music_kraken/tagging/id3.py":{"language":"Python","code":51,"comment":4,"blank":20},"file:///home/lars/Projects/music-downloader/src/music_kraken/__main__.py":{"language":"Python","code":3,"comment":0,"blank":3},"file:///home/lars/Projects/music-downloader/src/tests/test_download.py":{"language":"Python","code":30,"comment":1,"blank":12},"file:///home/lars/Projects/music-downloader/src/tests/test_objects.py":{"language":"Python","code":173,"comment":15,"blank":51},"file:///home/lars/Projects/music-downloader/src/tests/__init__.py":{"language":"Python","code":0,"comment":0,"blank":1},"file:///home/lars/Projects/music-downloader/src/create_custom_objects.py":{"language":"Python","code":58,"comment":0,"blank":6},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/fetch_source.py":{"language":"Python","code":54,"comment":1,"blank":16},"file:///home/lars/Projects/music-downloader/src/tests/test_building_objects.py":{"language":"Python","code":81,"comment":1,"blank":13},"file:///home/lars/Projects/music-downloader/src/metal_archives.py":{"language":"Python","code":30,"comment":0,"blank":12},"file:///home/lars/Projects/music-downloader/src/tests/conftest.py":{"language":"Python","code":3,"comment":1,"blank":2},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/__init__.py":{"language":"Python","code":0,"comment":0,"blank":3},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/fetch_audio.py":{"language":"Python","code":75,"comment":12,"blank":20},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/__init__.py":{"language":"Python","code":0,"comment":0,"blank":1},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/genius.py":{"language":"Python","code":115,"comment":16,"blank":42},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/source.py":{"language":"Python","code":11,"comment":5,"blank":8},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/youtube.py":{"language":"Python","code":71,"comment":4,"blank":24},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/musify.py":{"language":"Python","code":136,"comment":9,"blank":37},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/local_files.py":{"language":"Python","code":40,"comment":0,"blank":18},"file:///home/lars/Projects/music-downloader/src/actual_donwload.py":{"language":"Python","code":21,"comment":0,"blank":12},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/metadata_fetch.py":{"language":"Python","code":257,"comment":24,"blank":65},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/__init__.py":{"language":"Python","code":6,"comment":0,"blank":2},"file:///home/lars/Projects/music-downloader/src/music_kraken_cli.py":{"language":"Python","code":3,"comment":0,"blank":3},"file:///home/lars/Projects/music-downloader/src/musify_search.py":{"language":"Python","code":26,"comment":0,"blank":11},"file:///home/lars/Projects/music-downloader/src/music_kraken_gtk.py":{"language":"Python","code":3,"comment":0,"blank":2},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/metadata_search.py":{"language":"Python","code":253,"comment":40,"blank":72},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/sources/musicbrainz.py":{"language":"Python","code":42,"comment":6,"blank":12},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/sources/__init__.py":{"language":"Python","code":3,"comment":0,"blank":2}} \ No newline at end of file diff --git a/.VSCodeCounter/2023-03-30_12-56-39/results.md b/.VSCodeCounter/2023-03-30_12-56-39/results.md deleted file mode 100644 index cd8af28..0000000 --- a/.VSCodeCounter/2023-03-30_12-56-39/results.md +++ /dev/null @@ -1,40 +0,0 @@ -# Summary - -Date : 2023-03-30 12:56:39 - -Directory /home/lars/Projects/music-downloader/src - -Total : 62 files, 4737 codes, 921 comments, 1383 blanks, all 7041 lines - -Summary / [Details](details.md) / [Diff Summary](diff.md) / [Diff Details](diff-details.md) - -## Languages -| language | files | code | comment | blank | total | -| :--- | ---: | ---: | ---: | ---: | ---: | -| Python | 60 | 4,530 | 921 | 1,363 | 6,814 | -| SQLite | 2 | 207 | 0 | 20 | 227 | - -## Directories -| path | files | code | comment | blank | total | -| :--- | ---: | ---: | ---: | ---: | ---: | -| . | 62 | 4,737 | 921 | 1,383 | 7,041 | -| . (Files) | 7 | 141 | 0 | 47 | 188 | -| music_kraken | 50 | 4,309 | 903 | 1,257 | 6,469 | -| music_kraken (Files) | 2 | 51 | 7 | 22 | 80 | -| music_kraken/database | 3 | 226 | 71 | 91 | 388 | -| music_kraken/not_used_anymore | 14 | 1,063 | 117 | 322 | 1,502 | -| music_kraken/not_used_anymore (Files) | 3 | 129 | 13 | 39 | 181 | -| music_kraken/not_used_anymore/metadata | 5 | 561 | 70 | 153 | 784 | -| music_kraken/not_used_anymore/metadata (Files) | 3 | 516 | 64 | 139 | 719 | -| music_kraken/not_used_anymore/metadata/sources | 2 | 45 | 6 | 14 | 65 | -| music_kraken/not_used_anymore/sources | 6 | 373 | 34 | 130 | 537 | -| music_kraken/objects | 12 | 1,182 | 302 | 341 | 1,825 | -| music_kraken/pages | 9 | 1,395 | 391 | 402 | 2,188 | -| music_kraken/pages (Files) | 5 | 1,198 | 382 | 325 | 1,905 | -| music_kraken/pages/download_center | 4 | 197 | 9 | 77 | 283 | -| music_kraken/static_files | 2 | 207 | 0 | 20 | 227 | -| music_kraken/tagging | 2 | 59 | 4 | 22 | 85 | -| music_kraken/utils | 6 | 126 | 11 | 37 | 174 | -| tests | 5 | 287 | 18 | 79 | 384 | - -Summary / [Details](details.md) / [Diff Summary](diff.md) / [Diff Details](diff-details.md) \ No newline at end of file diff --git a/.VSCodeCounter/2023-03-30_12-56-39/results.txt b/.VSCodeCounter/2023-03-30_12-56-39/results.txt deleted file mode 100644 index c3721e4..0000000 --- a/.VSCodeCounter/2023-03-30_12-56-39/results.txt +++ /dev/null @@ -1,105 +0,0 @@ -Date : 2023-03-30 12:56:39 -Directory : /home/lars/Projects/music-downloader/src -Total : 62 files, 4737 codes, 921 comments, 1383 blanks, all 7041 lines - -Languages -+----------+------------+------------+------------+------------+------------+ -| language | files | code | comment | blank | total | -+----------+------------+------------+------------+------------+------------+ -| Python | 60 | 4,530 | 921 | 1,363 | 6,814 | -| SQLite | 2 | 207 | 0 | 20 | 227 | -+----------+------------+------------+------------+------------+------------+ - -Directories -+--------------------------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ -| path | files | code | comment | blank | total | -+--------------------------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ -| . | 62 | 4,737 | 921 | 1,383 | 7,041 | -| . (Files) | 7 | 141 | 0 | 47 | 188 | -| music_kraken | 50 | 4,309 | 903 | 1,257 | 6,469 | -| music_kraken (Files) | 2 | 51 | 7 | 22 | 80 | -| music_kraken/database | 3 | 226 | 71 | 91 | 388 | -| music_kraken/not_used_anymore | 14 | 1,063 | 117 | 322 | 1,502 | -| music_kraken/not_used_anymore (Files) | 3 | 129 | 13 | 39 | 181 | -| music_kraken/not_used_anymore/metadata | 5 | 561 | 70 | 153 | 784 | -| music_kraken/not_used_anymore/metadata (Files) | 3 | 516 | 64 | 139 | 719 | -| music_kraken/not_used_anymore/metadata/sources | 2 | 45 | 6 | 14 | 65 | -| music_kraken/not_used_anymore/sources | 6 | 373 | 34 | 130 | 537 | -| music_kraken/objects | 12 | 1,182 | 302 | 341 | 1,825 | -| music_kraken/pages | 9 | 1,395 | 391 | 402 | 2,188 | -| music_kraken/pages (Files) | 5 | 1,198 | 382 | 325 | 1,905 | -| music_kraken/pages/download_center | 4 | 197 | 9 | 77 | 283 | -| music_kraken/static_files | 2 | 207 | 0 | 20 | 227 | -| music_kraken/tagging | 2 | 59 | 4 | 22 | 85 | -| music_kraken/utils | 6 | 126 | 11 | 37 | 174 | -| tests | 5 | 287 | 18 | 79 | 384 | -+--------------------------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ - -Files -+--------------------------------------------------------------------------------------------------------+----------+------------+------------+------------+------------+ -| filename | language | code | comment | blank | total | -+--------------------------------------------------------------------------------------------------------+----------+------------+------------+------------+------------+ -| /home/lars/Projects/music-downloader/src/__init__.py | Python | 0 | 0 | 1 | 1 | -| /home/lars/Projects/music-downloader/src/actual_donwload.py | Python | 21 | 0 | 12 | 33 | -| /home/lars/Projects/music-downloader/src/create_custom_objects.py | Python | 58 | 0 | 6 | 64 | -| /home/lars/Projects/music-downloader/src/metal_archives.py | Python | 30 | 0 | 12 | 42 | -| /home/lars/Projects/music-downloader/src/music_kraken/__init__.py | Python | 48 | 7 | 19 | 74 | -| /home/lars/Projects/music-downloader/src/music_kraken/__main__.py | Python | 3 | 0 | 3 | 6 | -| /home/lars/Projects/music-downloader/src/music_kraken/database/__init__.py | Python | 0 | 0 | 1 | 1 | -| /home/lars/Projects/music-downloader/src/music_kraken/database/data_models.py | Python | 122 | 24 | 52 | 198 | -| /home/lars/Projects/music-downloader/src/music_kraken/database/database.py | Python | 104 | 47 | 38 | 189 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/__init__.py | Python | 0 | 0 | 3 | 3 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/fetch_audio.py | Python | 75 | 12 | 20 | 107 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/fetch_source.py | Python | 54 | 1 | 16 | 71 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/__init__.py | Python | 6 | 0 | 2 | 8 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/metadata_fetch.py | Python | 257 | 24 | 65 | 346 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/metadata_search.py | Python | 253 | 40 | 72 | 365 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/sources/__init__.py | Python | 3 | 0 | 2 | 5 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/sources/musicbrainz.py | Python | 42 | 6 | 12 | 60 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/__init__.py | Python | 0 | 0 | 1 | 1 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/genius.py | Python | 115 | 16 | 42 | 173 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/local_files.py | Python | 40 | 0 | 18 | 58 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/musify.py | Python | 136 | 9 | 37 | 182 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/source.py | Python | 11 | 5 | 8 | 24 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/youtube.py | Python | 71 | 4 | 24 | 99 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/__init__.py | Python | 28 | 0 | 8 | 36 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/album.py | Python | 16 | 6 | 5 | 27 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/cache.py | Python | 37 | 56 | 18 | 111 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/collection.py | Python | 84 | 30 | 37 | 151 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/formatted_text.py | Python | 50 | 10 | 19 | 79 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/lyrics.py | Python | 25 | 0 | 7 | 32 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/metadata.py | Python | 259 | 62 | 60 | 381 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/option.py | Python | 23 | 0 | 11 | 34 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/parents.py | Python | 66 | 31 | 31 | 128 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/song.py | Python | 445 | 83 | 95 | 623 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/source.py | Python | 120 | 17 | 41 | 178 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/target.py | Python | 29 | 7 | 9 | 45 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/__init__.py | Python | 6 | 0 | 5 | 11 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/abstract.py | Python | 203 | 32 | 61 | 296 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/__init__.py | Python | 4 | 0 | 2 | 6 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/download.py | Python | 33 | 0 | 12 | 45 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/page_attributes.py | Python | 24 | 1 | 9 | 34 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/search.py | Python | 136 | 8 | 54 | 198 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/encyclopaedia_metallum.py | Python | 455 | 86 | 116 | 657 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/musify.py | Python | 509 | 248 | 137 | 894 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/youtube.py | Python | 25 | 16 | 6 | 47 | -| /home/lars/Projects/music-downloader/src/music_kraken/static_files/new_db.sql | SQLite | 72 | 0 | 10 | 82 | -| /home/lars/Projects/music-downloader/src/music_kraken/static_files/temp_database_structure.sql | SQLite | 135 | 0 | 10 | 145 | -| /home/lars/Projects/music-downloader/src/music_kraken/tagging/__init__.py | Python | 8 | 0 | 2 | 10 | -| /home/lars/Projects/music-downloader/src/music_kraken/tagging/id3.py | Python | 51 | 4 | 20 | 75 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/__init__.py | Python | 1 | 1 | 1 | 3 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/functions.py | Python | 3 | 0 | 1 | 4 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/object_handeling.py | Python | 19 | 0 | 6 | 25 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/phonetic_compares.py | Python | 39 | 2 | 17 | 58 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/shared.py | Python | 62 | 3 | 10 | 75 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/string_processing.py | Python | 2 | 5 | 2 | 9 | -| /home/lars/Projects/music-downloader/src/music_kraken_cli.py | Python | 3 | 0 | 3 | 6 | -| /home/lars/Projects/music-downloader/src/music_kraken_gtk.py | Python | 3 | 0 | 2 | 5 | -| /home/lars/Projects/music-downloader/src/musify_search.py | Python | 26 | 0 | 11 | 37 | -| /home/lars/Projects/music-downloader/src/tests/__init__.py | Python | 0 | 0 | 1 | 1 | -| /home/lars/Projects/music-downloader/src/tests/conftest.py | Python | 3 | 1 | 2 | 6 | -| /home/lars/Projects/music-downloader/src/tests/test_building_objects.py | Python | 81 | 1 | 13 | 95 | -| /home/lars/Projects/music-downloader/src/tests/test_download.py | Python | 30 | 1 | 12 | 43 | -| /home/lars/Projects/music-downloader/src/tests/test_objects.py | Python | 173 | 15 | 51 | 239 | -| Total | | 4,737 | 921 | 1,383 | 7,041 | -+--------------------------------------------------------------------------------------------------------+----------+------------+------------+------------+------------+ \ No newline at end of file diff --git a/.VSCodeCounter/2023-04-05_09-31-17/details.md b/.VSCodeCounter/2023-04-05_09-31-17/details.md deleted file mode 100644 index f6c75fb..0000000 --- a/.VSCodeCounter/2023-04-05_09-31-17/details.md +++ /dev/null @@ -1,81 +0,0 @@ -# Details - -Date : 2023-04-05 09:31:17 - -Directory /home/lars/Projects/music-downloader/src - -Total : 66 files, 5256 codes, 1000 comments, 1543 blanks, all 7799 lines - -[Summary](results.md) / Details / [Diff Summary](diff.md) / [Diff Details](diff-details.md) - -## Files -| filename | language | code | comment | blank | total | -| :--- | :--- | ---: | ---: | ---: | ---: | -| [src/__init__.py](/src/__init__.py) | Python | 0 | 0 | 1 | 1 | -| [src/actual_donwload.py](/src/actual_donwload.py) | Python | 37 | 0 | 17 | 54 | -| [src/create_custom_objects.py](/src/create_custom_objects.py) | Python | 58 | 0 | 6 | 64 | -| [src/metal_archives.py](/src/metal_archives.py) | Python | 30 | 0 | 12 | 42 | -| [src/music_kraken/__init__.py](/src/music_kraken/__init__.py) | Python | 86 | 12 | 28 | 126 | -| [src/music_kraken/__main__.py](/src/music_kraken/__main__.py) | Python | 3 | 0 | 3 | 6 | -| [src/music_kraken/database/__init__.py](/src/music_kraken/database/__init__.py) | Python | 0 | 0 | 1 | 1 | -| [src/music_kraken/database/data_models.py](/src/music_kraken/database/data_models.py) | Python | 122 | 24 | 52 | 198 | -| [src/music_kraken/database/database.py](/src/music_kraken/database/database.py) | Python | 104 | 47 | 38 | 189 | -| [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 | -| [src/music_kraken/not_used_anymore/fetch_source.py](/src/music_kraken/not_used_anymore/fetch_source.py) | Python | 54 | 1 | 16 | 71 | -| [src/music_kraken/not_used_anymore/metadata/__init__.py](/src/music_kraken/not_used_anymore/metadata/__init__.py) | Python | 6 | 0 | 2 | 8 | -| [src/music_kraken/not_used_anymore/metadata/metadata_fetch.py](/src/music_kraken/not_used_anymore/metadata/metadata_fetch.py) | Python | 257 | 24 | 65 | 346 | -| [src/music_kraken/not_used_anymore/metadata/metadata_search.py](/src/music_kraken/not_used_anymore/metadata/metadata_search.py) | Python | 253 | 40 | 72 | 365 | -| [src/music_kraken/not_used_anymore/metadata/sources/__init__.py](/src/music_kraken/not_used_anymore/metadata/sources/__init__.py) | Python | 3 | 0 | 2 | 5 | -| [src/music_kraken/not_used_anymore/metadata/sources/musicbrainz.py](/src/music_kraken/not_used_anymore/metadata/sources/musicbrainz.py) | Python | 42 | 6 | 12 | 60 | -| [src/music_kraken/not_used_anymore/sources/__init__.py](/src/music_kraken/not_used_anymore/sources/__init__.py) | Python | 0 | 0 | 1 | 1 | -| [src/music_kraken/not_used_anymore/sources/genius.py](/src/music_kraken/not_used_anymore/sources/genius.py) | Python | 115 | 16 | 42 | 173 | -| [src/music_kraken/not_used_anymore/sources/local_files.py](/src/music_kraken/not_used_anymore/sources/local_files.py) | Python | 40 | 0 | 18 | 58 | -| [src/music_kraken/not_used_anymore/sources/musify.py](/src/music_kraken/not_used_anymore/sources/musify.py) | Python | 136 | 9 | 37 | 182 | -| [src/music_kraken/not_used_anymore/sources/source.py](/src/music_kraken/not_used_anymore/sources/source.py) | Python | 11 | 5 | 8 | 24 | -| [src/music_kraken/not_used_anymore/sources/youtube.py](/src/music_kraken/not_used_anymore/sources/youtube.py) | Python | 71 | 4 | 24 | 99 | -| [src/music_kraken/objects/__init__.py](/src/music_kraken/objects/__init__.py) | Python | 29 | 0 | 8 | 37 | -| [src/music_kraken/objects/album.py](/src/music_kraken/objects/album.py) | Python | 16 | 6 | 5 | 27 | -| [src/music_kraken/objects/cache.py](/src/music_kraken/objects/cache.py) | Python | 37 | 56 | 18 | 111 | -| [src/music_kraken/objects/collection.py](/src/music_kraken/objects/collection.py) | Python | 91 | 31 | 39 | 161 | -| [src/music_kraken/objects/formatted_text.py](/src/music_kraken/objects/formatted_text.py) | Python | 50 | 10 | 19 | 79 | -| [src/music_kraken/objects/lyrics.py](/src/music_kraken/objects/lyrics.py) | Python | 25 | 0 | 7 | 32 | -| [src/music_kraken/objects/metadata.py](/src/music_kraken/objects/metadata.py) | Python | 261 | 62 | 61 | 384 | -| [src/music_kraken/objects/option.py](/src/music_kraken/objects/option.py) | Python | 23 | 0 | 11 | 34 | -| [src/music_kraken/objects/parents.py](/src/music_kraken/objects/parents.py) | Python | 62 | 31 | 31 | 124 | -| [src/music_kraken/objects/song.py](/src/music_kraken/objects/song.py) | Python | 436 | 93 | 99 | 628 | -| [src/music_kraken/objects/source.py](/src/music_kraken/objects/source.py) | Python | 120 | 17 | 41 | 178 | -| [src/music_kraken/objects/target.py](/src/music_kraken/objects/target.py) | Python | 58 | 11 | 21 | 90 | -| [src/music_kraken/pages/__init__.py](/src/music_kraken/pages/__init__.py) | Python | 6 | 0 | 5 | 11 | -| [src/music_kraken/pages/abstract.py](/src/music_kraken/pages/abstract.py) | Python | 428 | 44 | 103 | 575 | -| [src/music_kraken/pages/download_center/__init__.py](/src/music_kraken/pages/download_center/__init__.py) | Python | 4 | 0 | 2 | 6 | -| [src/music_kraken/pages/download_center/download.py](/src/music_kraken/pages/download_center/download.py) | Python | 33 | 0 | 13 | 46 | -| [src/music_kraken/pages/download_center/multiple_options.py](/src/music_kraken/pages/download_center/multiple_options.py) | Python | 69 | 0 | 31 | 100 | -| [src/music_kraken/pages/download_center/page_attributes.py](/src/music_kraken/pages/download_center/page_attributes.py) | Python | 24 | 1 | 9 | 34 | -| [src/music_kraken/pages/download_center/search.py](/src/music_kraken/pages/download_center/search.py) | Python | 85 | 23 | 38 | 146 | -| [src/music_kraken/pages/encyclopaedia_metallum.py](/src/music_kraken/pages/encyclopaedia_metallum.py) | Python | 451 | 83 | 117 | 651 | -| [src/music_kraken/pages/musify.py](/src/music_kraken/pages/musify.py) | Python | 564 | 259 | 155 | 978 | -| [src/music_kraken/pages/support_classes/__init__.py](/src/music_kraken/pages/support_classes/__init__.py) | Python | 0 | 0 | 1 | 1 | -| [src/music_kraken/pages/support_classes/default_target.py](/src/music_kraken/pages/support_classes/default_target.py) | Python | 55 | 0 | 15 | 70 | -| [src/music_kraken/pages/support_classes/download_result.py](/src/music_kraken/pages/support_classes/download_result.py) | Python | 51 | 0 | 15 | 66 | -| [src/music_kraken/pages/youtube.py](/src/music_kraken/pages/youtube.py) | Python | 25 | 16 | 6 | 47 | -| [src/music_kraken/static_files/new_db.sql](/src/music_kraken/static_files/new_db.sql) | SQLite | 72 | 0 | 10 | 82 | -| [src/music_kraken/static_files/temp_database_structure.sql](/src/music_kraken/static_files/temp_database_structure.sql) | SQLite | 135 | 0 | 10 | 145 | -| [src/music_kraken/tagging/__init__.py](/src/music_kraken/tagging/__init__.py) | Python | 5 | 0 | 2 | 7 | -| [src/music_kraken/tagging/id3.py](/src/music_kraken/tagging/id3.py) | Python | 60 | 4 | 24 | 88 | -| [src/music_kraken/utils/__init__.py](/src/music_kraken/utils/__init__.py) | Python | 1 | 1 | 1 | 3 | -| [src/music_kraken/utils/functions.py](/src/music_kraken/utils/functions.py) | Python | 3 | 0 | 1 | 4 | -| [src/music_kraken/utils/object_handeling.py](/src/music_kraken/utils/object_handeling.py) | Python | 19 | 0 | 6 | 25 | -| [src/music_kraken/utils/phonetic_compares.py](/src/music_kraken/utils/phonetic_compares.py) | Python | 39 | 2 | 17 | 58 | -| [src/music_kraken/utils/shared.py](/src/music_kraken/utils/shared.py) | Python | 75 | 27 | 17 | 119 | -| [src/music_kraken/utils/string_processing.py](/src/music_kraken/utils/string_processing.py) | Python | 10 | 5 | 7 | 22 | -| [src/music_kraken_cli.py](/src/music_kraken_cli.py) | Python | 3 | 0 | 3 | 6 | -| [src/music_kraken_gtk.py](/src/music_kraken_gtk.py) | Python | 3 | 0 | 2 | 5 | -| [src/musify_search.py](/src/musify_search.py) | Python | 38 | 0 | 14 | 52 | -| [src/tests/__init__.py](/src/tests/__init__.py) | Python | 0 | 0 | 1 | 1 | -| [src/tests/conftest.py](/src/tests/conftest.py) | Python | 3 | 1 | 2 | 6 | -| [src/tests/test_building_objects.py](/src/tests/test_building_objects.py) | Python | 81 | 1 | 13 | 95 | -| [src/tests/test_download.py](/src/tests/test_download.py) | Python | 30 | 1 | 12 | 43 | -| [src/tests/test_objects.py](/src/tests/test_objects.py) | Python | 173 | 15 | 51 | 239 | - -[Summary](results.md) / Details / [Diff Summary](diff.md) / [Diff Details](diff-details.md) \ No newline at end of file diff --git a/.VSCodeCounter/2023-04-05_09-31-17/diff-details.md b/.VSCodeCounter/2023-04-05_09-31-17/diff-details.md deleted file mode 100644 index 7a53575..0000000 --- a/.VSCodeCounter/2023-04-05_09-31-17/diff-details.md +++ /dev/null @@ -1,37 +0,0 @@ -# Diff Details - -Date : 2023-04-05 09:31:17 - -Directory /home/lars/Projects/music-downloader/src - -Total : 22 files, 519 codes, 79 comments, 160 blanks, all 758 lines - -[Summary](results.md) / [Details](details.md) / [Diff Summary](diff.md) / Diff Details - -## Files -| filename | language | code | comment | blank | total | -| :--- | :--- | ---: | ---: | ---: | ---: | -| [src/actual_donwload.py](/src/actual_donwload.py) | Python | 16 | 0 | 5 | 21 | -| [src/music_kraken/__init__.py](/src/music_kraken/__init__.py) | Python | 38 | 5 | 9 | 52 | -| [src/music_kraken/objects/__init__.py](/src/music_kraken/objects/__init__.py) | Python | 1 | 0 | 0 | 1 | -| [src/music_kraken/objects/collection.py](/src/music_kraken/objects/collection.py) | Python | 7 | 1 | 2 | 10 | -| [src/music_kraken/objects/metadata.py](/src/music_kraken/objects/metadata.py) | Python | 2 | 0 | 1 | 3 | -| [src/music_kraken/objects/parents.py](/src/music_kraken/objects/parents.py) | Python | -4 | 0 | 0 | -4 | -| [src/music_kraken/objects/song.py](/src/music_kraken/objects/song.py) | Python | -9 | 10 | 4 | 5 | -| [src/music_kraken/objects/target.py](/src/music_kraken/objects/target.py) | Python | 29 | 4 | 12 | 45 | -| [src/music_kraken/pages/abstract.py](/src/music_kraken/pages/abstract.py) | Python | 225 | 12 | 42 | 279 | -| [src/music_kraken/pages/download_center/download.py](/src/music_kraken/pages/download_center/download.py) | Python | 0 | 0 | 1 | 1 | -| [src/music_kraken/pages/download_center/multiple_options.py](/src/music_kraken/pages/download_center/multiple_options.py) | Python | 69 | 0 | 31 | 100 | -| [src/music_kraken/pages/download_center/search.py](/src/music_kraken/pages/download_center/search.py) | Python | -51 | 15 | -16 | -52 | -| [src/music_kraken/pages/encyclopaedia_metallum.py](/src/music_kraken/pages/encyclopaedia_metallum.py) | Python | -4 | -3 | 1 | -6 | -| [src/music_kraken/pages/musify.py](/src/music_kraken/pages/musify.py) | Python | 55 | 11 | 18 | 84 | -| [src/music_kraken/pages/support_classes/__init__.py](/src/music_kraken/pages/support_classes/__init__.py) | Python | 0 | 0 | 1 | 1 | -| [src/music_kraken/pages/support_classes/default_target.py](/src/music_kraken/pages/support_classes/default_target.py) | Python | 55 | 0 | 15 | 70 | -| [src/music_kraken/pages/support_classes/download_result.py](/src/music_kraken/pages/support_classes/download_result.py) | Python | 51 | 0 | 15 | 66 | -| [src/music_kraken/tagging/__init__.py](/src/music_kraken/tagging/__init__.py) | Python | -3 | 0 | 0 | -3 | -| [src/music_kraken/tagging/id3.py](/src/music_kraken/tagging/id3.py) | Python | 9 | 0 | 4 | 13 | -| [src/music_kraken/utils/shared.py](/src/music_kraken/utils/shared.py) | Python | 13 | 24 | 7 | 44 | -| [src/music_kraken/utils/string_processing.py](/src/music_kraken/utils/string_processing.py) | Python | 8 | 0 | 5 | 13 | -| [src/musify_search.py](/src/musify_search.py) | Python | 12 | 0 | 3 | 15 | - -[Summary](results.md) / [Details](details.md) / [Diff Summary](diff.md) / Diff Details \ No newline at end of file diff --git a/.VSCodeCounter/2023-04-05_09-31-17/diff.csv b/.VSCodeCounter/2023-04-05_09-31-17/diff.csv deleted file mode 100644 index 0c14433..0000000 --- a/.VSCodeCounter/2023-04-05_09-31-17/diff.csv +++ /dev/null @@ -1,24 +0,0 @@ -"filename", "language", "Python", "comment", "blank", "total" -"/home/lars/Projects/music-downloader/src/actual_donwload.py", "Python", 16, 0, 5, 21 -"/home/lars/Projects/music-downloader/src/music_kraken/__init__.py", "Python", 38, 5, 9, 52 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/__init__.py", "Python", 1, 0, 0, 1 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/collection.py", "Python", 7, 1, 2, 10 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/metadata.py", "Python", 2, 0, 1, 3 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/parents.py", "Python", -4, 0, 0, -4 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/song.py", "Python", -9, 10, 4, 5 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/target.py", "Python", 29, 4, 12, 45 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/abstract.py", "Python", 225, 12, 42, 279 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/download.py", "Python", 0, 0, 1, 1 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/multiple_options.py", "Python", 69, 0, 31, 100 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/search.py", "Python", -51, 15, -16, -52 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/encyclopaedia_metallum.py", "Python", -4, -3, 1, -6 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/musify.py", "Python", 55, 11, 18, 84 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/support_classes/__init__.py", "Python", 0, 0, 1, 1 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/support_classes/default_target.py", "Python", 55, 0, 15, 70 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/support_classes/download_result.py", "Python", 51, 0, 15, 66 -"/home/lars/Projects/music-downloader/src/music_kraken/tagging/__init__.py", "Python", -3, 0, 0, -3 -"/home/lars/Projects/music-downloader/src/music_kraken/tagging/id3.py", "Python", 9, 0, 4, 13 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/shared.py", "Python", 13, 24, 7, 44 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/string_processing.py", "Python", 8, 0, 5, 13 -"/home/lars/Projects/music-downloader/src/musify_search.py", "Python", 12, 0, 3, 15 -"Total", "-", 519, 79, 160, 758 \ No newline at end of file diff --git a/.VSCodeCounter/2023-04-05_09-31-17/diff.md b/.VSCodeCounter/2023-04-05_09-31-17/diff.md deleted file mode 100644 index 883be25..0000000 --- a/.VSCodeCounter/2023-04-05_09-31-17/diff.md +++ /dev/null @@ -1,31 +0,0 @@ -# Diff Summary - -Date : 2023-04-05 09:31:17 - -Directory /home/lars/Projects/music-downloader/src - -Total : 22 files, 519 codes, 79 comments, 160 blanks, all 758 lines - -[Summary](results.md) / [Details](details.md) / Diff Summary / [Diff Details](diff-details.md) - -## Languages -| language | files | code | comment | blank | total | -| :--- | ---: | ---: | ---: | ---: | ---: | -| Python | 22 | 519 | 79 | 160 | 758 | - -## Directories -| path | files | code | comment | blank | total | -| :--- | ---: | ---: | ---: | ---: | ---: | -| . | 22 | 519 | 79 | 160 | 758 | -| . (Files) | 2 | 28 | 0 | 8 | 36 | -| music_kraken | 20 | 491 | 79 | 152 | 722 | -| music_kraken (Files) | 1 | 38 | 5 | 9 | 52 | -| music_kraken/objects | 6 | 26 | 15 | 19 | 60 | -| music_kraken/pages | 9 | 400 | 35 | 108 | 543 | -| music_kraken/pages (Files) | 3 | 276 | 20 | 61 | 357 | -| music_kraken/pages/download_center | 3 | 18 | 15 | 16 | 49 | -| music_kraken/pages/support_classes | 3 | 106 | 0 | 31 | 137 | -| music_kraken/tagging | 2 | 6 | 0 | 4 | 10 | -| music_kraken/utils | 2 | 21 | 24 | 12 | 57 | - -[Summary](results.md) / [Details](details.md) / Diff Summary / [Diff Details](diff-details.md) \ No newline at end of file diff --git a/.VSCodeCounter/2023-04-05_09-31-17/diff.txt b/.VSCodeCounter/2023-04-05_09-31-17/diff.txt deleted file mode 100644 index 6c1a0a2..0000000 --- a/.VSCodeCounter/2023-04-05_09-31-17/diff.txt +++ /dev/null @@ -1,56 +0,0 @@ -Date : 2023-04-05 09:31:17 -Directory : /home/lars/Projects/music-downloader/src -Total : 22 files, 519 codes, 79 comments, 160 blanks, all 758 lines - -Languages -+----------+------------+------------+------------+------------+------------+ -| language | files | code | comment | blank | total | -+----------+------------+------------+------------+------------+------------+ -| Python | 22 | 519 | 79 | 160 | 758 | -+----------+------------+------------+------------+------------+------------+ - -Directories -+-------------------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ -| path | files | code | comment | blank | total | -+-------------------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ -| . | 22 | 519 | 79 | 160 | 758 | -| . (Files) | 2 | 28 | 0 | 8 | 36 | -| music_kraken | 20 | 491 | 79 | 152 | 722 | -| music_kraken (Files) | 1 | 38 | 5 | 9 | 52 | -| music_kraken/objects | 6 | 26 | 15 | 19 | 60 | -| music_kraken/pages | 9 | 400 | 35 | 108 | 543 | -| music_kraken/pages (Files) | 3 | 276 | 20 | 61 | 357 | -| music_kraken/pages/download_center | 3 | 18 | 15 | 16 | 49 | -| music_kraken/pages/support_classes | 3 | 106 | 0 | 31 | 137 | -| music_kraken/tagging | 2 | 6 | 0 | 4 | 10 | -| music_kraken/utils | 2 | 21 | 24 | 12 | 57 | -+-------------------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ - -Files -+-------------------------------------------------------------------------------------------------+----------+------------+------------+------------+------------+ -| filename | language | code | comment | blank | total | -+-------------------------------------------------------------------------------------------------+----------+------------+------------+------------+------------+ -| /home/lars/Projects/music-downloader/src/actual_donwload.py | Python | 16 | 0 | 5 | 21 | -| /home/lars/Projects/music-downloader/src/music_kraken/__init__.py | Python | 38 | 5 | 9 | 52 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/__init__.py | Python | 1 | 0 | 0 | 1 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/collection.py | Python | 7 | 1 | 2 | 10 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/metadata.py | Python | 2 | 0 | 1 | 3 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/parents.py | Python | -4 | 0 | 0 | -4 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/song.py | Python | -9 | 10 | 4 | 5 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/target.py | Python | 29 | 4 | 12 | 45 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/abstract.py | Python | 225 | 12 | 42 | 279 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/download.py | Python | 0 | 0 | 1 | 1 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/multiple_options.py | Python | 69 | 0 | 31 | 100 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/search.py | Python | -51 | 15 | -16 | -52 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/encyclopaedia_metallum.py | Python | -4 | -3 | 1 | -6 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/musify.py | Python | 55 | 11 | 18 | 84 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/support_classes/__init__.py | Python | 0 | 0 | 1 | 1 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/support_classes/default_target.py | Python | 55 | 0 | 15 | 70 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/support_classes/download_result.py | Python | 51 | 0 | 15 | 66 | -| /home/lars/Projects/music-downloader/src/music_kraken/tagging/__init__.py | Python | -3 | 0 | 0 | -3 | -| /home/lars/Projects/music-downloader/src/music_kraken/tagging/id3.py | Python | 9 | 0 | 4 | 13 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/shared.py | Python | 13 | 24 | 7 | 44 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/string_processing.py | Python | 8 | 0 | 5 | 13 | -| /home/lars/Projects/music-downloader/src/musify_search.py | Python | 12 | 0 | 3 | 15 | -| Total | | 519 | 79 | 160 | 758 | -+-------------------------------------------------------------------------------------------------+----------+------------+------------+------------+------------+ \ No newline at end of file diff --git a/.VSCodeCounter/2023-04-05_09-31-17/results.csv b/.VSCodeCounter/2023-04-05_09-31-17/results.csv deleted file mode 100644 index dc7322c..0000000 --- a/.VSCodeCounter/2023-04-05_09-31-17/results.csv +++ /dev/null @@ -1,68 +0,0 @@ -"filename", "language", "Python", "SQLite", "comment", "blank", "total" -"/home/lars/Projects/music-downloader/src/__init__.py", "Python", 0, 0, 0, 1, 1 -"/home/lars/Projects/music-downloader/src/actual_donwload.py", "Python", 37, 0, 0, 17, 54 -"/home/lars/Projects/music-downloader/src/create_custom_objects.py", "Python", 58, 0, 0, 6, 64 -"/home/lars/Projects/music-downloader/src/metal_archives.py", "Python", 30, 0, 0, 12, 42 -"/home/lars/Projects/music-downloader/src/music_kraken/__init__.py", "Python", 86, 0, 12, 28, 126 -"/home/lars/Projects/music-downloader/src/music_kraken/__main__.py", "Python", 3, 0, 0, 3, 6 -"/home/lars/Projects/music-downloader/src/music_kraken/database/__init__.py", "Python", 0, 0, 0, 1, 1 -"/home/lars/Projects/music-downloader/src/music_kraken/database/data_models.py", "Python", 122, 0, 24, 52, 198 -"/home/lars/Projects/music-downloader/src/music_kraken/database/database.py", "Python", 104, 0, 47, 38, 189 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/__init__.py", "Python", 0, 0, 0, 3, 3 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/fetch_audio.py", "Python", 75, 0, 12, 20, 107 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/fetch_source.py", "Python", 54, 0, 1, 16, 71 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/__init__.py", "Python", 6, 0, 0, 2, 8 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/metadata_fetch.py", "Python", 257, 0, 24, 65, 346 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/metadata_search.py", "Python", 253, 0, 40, 72, 365 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/sources/__init__.py", "Python", 3, 0, 0, 2, 5 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/sources/musicbrainz.py", "Python", 42, 0, 6, 12, 60 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/__init__.py", "Python", 0, 0, 0, 1, 1 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/genius.py", "Python", 115, 0, 16, 42, 173 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/local_files.py", "Python", 40, 0, 0, 18, 58 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/musify.py", "Python", 136, 0, 9, 37, 182 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/source.py", "Python", 11, 0, 5, 8, 24 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/youtube.py", "Python", 71, 0, 4, 24, 99 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/__init__.py", "Python", 29, 0, 0, 8, 37 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/album.py", "Python", 16, 0, 6, 5, 27 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/cache.py", "Python", 37, 0, 56, 18, 111 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/collection.py", "Python", 91, 0, 31, 39, 161 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/formatted_text.py", "Python", 50, 0, 10, 19, 79 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/lyrics.py", "Python", 25, 0, 0, 7, 32 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/metadata.py", "Python", 261, 0, 62, 61, 384 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/option.py", "Python", 23, 0, 0, 11, 34 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/parents.py", "Python", 62, 0, 31, 31, 124 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/song.py", "Python", 436, 0, 93, 99, 628 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/source.py", "Python", 120, 0, 17, 41, 178 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/target.py", "Python", 58, 0, 11, 21, 90 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/__init__.py", "Python", 6, 0, 0, 5, 11 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/abstract.py", "Python", 428, 0, 44, 103, 575 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/__init__.py", "Python", 4, 0, 0, 2, 6 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/download.py", "Python", 33, 0, 0, 13, 46 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/multiple_options.py", "Python", 69, 0, 0, 31, 100 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/page_attributes.py", "Python", 24, 0, 1, 9, 34 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/search.py", "Python", 85, 0, 23, 38, 146 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/encyclopaedia_metallum.py", "Python", 451, 0, 83, 117, 651 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/musify.py", "Python", 564, 0, 259, 155, 978 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/support_classes/__init__.py", "Python", 0, 0, 0, 1, 1 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/support_classes/default_target.py", "Python", 55, 0, 0, 15, 70 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/support_classes/download_result.py", "Python", 51, 0, 0, 15, 66 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/youtube.py", "Python", 25, 0, 16, 6, 47 -"/home/lars/Projects/music-downloader/src/music_kraken/static_files/new_db.sql", "SQLite", 0, 72, 0, 10, 82 -"/home/lars/Projects/music-downloader/src/music_kraken/static_files/temp_database_structure.sql", "SQLite", 0, 135, 0, 10, 145 -"/home/lars/Projects/music-downloader/src/music_kraken/tagging/__init__.py", "Python", 5, 0, 0, 2, 7 -"/home/lars/Projects/music-downloader/src/music_kraken/tagging/id3.py", "Python", 60, 0, 4, 24, 88 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/__init__.py", "Python", 1, 0, 1, 1, 3 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/functions.py", "Python", 3, 0, 0, 1, 4 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/object_handeling.py", "Python", 19, 0, 0, 6, 25 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/phonetic_compares.py", "Python", 39, 0, 2, 17, 58 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/shared.py", "Python", 75, 0, 27, 17, 119 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/string_processing.py", "Python", 10, 0, 5, 7, 22 -"/home/lars/Projects/music-downloader/src/music_kraken_cli.py", "Python", 3, 0, 0, 3, 6 -"/home/lars/Projects/music-downloader/src/music_kraken_gtk.py", "Python", 3, 0, 0, 2, 5 -"/home/lars/Projects/music-downloader/src/musify_search.py", "Python", 38, 0, 0, 14, 52 -"/home/lars/Projects/music-downloader/src/tests/__init__.py", "Python", 0, 0, 0, 1, 1 -"/home/lars/Projects/music-downloader/src/tests/conftest.py", "Python", 3, 0, 1, 2, 6 -"/home/lars/Projects/music-downloader/src/tests/test_building_objects.py", "Python", 81, 0, 1, 13, 95 -"/home/lars/Projects/music-downloader/src/tests/test_download.py", "Python", 30, 0, 1, 12, 43 -"/home/lars/Projects/music-downloader/src/tests/test_objects.py", "Python", 173, 0, 15, 51, 239 -"Total", "-", 5049, 207, 1000, 1543, 7799 \ No newline at end of file diff --git a/.VSCodeCounter/2023-04-05_09-31-17/results.json b/.VSCodeCounter/2023-04-05_09-31-17/results.json deleted file mode 100644 index a679e13..0000000 --- a/.VSCodeCounter/2023-04-05_09-31-17/results.json +++ /dev/null @@ -1 +0,0 @@ -{"file:///home/lars/Projects/music-downloader/src/actual_donwload.py":{"language":"Python","code":37,"comment":0,"blank":17},"file:///home/lars/Projects/music-downloader/src/music_kraken_cli.py":{"language":"Python","code":3,"comment":0,"blank":3},"file:///home/lars/Projects/music-downloader/src/metal_archives.py":{"language":"Python","code":30,"comment":0,"blank":12},"file:///home/lars/Projects/music-downloader/src/__init__.py":{"language":"Python","code":0,"comment":0,"blank":1},"file:///home/lars/Projects/music-downloader/src/create_custom_objects.py":{"language":"Python","code":58,"comment":0,"blank":6},"file:///home/lars/Projects/music-downloader/src/music_kraken/static_files/temp_database_structure.sql":{"language":"SQLite","code":135,"comment":0,"blank":10},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/option.py":{"language":"Python","code":23,"comment":0,"blank":11},"file:///home/lars/Projects/music-downloader/src/music_kraken/pages/__init__.py":{"language":"Python","code":6,"comment":0,"blank":5},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/song.py":{"language":"Python","code":436,"comment":93,"blank":99},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/__init__.py":{"language":"Python","code":29,"comment":0,"blank":8},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/cache.py":{"language":"Python","code":37,"comment":56,"blank":18},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/album.py":{"language":"Python","code":16,"comment":6,"blank":5},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/source.py":{"language":"Python","code":120,"comment":17,"blank":41},"file:///home/lars/Projects/music-downloader/src/music_kraken/database/database.py":{"language":"Python","code":104,"comment":47,"blank":38},"file:///home/lars/Projects/music-downloader/src/music_kraken/pages/abstract.py":{"language":"Python","code":428,"comment":44,"blank":103},"file:///home/lars/Projects/music-downloader/src/music_kraken/database/__init__.py":{"language":"Python","code":0,"comment":0,"blank":1},"file:///home/lars/Projects/music-downloader/src/music_kraken/pages/youtube.py":{"language":"Python","code":25,"comment":16,"blank":6},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/metadata.py":{"language":"Python","code":261,"comment":62,"blank":61},"file:///home/lars/Projects/music-downloader/src/music_kraken/database/data_models.py":{"language":"Python","code":122,"comment":24,"blank":52},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/target.py":{"language":"Python","code":58,"comment":11,"blank":21},"file:///home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/search.py":{"language":"Python","code":85,"comment":23,"blank":38},"file:///home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/__init__.py":{"language":"Python","code":4,"comment":0,"blank":2},"file:///home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/page_attributes.py":{"language":"Python","code":24,"comment":1,"blank":9},"file:///home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/download.py":{"language":"Python","code":33,"comment":0,"blank":13},"file:///home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/multiple_options.py":{"language":"Python","code":69,"comment":0,"blank":31},"file:///home/lars/Projects/music-downloader/src/music_kraken/pages/encyclopaedia_metallum.py":{"language":"Python","code":451,"comment":83,"blank":117},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/parents.py":{"language":"Python","code":62,"comment":31,"blank":31},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/collection.py":{"language":"Python","code":91,"comment":31,"blank":39},"file:///home/lars/Projects/music-downloader/src/music_kraken/__init__.py":{"language":"Python","code":86,"comment":12,"blank":28},"file:///home/lars/Projects/music-downloader/src/music_kraken/pages/musify.py":{"language":"Python","code":564,"comment":259,"blank":155},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/lyrics.py":{"language":"Python","code":25,"comment":0,"blank":7},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/formatted_text.py":{"language":"Python","code":50,"comment":10,"blank":19},"file:///home/lars/Projects/music-downloader/src/music_kraken/pages/support_classes/__init__.py":{"language":"Python","code":0,"comment":0,"blank":1},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/object_handeling.py":{"language":"Python","code":19,"comment":0,"blank":6},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/string_processing.py":{"language":"Python","code":10,"comment":5,"blank":7},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/__init__.py":{"language":"Python","code":1,"comment":1,"blank":1},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/shared.py":{"language":"Python","code":75,"comment":27,"blank":17},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/functions.py":{"language":"Python","code":3,"comment":0,"blank":1},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/phonetic_compares.py":{"language":"Python","code":39,"comment":2,"blank":17},"file:///home/lars/Projects/music-downloader/src/music_kraken/pages/support_classes/default_target.py":{"language":"Python","code":55,"comment":0,"blank":15},"file:///home/lars/Projects/music-downloader/src/music_kraken/pages/support_classes/download_result.py":{"language":"Python","code":51,"comment":0,"blank":15},"file:///home/lars/Projects/music-downloader/src/music_kraken/static_files/new_db.sql":{"language":"SQLite","code":72,"comment":0,"blank":10},"file:///home/lars/Projects/music-downloader/src/tests/__init__.py":{"language":"Python","code":0,"comment":0,"blank":1},"file:///home/lars/Projects/music-downloader/src/tests/conftest.py":{"language":"Python","code":3,"comment":1,"blank":2},"file:///home/lars/Projects/music-downloader/src/tests/test_objects.py":{"language":"Python","code":173,"comment":15,"blank":51},"file:///home/lars/Projects/music-downloader/src/tests/test_building_objects.py":{"language":"Python","code":81,"comment":1,"blank":13},"file:///home/lars/Projects/music-downloader/src/music_kraken/tagging/__init__.py":{"language":"Python","code":5,"comment":0,"blank":2},"file:///home/lars/Projects/music-downloader/src/music_kraken/tagging/id3.py":{"language":"Python","code":60,"comment":4,"blank":24},"file:///home/lars/Projects/music-downloader/src/music_kraken/__main__.py":{"language":"Python","code":3,"comment":0,"blank":3},"file:///home/lars/Projects/music-downloader/src/tests/test_download.py":{"language":"Python","code":30,"comment":1,"blank":12},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/metadata_search.py":{"language":"Python","code":253,"comment":40,"blank":72},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/__init__.py":{"language":"Python","code":6,"comment":0,"blank":2},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/__init__.py":{"language":"Python","code":0,"comment":0,"blank":3},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/metadata_fetch.py":{"language":"Python","code":257,"comment":24,"blank":65},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/fetch_audio.py":{"language":"Python","code":75,"comment":12,"blank":20},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/sources/__init__.py":{"language":"Python","code":3,"comment":0,"blank":2},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/musify.py":{"language":"Python","code":136,"comment":9,"blank":37},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/sources/musicbrainz.py":{"language":"Python","code":42,"comment":6,"blank":12},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/youtube.py":{"language":"Python","code":71,"comment":4,"blank":24},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/local_files.py":{"language":"Python","code":40,"comment":0,"blank":18},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/fetch_source.py":{"language":"Python","code":54,"comment":1,"blank":16},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/__init__.py":{"language":"Python","code":0,"comment":0,"blank":1},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/source.py":{"language":"Python","code":11,"comment":5,"blank":8},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/genius.py":{"language":"Python","code":115,"comment":16,"blank":42},"file:///home/lars/Projects/music-downloader/src/music_kraken_gtk.py":{"language":"Python","code":3,"comment":0,"blank":2},"file:///home/lars/Projects/music-downloader/src/musify_search.py":{"language":"Python","code":38,"comment":0,"blank":14}} \ No newline at end of file diff --git a/.VSCodeCounter/2023-04-05_09-31-17/results.md b/.VSCodeCounter/2023-04-05_09-31-17/results.md deleted file mode 100644 index 6b0d2b2..0000000 --- a/.VSCodeCounter/2023-04-05_09-31-17/results.md +++ /dev/null @@ -1,41 +0,0 @@ -# Summary - -Date : 2023-04-05 09:31:17 - -Directory /home/lars/Projects/music-downloader/src - -Total : 66 files, 5256 codes, 1000 comments, 1543 blanks, all 7799 lines - -Summary / [Details](details.md) / [Diff Summary](diff.md) / [Diff Details](diff-details.md) - -## Languages -| language | files | code | comment | blank | total | -| :--- | ---: | ---: | ---: | ---: | ---: | -| Python | 64 | 5,049 | 1,000 | 1,523 | 7,572 | -| SQLite | 2 | 207 | 0 | 20 | 227 | - -## Directories -| path | files | code | comment | blank | total | -| :--- | ---: | ---: | ---: | ---: | ---: | -| . | 66 | 5,256 | 1,000 | 1,543 | 7,799 | -| . (Files) | 7 | 169 | 0 | 55 | 224 | -| music_kraken | 54 | 4,800 | 982 | 1,409 | 7,191 | -| music_kraken (Files) | 2 | 89 | 12 | 31 | 132 | -| music_kraken/database | 3 | 226 | 71 | 91 | 388 | -| music_kraken/not_used_anymore | 14 | 1,063 | 117 | 322 | 1,502 | -| music_kraken/not_used_anymore (Files) | 3 | 129 | 13 | 39 | 181 | -| music_kraken/not_used_anymore/metadata | 5 | 561 | 70 | 153 | 784 | -| music_kraken/not_used_anymore/metadata (Files) | 3 | 516 | 64 | 139 | 719 | -| music_kraken/not_used_anymore/metadata/sources | 2 | 45 | 6 | 14 | 65 | -| music_kraken/not_used_anymore/sources | 6 | 373 | 34 | 130 | 537 | -| music_kraken/objects | 12 | 1,208 | 317 | 360 | 1,885 | -| music_kraken/pages | 13 | 1,795 | 426 | 510 | 2,731 | -| music_kraken/pages (Files) | 5 | 1,474 | 402 | 386 | 2,262 | -| music_kraken/pages/download_center | 5 | 215 | 24 | 93 | 332 | -| music_kraken/pages/support_classes | 3 | 106 | 0 | 31 | 137 | -| music_kraken/static_files | 2 | 207 | 0 | 20 | 227 | -| music_kraken/tagging | 2 | 65 | 4 | 26 | 95 | -| music_kraken/utils | 6 | 147 | 35 | 49 | 231 | -| tests | 5 | 287 | 18 | 79 | 384 | - -Summary / [Details](details.md) / [Diff Summary](diff.md) / [Diff Details](diff-details.md) \ No newline at end of file diff --git a/.VSCodeCounter/2023-04-05_09-31-17/results.txt b/.VSCodeCounter/2023-04-05_09-31-17/results.txt deleted file mode 100644 index e7657dc..0000000 --- a/.VSCodeCounter/2023-04-05_09-31-17/results.txt +++ /dev/null @@ -1,110 +0,0 @@ -Date : 2023-04-05 09:31:17 -Directory : /home/lars/Projects/music-downloader/src -Total : 66 files, 5256 codes, 1000 comments, 1543 blanks, all 7799 lines - -Languages -+----------+------------+------------+------------+------------+------------+ -| language | files | code | comment | blank | total | -+----------+------------+------------+------------+------------+------------+ -| Python | 64 | 5,049 | 1,000 | 1,523 | 7,572 | -| SQLite | 2 | 207 | 0 | 20 | 227 | -+----------+------------+------------+------------+------------+------------+ - -Directories -+--------------------------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ -| path | files | code | comment | blank | total | -+--------------------------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ -| . | 66 | 5,256 | 1,000 | 1,543 | 7,799 | -| . (Files) | 7 | 169 | 0 | 55 | 224 | -| music_kraken | 54 | 4,800 | 982 | 1,409 | 7,191 | -| music_kraken (Files) | 2 | 89 | 12 | 31 | 132 | -| music_kraken/database | 3 | 226 | 71 | 91 | 388 | -| music_kraken/not_used_anymore | 14 | 1,063 | 117 | 322 | 1,502 | -| music_kraken/not_used_anymore (Files) | 3 | 129 | 13 | 39 | 181 | -| music_kraken/not_used_anymore/metadata | 5 | 561 | 70 | 153 | 784 | -| music_kraken/not_used_anymore/metadata (Files) | 3 | 516 | 64 | 139 | 719 | -| music_kraken/not_used_anymore/metadata/sources | 2 | 45 | 6 | 14 | 65 | -| music_kraken/not_used_anymore/sources | 6 | 373 | 34 | 130 | 537 | -| music_kraken/objects | 12 | 1,208 | 317 | 360 | 1,885 | -| music_kraken/pages | 13 | 1,795 | 426 | 510 | 2,731 | -| music_kraken/pages (Files) | 5 | 1,474 | 402 | 386 | 2,262 | -| music_kraken/pages/download_center | 5 | 215 | 24 | 93 | 332 | -| music_kraken/pages/support_classes | 3 | 106 | 0 | 31 | 137 | -| music_kraken/static_files | 2 | 207 | 0 | 20 | 227 | -| music_kraken/tagging | 2 | 65 | 4 | 26 | 95 | -| music_kraken/utils | 6 | 147 | 35 | 49 | 231 | -| tests | 5 | 287 | 18 | 79 | 384 | -+--------------------------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ - -Files -+--------------------------------------------------------------------------------------------------------+----------+------------+------------+------------+------------+ -| filename | language | code | comment | blank | total | -+--------------------------------------------------------------------------------------------------------+----------+------------+------------+------------+------------+ -| /home/lars/Projects/music-downloader/src/__init__.py | Python | 0 | 0 | 1 | 1 | -| /home/lars/Projects/music-downloader/src/actual_donwload.py | Python | 37 | 0 | 17 | 54 | -| /home/lars/Projects/music-downloader/src/create_custom_objects.py | Python | 58 | 0 | 6 | 64 | -| /home/lars/Projects/music-downloader/src/metal_archives.py | Python | 30 | 0 | 12 | 42 | -| /home/lars/Projects/music-downloader/src/music_kraken/__init__.py | Python | 86 | 12 | 28 | 126 | -| /home/lars/Projects/music-downloader/src/music_kraken/__main__.py | Python | 3 | 0 | 3 | 6 | -| /home/lars/Projects/music-downloader/src/music_kraken/database/__init__.py | Python | 0 | 0 | 1 | 1 | -| /home/lars/Projects/music-downloader/src/music_kraken/database/data_models.py | Python | 122 | 24 | 52 | 198 | -| /home/lars/Projects/music-downloader/src/music_kraken/database/database.py | Python | 104 | 47 | 38 | 189 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/__init__.py | Python | 0 | 0 | 3 | 3 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/fetch_audio.py | Python | 75 | 12 | 20 | 107 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/fetch_source.py | Python | 54 | 1 | 16 | 71 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/__init__.py | Python | 6 | 0 | 2 | 8 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/metadata_fetch.py | Python | 257 | 24 | 65 | 346 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/metadata_search.py | Python | 253 | 40 | 72 | 365 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/sources/__init__.py | Python | 3 | 0 | 2 | 5 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/sources/musicbrainz.py | Python | 42 | 6 | 12 | 60 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/__init__.py | Python | 0 | 0 | 1 | 1 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/genius.py | Python | 115 | 16 | 42 | 173 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/local_files.py | Python | 40 | 0 | 18 | 58 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/musify.py | Python | 136 | 9 | 37 | 182 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/source.py | Python | 11 | 5 | 8 | 24 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/youtube.py | Python | 71 | 4 | 24 | 99 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/__init__.py | Python | 29 | 0 | 8 | 37 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/album.py | Python | 16 | 6 | 5 | 27 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/cache.py | Python | 37 | 56 | 18 | 111 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/collection.py | Python | 91 | 31 | 39 | 161 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/formatted_text.py | Python | 50 | 10 | 19 | 79 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/lyrics.py | Python | 25 | 0 | 7 | 32 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/metadata.py | Python | 261 | 62 | 61 | 384 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/option.py | Python | 23 | 0 | 11 | 34 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/parents.py | Python | 62 | 31 | 31 | 124 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/song.py | Python | 436 | 93 | 99 | 628 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/source.py | Python | 120 | 17 | 41 | 178 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/target.py | Python | 58 | 11 | 21 | 90 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/__init__.py | Python | 6 | 0 | 5 | 11 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/abstract.py | Python | 428 | 44 | 103 | 575 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/__init__.py | Python | 4 | 0 | 2 | 6 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/download.py | Python | 33 | 0 | 13 | 46 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/multiple_options.py | Python | 69 | 0 | 31 | 100 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/page_attributes.py | Python | 24 | 1 | 9 | 34 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/search.py | Python | 85 | 23 | 38 | 146 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/encyclopaedia_metallum.py | Python | 451 | 83 | 117 | 651 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/musify.py | Python | 564 | 259 | 155 | 978 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/support_classes/__init__.py | Python | 0 | 0 | 1 | 1 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/support_classes/default_target.py | Python | 55 | 0 | 15 | 70 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/support_classes/download_result.py | Python | 51 | 0 | 15 | 66 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/youtube.py | Python | 25 | 16 | 6 | 47 | -| /home/lars/Projects/music-downloader/src/music_kraken/static_files/new_db.sql | SQLite | 72 | 0 | 10 | 82 | -| /home/lars/Projects/music-downloader/src/music_kraken/static_files/temp_database_structure.sql | SQLite | 135 | 0 | 10 | 145 | -| /home/lars/Projects/music-downloader/src/music_kraken/tagging/__init__.py | Python | 5 | 0 | 2 | 7 | -| /home/lars/Projects/music-downloader/src/music_kraken/tagging/id3.py | Python | 60 | 4 | 24 | 88 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/__init__.py | Python | 1 | 1 | 1 | 3 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/functions.py | Python | 3 | 0 | 1 | 4 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/object_handeling.py | Python | 19 | 0 | 6 | 25 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/phonetic_compares.py | Python | 39 | 2 | 17 | 58 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/shared.py | Python | 75 | 27 | 17 | 119 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/string_processing.py | Python | 10 | 5 | 7 | 22 | -| /home/lars/Projects/music-downloader/src/music_kraken_cli.py | Python | 3 | 0 | 3 | 6 | -| /home/lars/Projects/music-downloader/src/music_kraken_gtk.py | Python | 3 | 0 | 2 | 5 | -| /home/lars/Projects/music-downloader/src/musify_search.py | Python | 38 | 0 | 14 | 52 | -| /home/lars/Projects/music-downloader/src/tests/__init__.py | Python | 0 | 0 | 1 | 1 | -| /home/lars/Projects/music-downloader/src/tests/conftest.py | Python | 3 | 1 | 2 | 6 | -| /home/lars/Projects/music-downloader/src/tests/test_building_objects.py | Python | 81 | 1 | 13 | 95 | -| /home/lars/Projects/music-downloader/src/tests/test_download.py | Python | 30 | 1 | 12 | 43 | -| /home/lars/Projects/music-downloader/src/tests/test_objects.py | Python | 173 | 15 | 51 | 239 | -| Total | | 5,256 | 1,000 | 1,543 | 7,799 | -+--------------------------------------------------------------------------------------------------------+----------+------------+------------+------------+------------+ \ No newline at end of file diff --git a/.VSCodeCounter/2023-05-24_11-17-57/details.md b/.VSCodeCounter/2023-05-24_11-17-57/details.md deleted file mode 100644 index 3f7aca9..0000000 --- a/.VSCodeCounter/2023-05-24_11-17-57/details.md +++ /dev/null @@ -1,104 +0,0 @@ -# Details - -Date : 2023-05-24 11:17:57 - -Directory /home/lars/Projects/music-downloader/src - -Total : 89 files, 6536 codes, 1157 comments, 1980 blanks, all 9673 lines - -[Summary](results.md) / Details / [Diff Summary](diff.md) / [Diff Details](diff-details.md) - -## Files -| filename | language | code | comment | blank | total | -| :--- | :--- | ---: | ---: | ---: | ---: | -| [src/__init__.py](/src/__init__.py) | Python | 0 | 0 | 1 | 1 | -| [src/actual_donwload.py](/src/actual_donwload.py) | Python | 43 | 2 | 18 | 63 | -| [src/create_custom_objects.py](/src/create_custom_objects.py) | Python | 58 | 0 | 6 | 64 | -| [src/metal_archives.py](/src/metal_archives.py) | Python | 30 | 0 | 12 | 42 | -| [src/music_kraken/__init__.py](/src/music_kraken/__init__.py) | Python | 189 | 21 | 60 | 270 | -| [src/music_kraken/__main__.py](/src/music_kraken/__main__.py) | Python | 88 | 3 | 21 | 112 | -| [src/music_kraken/audio/__init__.py](/src/music_kraken/audio/__init__.py) | Python | 7 | 0 | 3 | 10 | -| [src/music_kraken/audio/codec.py](/src/music_kraken/audio/codec.py) | Python | 25 | 0 | 8 | 33 | -| [src/music_kraken/audio/metadata.py](/src/music_kraken/audio/metadata.py) | Python | 60 | 4 | 24 | 88 | -| [src/music_kraken/connection/__init__.py](/src/music_kraken/connection/__init__.py) | Python | 1 | 0 | 1 | 2 | -| [src/music_kraken/connection/connection.py](/src/music_kraken/connection/connection.py) | Python | 162 | 1 | 30 | 193 | -| [src/music_kraken/connection/rotating.py](/src/music_kraken/connection/rotating.py) | Python | 27 | 3 | 14 | 44 | -| [src/music_kraken/database/__init__.py](/src/music_kraken/database/__init__.py) | Python | 0 | 0 | 1 | 1 | -| [src/music_kraken/database/data_models.py](/src/music_kraken/database/data_models.py) | Python | 122 | 24 | 52 | 198 | -| [src/music_kraken/database/database.py](/src/music_kraken/database/database.py) | Python | 104 | 47 | 38 | 189 | -| [src/music_kraken/download/__init__.py](/src/music_kraken/download/__init__.py) | Python | 2 | 0 | 1 | 3 | -| [src/music_kraken/download/download.py](/src/music_kraken/download/download.py) | Python | 35 | 0 | 14 | 49 | -| [src/music_kraken/download/multiple_options.py](/src/music_kraken/download/multiple_options.py) | Python | 69 | 0 | 31 | 100 | -| [src/music_kraken/download/page_attributes.py](/src/music_kraken/download/page_attributes.py) | Python | 21 | 1 | 10 | 32 | -| [src/music_kraken/download/search.py](/src/music_kraken/download/search.py) | Python | 130 | 24 | 56 | 210 | -| [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 | -| [src/music_kraken/not_used_anymore/fetch_source.py](/src/music_kraken/not_used_anymore/fetch_source.py) | Python | 54 | 1 | 16 | 71 | -| [src/music_kraken/not_used_anymore/metadata/__init__.py](/src/music_kraken/not_used_anymore/metadata/__init__.py) | Python | 6 | 0 | 2 | 8 | -| [src/music_kraken/not_used_anymore/metadata/metadata_fetch.py](/src/music_kraken/not_used_anymore/metadata/metadata_fetch.py) | Python | 257 | 24 | 65 | 346 | -| [src/music_kraken/not_used_anymore/metadata/metadata_search.py](/src/music_kraken/not_used_anymore/metadata/metadata_search.py) | Python | 253 | 40 | 72 | 365 | -| [src/music_kraken/not_used_anymore/metadata/sources/__init__.py](/src/music_kraken/not_used_anymore/metadata/sources/__init__.py) | Python | 3 | 0 | 2 | 5 | -| [src/music_kraken/not_used_anymore/metadata/sources/musicbrainz.py](/src/music_kraken/not_used_anymore/metadata/sources/musicbrainz.py) | Python | 42 | 6 | 12 | 60 | -| [src/music_kraken/not_used_anymore/sources/__init__.py](/src/music_kraken/not_used_anymore/sources/__init__.py) | Python | 0 | 0 | 1 | 1 | -| [src/music_kraken/not_used_anymore/sources/genius.py](/src/music_kraken/not_used_anymore/sources/genius.py) | Python | 115 | 16 | 42 | 173 | -| [src/music_kraken/not_used_anymore/sources/local_files.py](/src/music_kraken/not_used_anymore/sources/local_files.py) | Python | 40 | 0 | 18 | 58 | -| [src/music_kraken/not_used_anymore/sources/musify.py](/src/music_kraken/not_used_anymore/sources/musify.py) | Python | 136 | 9 | 37 | 182 | -| [src/music_kraken/not_used_anymore/sources/source.py](/src/music_kraken/not_used_anymore/sources/source.py) | Python | 11 | 5 | 8 | 24 | -| [src/music_kraken/not_used_anymore/sources/youtube.py](/src/music_kraken/not_used_anymore/sources/youtube.py) | Python | 71 | 4 | 24 | 99 | -| [src/music_kraken/objects/__init__.py](/src/music_kraken/objects/__init__.py) | Python | 14 | 0 | 5 | 19 | -| [src/music_kraken/objects/cache.py](/src/music_kraken/objects/cache.py) | Python | 37 | 56 | 18 | 111 | -| [src/music_kraken/objects/collection.py](/src/music_kraken/objects/collection.py) | Python | 91 | 31 | 39 | 161 | -| [src/music_kraken/objects/formatted_text.py](/src/music_kraken/objects/formatted_text.py) | Python | 50 | 10 | 19 | 79 | -| [src/music_kraken/objects/lyrics.py](/src/music_kraken/objects/lyrics.py) | Python | 25 | 0 | 7 | 32 | -| [src/music_kraken/objects/metadata.py](/src/music_kraken/objects/metadata.py) | Python | 261 | 62 | 61 | 384 | -| [src/music_kraken/objects/option.py](/src/music_kraken/objects/option.py) | Python | 28 | 0 | 13 | 41 | -| [src/music_kraken/objects/parents.py](/src/music_kraken/objects/parents.py) | Python | 65 | 33 | 33 | 131 | -| [src/music_kraken/objects/song.py](/src/music_kraken/objects/song.py) | Python | 473 | 113 | 115 | 701 | -| [src/music_kraken/objects/source.py](/src/music_kraken/objects/source.py) | Python | 90 | 16 | 32 | 138 | -| [src/music_kraken/objects/target.py](/src/music_kraken/objects/target.py) | Python | 63 | 15 | 23 | 101 | -| [src/music_kraken/pages/__init__.py](/src/music_kraken/pages/__init__.py) | Python | 3 | 0 | 1 | 4 | -| [src/music_kraken/pages/abstract.py](/src/music_kraken/pages/abstract.py) | Python | 357 | 34 | 103 | 494 | -| [src/music_kraken/pages/encyclopaedia_metallum.py](/src/music_kraken/pages/encyclopaedia_metallum.py) | Python | 432 | 90 | 127 | 649 | -| [src/music_kraken/pages/musify.py](/src/music_kraken/pages/musify.py) | Python | 629 | 289 | 187 | 1,105 | -| [src/music_kraken/pages/preset.py](/src/music_kraken/pages/preset.py) | Python | 43 | 1 | 16 | 60 | -| [src/music_kraken/pages/youtube.py](/src/music_kraken/pages/youtube.py) | Python | 25 | 16 | 6 | 47 | -| [src/music_kraken/static_files/new_db.sql](/src/music_kraken/static_files/new_db.sql) | SQLite | 72 | 0 | 10 | 82 | -| [src/music_kraken/static_files/temp_database_structure.sql](/src/music_kraken/static_files/temp_database_structure.sql) | SQLite | 135 | 0 | 10 | 145 | -| [src/music_kraken/utils/__init__.py](/src/music_kraken/utils/__init__.py) | Python | 2 | 1 | 2 | 5 | -| [src/music_kraken/utils/config/__init__.py](/src/music_kraken/utils/config/__init__.py) | Python | 7 | 0 | 4 | 11 | -| [src/music_kraken/utils/config/audio.py](/src/music_kraken/utils/config/audio.py) | Python | 152 | 15 | 28 | 195 | -| [src/music_kraken/utils/config/base_classes.py](/src/music_kraken/utils/config/base_classes.py) | Python | 136 | 35 | 61 | 232 | -| [src/music_kraken/utils/config/config.py](/src/music_kraken/utils/config/config.py) | Python | 92 | 16 | 30 | 138 | -| [src/music_kraken/utils/config/connection.py](/src/music_kraken/utils/config/connection.py) | Python | 81 | 2 | 15 | 98 | -| [src/music_kraken/utils/config/logging.py](/src/music_kraken/utils/config/logging.py) | Python | 104 | 4 | 17 | 125 | -| [src/music_kraken/utils/config/misc.py](/src/music_kraken/utils/config/misc.py) | Python | 40 | 0 | 9 | 49 | -| [src/music_kraken/utils/config/paths.py](/src/music_kraken/utils/config/paths.py) | Python | 40 | 0 | 13 | 53 | -| [src/music_kraken/utils/enums/__init__.py](/src/music_kraken/utils/enums/__init__.py) | Python | 0 | 0 | 1 | 1 | -| [src/music_kraken/utils/enums/album.py](/src/music_kraken/utils/enums/album.py) | Python | 16 | 6 | 5 | 27 | -| [src/music_kraken/utils/enums/source.py](/src/music_kraken/utils/enums/source.py) | Python | 40 | 1 | 8 | 49 | -| [src/music_kraken/utils/exception/__init__.py](/src/music_kraken/utils/exception/__init__.py) | Python | 1 | 0 | 1 | 2 | -| [src/music_kraken/utils/exception/config.py](/src/music_kraken/utils/exception/config.py) | Python | 14 | 8 | 7 | 29 | -| [src/music_kraken/utils/functions.py](/src/music_kraken/utils/functions.py) | Python | 3 | 0 | 1 | 4 | -| [src/music_kraken/utils/object_handeling.py](/src/music_kraken/utils/object_handeling.py) | Python | 19 | 0 | 6 | 25 | -| [src/music_kraken/utils/path_manager/__init__.py](/src/music_kraken/utils/path_manager/__init__.py) | Python | 2 | 0 | 2 | 4 | -| [src/music_kraken/utils/path_manager/config_directory.py](/src/music_kraken/utils/path_manager/config_directory.py) | Python | 4 | 0 | 4 | 8 | -| [src/music_kraken/utils/path_manager/locations.py](/src/music_kraken/utils/path_manager/locations.py) | Python | 16 | 0 | 9 | 25 | -| [src/music_kraken/utils/path_manager/music_directory.py](/src/music_kraken/utils/path_manager/music_directory.py) | Python | 36 | 9 | 14 | 59 | -| [src/music_kraken/utils/phonetic_compares.py](/src/music_kraken/utils/phonetic_compares.py) | Python | 39 | 2 | 17 | 58 | -| [src/music_kraken/utils/regex.py](/src/music_kraken/utils/regex.py) | Python | 1 | 0 | 2 | 3 | -| [src/music_kraken/utils/shared.py](/src/music_kraken/utils/shared.py) | Python | 63 | 22 | 21 | 106 | -| [src/music_kraken/utils/string_processing.py](/src/music_kraken/utils/string_processing.py) | Python | 16 | 5 | 11 | 32 | -| [src/music_kraken/utils/support_classes/__init__.py](/src/music_kraken/utils/support_classes/__init__.py) | Python | 3 | 0 | 1 | 4 | -| [src/music_kraken/utils/support_classes/default_target.py](/src/music_kraken/utils/support_classes/default_target.py) | Python | 56 | 0 | 15 | 71 | -| [src/music_kraken/utils/support_classes/download_result.py](/src/music_kraken/utils/support_classes/download_result.py) | Python | 69 | 0 | 21 | 90 | -| [src/music_kraken/utils/support_classes/query.py](/src/music_kraken/utils/support_classes/query.py) | Python | 24 | 0 | 9 | 33 | -| [src/music_kraken_cli.py](/src/music_kraken_cli.py) | Python | 3 | 0 | 3 | 6 | -| [src/music_kraken_gtk.py](/src/music_kraken_gtk.py) | Python | 3 | 0 | 2 | 5 | -| [src/musify_search.py](/src/musify_search.py) | Python | 38 | 0 | 14 | 52 | -| [src/tests/__init__.py](/src/tests/__init__.py) | Python | 0 | 0 | 1 | 1 | -| [src/tests/conftest.py](/src/tests/conftest.py) | Python | 3 | 1 | 2 | 6 | -| [src/tests/test_building_objects.py](/src/tests/test_building_objects.py) | Python | 81 | 1 | 13 | 95 | -| [src/tests/test_download.py](/src/tests/test_download.py) | Python | 30 | 1 | 12 | 43 | -| [src/tests/test_objects.py](/src/tests/test_objects.py) | Python | 173 | 15 | 51 | 239 | - -[Summary](results.md) / Details / [Diff Summary](diff.md) / [Diff Details](diff-details.md) \ No newline at end of file diff --git a/.VSCodeCounter/2023-05-24_11-17-57/diff-details.md b/.VSCodeCounter/2023-05-24_11-17-57/diff-details.md deleted file mode 100644 index 279a11c..0000000 --- a/.VSCodeCounter/2023-05-24_11-17-57/diff-details.md +++ /dev/null @@ -1,76 +0,0 @@ -# Diff Details - -Date : 2023-05-24 11:17:57 - -Directory /home/lars/Projects/music-downloader/src - -Total : 61 files, 1280 codes, 157 comments, 437 blanks, all 1874 lines - -[Summary](results.md) / [Details](details.md) / [Diff Summary](diff.md) / Diff Details - -## Files -| filename | language | code | comment | blank | total | -| :--- | :--- | ---: | ---: | ---: | ---: | -| [src/actual_donwload.py](/src/actual_donwload.py) | Python | 6 | 2 | 1 | 9 | -| [src/music_kraken/__init__.py](/src/music_kraken/__init__.py) | Python | 103 | 9 | 32 | 144 | -| [src/music_kraken/__main__.py](/src/music_kraken/__main__.py) | Python | 85 | 3 | 18 | 106 | -| [src/music_kraken/audio/__init__.py](/src/music_kraken/audio/__init__.py) | Python | 7 | 0 | 3 | 10 | -| [src/music_kraken/audio/codec.py](/src/music_kraken/audio/codec.py) | Python | 25 | 0 | 8 | 33 | -| [src/music_kraken/audio/metadata.py](/src/music_kraken/audio/metadata.py) | Python | 60 | 4 | 24 | 88 | -| [src/music_kraken/connection/__init__.py](/src/music_kraken/connection/__init__.py) | Python | 1 | 0 | 1 | 2 | -| [src/music_kraken/connection/connection.py](/src/music_kraken/connection/connection.py) | Python | 162 | 1 | 30 | 193 | -| [src/music_kraken/connection/rotating.py](/src/music_kraken/connection/rotating.py) | Python | 27 | 3 | 14 | 44 | -| [src/music_kraken/download/__init__.py](/src/music_kraken/download/__init__.py) | Python | 2 | 0 | 1 | 3 | -| [src/music_kraken/download/download.py](/src/music_kraken/download/download.py) | Python | 35 | 0 | 14 | 49 | -| [src/music_kraken/download/multiple_options.py](/src/music_kraken/download/multiple_options.py) | Python | 69 | 0 | 31 | 100 | -| [src/music_kraken/download/page_attributes.py](/src/music_kraken/download/page_attributes.py) | Python | 21 | 1 | 10 | 32 | -| [src/music_kraken/download/search.py](/src/music_kraken/download/search.py) | Python | 130 | 24 | 56 | 210 | -| [src/music_kraken/objects/__init__.py](/src/music_kraken/objects/__init__.py) | Python | -15 | 0 | -3 | -18 | -| [src/music_kraken/objects/album.py](/src/music_kraken/objects/album.py) | Python | -16 | -6 | -5 | -27 | -| [src/music_kraken/objects/option.py](/src/music_kraken/objects/option.py) | Python | 5 | 0 | 2 | 7 | -| [src/music_kraken/objects/parents.py](/src/music_kraken/objects/parents.py) | Python | 3 | 2 | 2 | 7 | -| [src/music_kraken/objects/song.py](/src/music_kraken/objects/song.py) | Python | 37 | 20 | 16 | 73 | -| [src/music_kraken/objects/source.py](/src/music_kraken/objects/source.py) | Python | -30 | -1 | -9 | -40 | -| [src/music_kraken/objects/target.py](/src/music_kraken/objects/target.py) | Python | 5 | 4 | 2 | 11 | -| [src/music_kraken/pages/__init__.py](/src/music_kraken/pages/__init__.py) | Python | -3 | 0 | -4 | -7 | -| [src/music_kraken/pages/abstract.py](/src/music_kraken/pages/abstract.py) | Python | -71 | -10 | 0 | -81 | -| [src/music_kraken/pages/download_center/__init__.py](/src/music_kraken/pages/download_center/__init__.py) | Python | -4 | 0 | -2 | -6 | -| [src/music_kraken/pages/download_center/download.py](/src/music_kraken/pages/download_center/download.py) | Python | -33 | 0 | -13 | -46 | -| [src/music_kraken/pages/download_center/multiple_options.py](/src/music_kraken/pages/download_center/multiple_options.py) | Python | -69 | 0 | -31 | -100 | -| [src/music_kraken/pages/download_center/page_attributes.py](/src/music_kraken/pages/download_center/page_attributes.py) | Python | -24 | -1 | -9 | -34 | -| [src/music_kraken/pages/download_center/search.py](/src/music_kraken/pages/download_center/search.py) | Python | -85 | -23 | -38 | -146 | -| [src/music_kraken/pages/encyclopaedia_metallum.py](/src/music_kraken/pages/encyclopaedia_metallum.py) | Python | -19 | 7 | 10 | -2 | -| [src/music_kraken/pages/musify.py](/src/music_kraken/pages/musify.py) | Python | 65 | 30 | 32 | 127 | -| [src/music_kraken/pages/preset.py](/src/music_kraken/pages/preset.py) | Python | 43 | 1 | 16 | 60 | -| [src/music_kraken/pages/support_classes/__init__.py](/src/music_kraken/pages/support_classes/__init__.py) | Python | 0 | 0 | -1 | -1 | -| [src/music_kraken/pages/support_classes/default_target.py](/src/music_kraken/pages/support_classes/default_target.py) | Python | -55 | 0 | -15 | -70 | -| [src/music_kraken/pages/support_classes/download_result.py](/src/music_kraken/pages/support_classes/download_result.py) | Python | -51 | 0 | -15 | -66 | -| [src/music_kraken/tagging/__init__.py](/src/music_kraken/tagging/__init__.py) | Python | -5 | 0 | -2 | -7 | -| [src/music_kraken/tagging/id3.py](/src/music_kraken/tagging/id3.py) | Python | -60 | -4 | -24 | -88 | -| [src/music_kraken/utils/__init__.py](/src/music_kraken/utils/__init__.py) | Python | 1 | 0 | 1 | 2 | -| [src/music_kraken/utils/config/__init__.py](/src/music_kraken/utils/config/__init__.py) | Python | 7 | 0 | 4 | 11 | -| [src/music_kraken/utils/config/audio.py](/src/music_kraken/utils/config/audio.py) | Python | 152 | 15 | 28 | 195 | -| [src/music_kraken/utils/config/base_classes.py](/src/music_kraken/utils/config/base_classes.py) | Python | 136 | 35 | 61 | 232 | -| [src/music_kraken/utils/config/config.py](/src/music_kraken/utils/config/config.py) | Python | 92 | 16 | 30 | 138 | -| [src/music_kraken/utils/config/connection.py](/src/music_kraken/utils/config/connection.py) | Python | 81 | 2 | 15 | 98 | -| [src/music_kraken/utils/config/logging.py](/src/music_kraken/utils/config/logging.py) | Python | 104 | 4 | 17 | 125 | -| [src/music_kraken/utils/config/misc.py](/src/music_kraken/utils/config/misc.py) | Python | 40 | 0 | 9 | 49 | -| [src/music_kraken/utils/config/paths.py](/src/music_kraken/utils/config/paths.py) | Python | 40 | 0 | 13 | 53 | -| [src/music_kraken/utils/enums/__init__.py](/src/music_kraken/utils/enums/__init__.py) | Python | 0 | 0 | 1 | 1 | -| [src/music_kraken/utils/enums/album.py](/src/music_kraken/utils/enums/album.py) | Python | 16 | 6 | 5 | 27 | -| [src/music_kraken/utils/enums/source.py](/src/music_kraken/utils/enums/source.py) | Python | 40 | 1 | 8 | 49 | -| [src/music_kraken/utils/exception/__init__.py](/src/music_kraken/utils/exception/__init__.py) | Python | 1 | 0 | 1 | 2 | -| [src/music_kraken/utils/exception/config.py](/src/music_kraken/utils/exception/config.py) | Python | 14 | 8 | 7 | 29 | -| [src/music_kraken/utils/path_manager/__init__.py](/src/music_kraken/utils/path_manager/__init__.py) | Python | 2 | 0 | 2 | 4 | -| [src/music_kraken/utils/path_manager/config_directory.py](/src/music_kraken/utils/path_manager/config_directory.py) | Python | 4 | 0 | 4 | 8 | -| [src/music_kraken/utils/path_manager/locations.py](/src/music_kraken/utils/path_manager/locations.py) | Python | 16 | 0 | 9 | 25 | -| [src/music_kraken/utils/path_manager/music_directory.py](/src/music_kraken/utils/path_manager/music_directory.py) | Python | 36 | 9 | 14 | 59 | -| [src/music_kraken/utils/regex.py](/src/music_kraken/utils/regex.py) | Python | 1 | 0 | 2 | 3 | -| [src/music_kraken/utils/shared.py](/src/music_kraken/utils/shared.py) | Python | -12 | -5 | 4 | -13 | -| [src/music_kraken/utils/string_processing.py](/src/music_kraken/utils/string_processing.py) | Python | 6 | 0 | 4 | 10 | -| [src/music_kraken/utils/support_classes/__init__.py](/src/music_kraken/utils/support_classes/__init__.py) | Python | 3 | 0 | 1 | 4 | -| [src/music_kraken/utils/support_classes/default_target.py](/src/music_kraken/utils/support_classes/default_target.py) | Python | 56 | 0 | 15 | 71 | -| [src/music_kraken/utils/support_classes/download_result.py](/src/music_kraken/utils/support_classes/download_result.py) | Python | 69 | 0 | 21 | 90 | -| [src/music_kraken/utils/support_classes/query.py](/src/music_kraken/utils/support_classes/query.py) | Python | 24 | 0 | 9 | 33 | - -[Summary](results.md) / [Details](details.md) / [Diff Summary](diff.md) / Diff Details \ No newline at end of file diff --git a/.VSCodeCounter/2023-05-24_11-17-57/diff.csv b/.VSCodeCounter/2023-05-24_11-17-57/diff.csv deleted file mode 100644 index 34a7e92..0000000 --- a/.VSCodeCounter/2023-05-24_11-17-57/diff.csv +++ /dev/null @@ -1,63 +0,0 @@ -"filename", "language", "Python", "comment", "blank", "total" -"/home/lars/Projects/music-downloader/src/actual_donwload.py", "Python", 6, 2, 1, 9 -"/home/lars/Projects/music-downloader/src/music_kraken/__init__.py", "Python", 103, 9, 32, 144 -"/home/lars/Projects/music-downloader/src/music_kraken/__main__.py", "Python", 85, 3, 18, 106 -"/home/lars/Projects/music-downloader/src/music_kraken/audio/__init__.py", "Python", 7, 0, 3, 10 -"/home/lars/Projects/music-downloader/src/music_kraken/audio/codec.py", "Python", 25, 0, 8, 33 -"/home/lars/Projects/music-downloader/src/music_kraken/audio/metadata.py", "Python", 60, 4, 24, 88 -"/home/lars/Projects/music-downloader/src/music_kraken/connection/__init__.py", "Python", 1, 0, 1, 2 -"/home/lars/Projects/music-downloader/src/music_kraken/connection/connection.py", "Python", 162, 1, 30, 193 -"/home/lars/Projects/music-downloader/src/music_kraken/connection/rotating.py", "Python", 27, 3, 14, 44 -"/home/lars/Projects/music-downloader/src/music_kraken/download/__init__.py", "Python", 2, 0, 1, 3 -"/home/lars/Projects/music-downloader/src/music_kraken/download/download.py", "Python", 35, 0, 14, 49 -"/home/lars/Projects/music-downloader/src/music_kraken/download/multiple_options.py", "Python", 69, 0, 31, 100 -"/home/lars/Projects/music-downloader/src/music_kraken/download/page_attributes.py", "Python", 21, 1, 10, 32 -"/home/lars/Projects/music-downloader/src/music_kraken/download/search.py", "Python", 130, 24, 56, 210 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/__init__.py", "Python", -15, 0, -3, -18 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/album.py", "Python", -16, -6, -5, -27 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/option.py", "Python", 5, 0, 2, 7 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/parents.py", "Python", 3, 2, 2, 7 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/song.py", "Python", 37, 20, 16, 73 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/source.py", "Python", -30, -1, -9, -40 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/target.py", "Python", 5, 4, 2, 11 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/__init__.py", "Python", -3, 0, -4, -7 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/abstract.py", "Python", -71, -10, 0, -81 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/__init__.py", "Python", -4, 0, -2, -6 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/download.py", "Python", -33, 0, -13, -46 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/multiple_options.py", "Python", -69, 0, -31, -100 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/page_attributes.py", "Python", -24, -1, -9, -34 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/search.py", "Python", -85, -23, -38, -146 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/encyclopaedia_metallum.py", "Python", -19, 7, 10, -2 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/musify.py", "Python", 65, 30, 32, 127 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/preset.py", "Python", 43, 1, 16, 60 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/support_classes/__init__.py", "Python", 0, 0, -1, -1 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/support_classes/default_target.py", "Python", -55, 0, -15, -70 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/support_classes/download_result.py", "Python", -51, 0, -15, -66 -"/home/lars/Projects/music-downloader/src/music_kraken/tagging/__init__.py", "Python", -5, 0, -2, -7 -"/home/lars/Projects/music-downloader/src/music_kraken/tagging/id3.py", "Python", -60, -4, -24, -88 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/__init__.py", "Python", 1, 0, 1, 2 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/config/__init__.py", "Python", 7, 0, 4, 11 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/config/audio.py", "Python", 152, 15, 28, 195 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/config/base_classes.py", "Python", 136, 35, 61, 232 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/config/config.py", "Python", 92, 16, 30, 138 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/config/connection.py", "Python", 81, 2, 15, 98 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/config/logging.py", "Python", 104, 4, 17, 125 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/config/misc.py", "Python", 40, 0, 9, 49 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/config/paths.py", "Python", 40, 0, 13, 53 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/enums/__init__.py", "Python", 0, 0, 1, 1 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/enums/album.py", "Python", 16, 6, 5, 27 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/enums/source.py", "Python", 40, 1, 8, 49 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/exception/__init__.py", "Python", 1, 0, 1, 2 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/exception/config.py", "Python", 14, 8, 7, 29 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/path_manager/__init__.py", "Python", 2, 0, 2, 4 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/path_manager/config_directory.py", "Python", 4, 0, 4, 8 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/path_manager/locations.py", "Python", 16, 0, 9, 25 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/path_manager/music_directory.py", "Python", 36, 9, 14, 59 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/regex.py", "Python", 1, 0, 2, 3 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/shared.py", "Python", -12, -5, 4, -13 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/string_processing.py", "Python", 6, 0, 4, 10 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/support_classes/__init__.py", "Python", 3, 0, 1, 4 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/support_classes/default_target.py", "Python", 56, 0, 15, 71 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/support_classes/download_result.py", "Python", 69, 0, 21, 90 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/support_classes/query.py", "Python", 24, 0, 9, 33 -"Total", "-", 1280, 157, 437, 1874 \ No newline at end of file diff --git a/.VSCodeCounter/2023-05-24_11-17-57/diff.md b/.VSCodeCounter/2023-05-24_11-17-57/diff.md deleted file mode 100644 index 64d4b4d..0000000 --- a/.VSCodeCounter/2023-05-24_11-17-57/diff.md +++ /dev/null @@ -1,40 +0,0 @@ -# Diff Summary - -Date : 2023-05-24 11:17:57 - -Directory /home/lars/Projects/music-downloader/src - -Total : 61 files, 1280 codes, 157 comments, 437 blanks, all 1874 lines - -[Summary](results.md) / [Details](details.md) / Diff Summary / [Diff Details](diff-details.md) - -## Languages -| language | files | code | comment | blank | total | -| :--- | ---: | ---: | ---: | ---: | ---: | -| Python | 61 | 1,280 | 157 | 437 | 1,874 | - -## Directories -| path | files | code | comment | blank | total | -| :--- | ---: | ---: | ---: | ---: | ---: | -| . | 61 | 1,280 | 157 | 437 | 1,874 | -| . (Files) | 1 | 6 | 2 | 1 | 9 | -| music_kraken | 60 | 1,274 | 155 | 436 | 1,865 | -| music_kraken (Files) | 2 | 188 | 12 | 50 | 250 | -| music_kraken/audio | 3 | 92 | 4 | 35 | 131 | -| music_kraken/connection | 3 | 190 | 4 | 45 | 239 | -| music_kraken/download | 5 | 257 | 25 | 112 | 394 | -| music_kraken/objects | 7 | -11 | 19 | 5 | 13 | -| music_kraken/pages | 13 | -306 | 4 | -70 | -372 | -| music_kraken/pages (Files) | 5 | 15 | 28 | 54 | 97 | -| music_kraken/pages/download_center | 5 | -215 | -24 | -93 | -332 | -| music_kraken/pages/support_classes | 3 | -106 | 0 | -31 | -137 | -| music_kraken/tagging | 2 | -65 | -4 | -26 | -95 | -| music_kraken/utils | 25 | 929 | 91 | 285 | 1,305 | -| music_kraken/utils (Files) | 4 | -4 | -5 | 11 | 2 | -| music_kraken/utils/config | 8 | 652 | 72 | 177 | 901 | -| music_kraken/utils/enums | 3 | 56 | 7 | 14 | 77 | -| music_kraken/utils/exception | 2 | 15 | 8 | 8 | 31 | -| music_kraken/utils/path_manager | 4 | 58 | 9 | 29 | 96 | -| music_kraken/utils/support_classes | 4 | 152 | 0 | 46 | 198 | - -[Summary](results.md) / [Details](details.md) / Diff Summary / [Diff Details](diff-details.md) \ No newline at end of file diff --git a/.VSCodeCounter/2023-05-24_11-17-57/diff.txt b/.VSCodeCounter/2023-05-24_11-17-57/diff.txt deleted file mode 100644 index cb7308f..0000000 --- a/.VSCodeCounter/2023-05-24_11-17-57/diff.txt +++ /dev/null @@ -1,104 +0,0 @@ -Date : 2023-05-24 11:17:57 -Directory : /home/lars/Projects/music-downloader/src -Total : 61 files, 1280 codes, 157 comments, 437 blanks, all 1874 lines - -Languages -+----------+------------+------------+------------+------------+------------+ -| language | files | code | comment | blank | total | -+----------+------------+------------+------------+------------+------------+ -| Python | 61 | 1,280 | 157 | 437 | 1,874 | -+----------+------------+------------+------------+------------+------------+ - -Directories -+-------------------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ -| path | files | code | comment | blank | total | -+-------------------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ -| . | 61 | 1,280 | 157 | 437 | 1,874 | -| . (Files) | 1 | 6 | 2 | 1 | 9 | -| music_kraken | 60 | 1,274 | 155 | 436 | 1,865 | -| music_kraken (Files) | 2 | 188 | 12 | 50 | 250 | -| music_kraken/audio | 3 | 92 | 4 | 35 | 131 | -| music_kraken/connection | 3 | 190 | 4 | 45 | 239 | -| music_kraken/download | 5 | 257 | 25 | 112 | 394 | -| music_kraken/objects | 7 | -11 | 19 | 5 | 13 | -| music_kraken/pages | 13 | -306 | 4 | -70 | -372 | -| music_kraken/pages (Files) | 5 | 15 | 28 | 54 | 97 | -| music_kraken/pages/download_center | 5 | -215 | -24 | -93 | -332 | -| music_kraken/pages/support_classes | 3 | -106 | 0 | -31 | -137 | -| music_kraken/tagging | 2 | -65 | -4 | -26 | -95 | -| music_kraken/utils | 25 | 929 | 91 | 285 | 1,305 | -| music_kraken/utils (Files) | 4 | -4 | -5 | 11 | 2 | -| music_kraken/utils/config | 8 | 652 | 72 | 177 | 901 | -| music_kraken/utils/enums | 3 | 56 | 7 | 14 | 77 | -| music_kraken/utils/exception | 2 | 15 | 8 | 8 | 31 | -| music_kraken/utils/path_manager | 4 | 58 | 9 | 29 | 96 | -| music_kraken/utils/support_classes | 4 | 152 | 0 | 46 | 198 | -+-------------------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ - -Files -+-------------------------------------------------------------------------------------------------+----------+------------+------------+------------+------------+ -| filename | language | code | comment | blank | total | -+-------------------------------------------------------------------------------------------------+----------+------------+------------+------------+------------+ -| /home/lars/Projects/music-downloader/src/actual_donwload.py | Python | 6 | 2 | 1 | 9 | -| /home/lars/Projects/music-downloader/src/music_kraken/__init__.py | Python | 103 | 9 | 32 | 144 | -| /home/lars/Projects/music-downloader/src/music_kraken/__main__.py | Python | 85 | 3 | 18 | 106 | -| /home/lars/Projects/music-downloader/src/music_kraken/audio/__init__.py | Python | 7 | 0 | 3 | 10 | -| /home/lars/Projects/music-downloader/src/music_kraken/audio/codec.py | Python | 25 | 0 | 8 | 33 | -| /home/lars/Projects/music-downloader/src/music_kraken/audio/metadata.py | Python | 60 | 4 | 24 | 88 | -| /home/lars/Projects/music-downloader/src/music_kraken/connection/__init__.py | Python | 1 | 0 | 1 | 2 | -| /home/lars/Projects/music-downloader/src/music_kraken/connection/connection.py | Python | 162 | 1 | 30 | 193 | -| /home/lars/Projects/music-downloader/src/music_kraken/connection/rotating.py | Python | 27 | 3 | 14 | 44 | -| /home/lars/Projects/music-downloader/src/music_kraken/download/__init__.py | Python | 2 | 0 | 1 | 3 | -| /home/lars/Projects/music-downloader/src/music_kraken/download/download.py | Python | 35 | 0 | 14 | 49 | -| /home/lars/Projects/music-downloader/src/music_kraken/download/multiple_options.py | Python | 69 | 0 | 31 | 100 | -| /home/lars/Projects/music-downloader/src/music_kraken/download/page_attributes.py | Python | 21 | 1 | 10 | 32 | -| /home/lars/Projects/music-downloader/src/music_kraken/download/search.py | Python | 130 | 24 | 56 | 210 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/__init__.py | Python | -15 | 0 | -3 | -18 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/album.py | Python | -16 | -6 | -5 | -27 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/option.py | Python | 5 | 0 | 2 | 7 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/parents.py | Python | 3 | 2 | 2 | 7 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/song.py | Python | 37 | 20 | 16 | 73 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/source.py | Python | -30 | -1 | -9 | -40 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/target.py | Python | 5 | 4 | 2 | 11 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/__init__.py | Python | -3 | 0 | -4 | -7 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/abstract.py | Python | -71 | -10 | 0 | -81 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/__init__.py | Python | -4 | 0 | -2 | -6 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/download.py | Python | -33 | 0 | -13 | -46 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/multiple_options.py | Python | -69 | 0 | -31 | -100 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/page_attributes.py | Python | -24 | -1 | -9 | -34 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/download_center/search.py | Python | -85 | -23 | -38 | -146 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/encyclopaedia_metallum.py | Python | -19 | 7 | 10 | -2 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/musify.py | Python | 65 | 30 | 32 | 127 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/preset.py | Python | 43 | 1 | 16 | 60 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/support_classes/__init__.py | Python | 0 | 0 | -1 | -1 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/support_classes/default_target.py | Python | -55 | 0 | -15 | -70 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/support_classes/download_result.py | Python | -51 | 0 | -15 | -66 | -| /home/lars/Projects/music-downloader/src/music_kraken/tagging/__init__.py | Python | -5 | 0 | -2 | -7 | -| /home/lars/Projects/music-downloader/src/music_kraken/tagging/id3.py | Python | -60 | -4 | -24 | -88 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/__init__.py | Python | 1 | 0 | 1 | 2 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/config/__init__.py | Python | 7 | 0 | 4 | 11 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/config/audio.py | Python | 152 | 15 | 28 | 195 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/config/base_classes.py | Python | 136 | 35 | 61 | 232 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/config/config.py | Python | 92 | 16 | 30 | 138 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/config/connection.py | Python | 81 | 2 | 15 | 98 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/config/logging.py | Python | 104 | 4 | 17 | 125 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/config/misc.py | Python | 40 | 0 | 9 | 49 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/config/paths.py | Python | 40 | 0 | 13 | 53 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/enums/__init__.py | Python | 0 | 0 | 1 | 1 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/enums/album.py | Python | 16 | 6 | 5 | 27 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/enums/source.py | Python | 40 | 1 | 8 | 49 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/exception/__init__.py | Python | 1 | 0 | 1 | 2 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/exception/config.py | Python | 14 | 8 | 7 | 29 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/path_manager/__init__.py | Python | 2 | 0 | 2 | 4 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/path_manager/config_directory.py | Python | 4 | 0 | 4 | 8 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/path_manager/locations.py | Python | 16 | 0 | 9 | 25 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/path_manager/music_directory.py | Python | 36 | 9 | 14 | 59 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/regex.py | Python | 1 | 0 | 2 | 3 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/shared.py | Python | -12 | -5 | 4 | -13 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/string_processing.py | Python | 6 | 0 | 4 | 10 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/support_classes/__init__.py | Python | 3 | 0 | 1 | 4 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/support_classes/default_target.py | Python | 56 | 0 | 15 | 71 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/support_classes/download_result.py | Python | 69 | 0 | 21 | 90 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/support_classes/query.py | Python | 24 | 0 | 9 | 33 | -| Total | | 1,280 | 157 | 437 | 1,874 | -+-------------------------------------------------------------------------------------------------+----------+------------+------------+------------+------------+ \ No newline at end of file diff --git a/.VSCodeCounter/2023-05-24_11-17-57/results.csv b/.VSCodeCounter/2023-05-24_11-17-57/results.csv deleted file mode 100644 index 2bbbd07..0000000 --- a/.VSCodeCounter/2023-05-24_11-17-57/results.csv +++ /dev/null @@ -1,91 +0,0 @@ -"filename", "language", "Python", "SQLite", "comment", "blank", "total" -"/home/lars/Projects/music-downloader/src/__init__.py", "Python", 0, 0, 0, 1, 1 -"/home/lars/Projects/music-downloader/src/actual_donwload.py", "Python", 43, 0, 2, 18, 63 -"/home/lars/Projects/music-downloader/src/create_custom_objects.py", "Python", 58, 0, 0, 6, 64 -"/home/lars/Projects/music-downloader/src/metal_archives.py", "Python", 30, 0, 0, 12, 42 -"/home/lars/Projects/music-downloader/src/music_kraken/__init__.py", "Python", 189, 0, 21, 60, 270 -"/home/lars/Projects/music-downloader/src/music_kraken/__main__.py", "Python", 88, 0, 3, 21, 112 -"/home/lars/Projects/music-downloader/src/music_kraken/audio/__init__.py", "Python", 7, 0, 0, 3, 10 -"/home/lars/Projects/music-downloader/src/music_kraken/audio/codec.py", "Python", 25, 0, 0, 8, 33 -"/home/lars/Projects/music-downloader/src/music_kraken/audio/metadata.py", "Python", 60, 0, 4, 24, 88 -"/home/lars/Projects/music-downloader/src/music_kraken/connection/__init__.py", "Python", 1, 0, 0, 1, 2 -"/home/lars/Projects/music-downloader/src/music_kraken/connection/connection.py", "Python", 162, 0, 1, 30, 193 -"/home/lars/Projects/music-downloader/src/music_kraken/connection/rotating.py", "Python", 27, 0, 3, 14, 44 -"/home/lars/Projects/music-downloader/src/music_kraken/database/__init__.py", "Python", 0, 0, 0, 1, 1 -"/home/lars/Projects/music-downloader/src/music_kraken/database/data_models.py", "Python", 122, 0, 24, 52, 198 -"/home/lars/Projects/music-downloader/src/music_kraken/database/database.py", "Python", 104, 0, 47, 38, 189 -"/home/lars/Projects/music-downloader/src/music_kraken/download/__init__.py", "Python", 2, 0, 0, 1, 3 -"/home/lars/Projects/music-downloader/src/music_kraken/download/download.py", "Python", 35, 0, 0, 14, 49 -"/home/lars/Projects/music-downloader/src/music_kraken/download/multiple_options.py", "Python", 69, 0, 0, 31, 100 -"/home/lars/Projects/music-downloader/src/music_kraken/download/page_attributes.py", "Python", 21, 0, 1, 10, 32 -"/home/lars/Projects/music-downloader/src/music_kraken/download/search.py", "Python", 130, 0, 24, 56, 210 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/__init__.py", "Python", 0, 0, 0, 3, 3 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/fetch_audio.py", "Python", 75, 0, 12, 20, 107 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/fetch_source.py", "Python", 54, 0, 1, 16, 71 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/__init__.py", "Python", 6, 0, 0, 2, 8 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/metadata_fetch.py", "Python", 257, 0, 24, 65, 346 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/metadata_search.py", "Python", 253, 0, 40, 72, 365 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/sources/__init__.py", "Python", 3, 0, 0, 2, 5 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/sources/musicbrainz.py", "Python", 42, 0, 6, 12, 60 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/__init__.py", "Python", 0, 0, 0, 1, 1 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/genius.py", "Python", 115, 0, 16, 42, 173 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/local_files.py", "Python", 40, 0, 0, 18, 58 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/musify.py", "Python", 136, 0, 9, 37, 182 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/source.py", "Python", 11, 0, 5, 8, 24 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/youtube.py", "Python", 71, 0, 4, 24, 99 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/__init__.py", "Python", 14, 0, 0, 5, 19 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/cache.py", "Python", 37, 0, 56, 18, 111 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/collection.py", "Python", 91, 0, 31, 39, 161 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/formatted_text.py", "Python", 50, 0, 10, 19, 79 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/lyrics.py", "Python", 25, 0, 0, 7, 32 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/metadata.py", "Python", 261, 0, 62, 61, 384 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/option.py", "Python", 28, 0, 0, 13, 41 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/parents.py", "Python", 65, 0, 33, 33, 131 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/song.py", "Python", 473, 0, 113, 115, 701 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/source.py", "Python", 90, 0, 16, 32, 138 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/target.py", "Python", 63, 0, 15, 23, 101 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/__init__.py", "Python", 3, 0, 0, 1, 4 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/abstract.py", "Python", 357, 0, 34, 103, 494 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/encyclopaedia_metallum.py", "Python", 432, 0, 90, 127, 649 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/musify.py", "Python", 629, 0, 289, 187, 1105 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/preset.py", "Python", 43, 0, 1, 16, 60 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/youtube.py", "Python", 25, 0, 16, 6, 47 -"/home/lars/Projects/music-downloader/src/music_kraken/static_files/new_db.sql", "SQLite", 0, 72, 0, 10, 82 -"/home/lars/Projects/music-downloader/src/music_kraken/static_files/temp_database_structure.sql", "SQLite", 0, 135, 0, 10, 145 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/__init__.py", "Python", 2, 0, 1, 2, 5 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/config/__init__.py", "Python", 7, 0, 0, 4, 11 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/config/audio.py", "Python", 152, 0, 15, 28, 195 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/config/base_classes.py", "Python", 136, 0, 35, 61, 232 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/config/config.py", "Python", 92, 0, 16, 30, 138 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/config/connection.py", "Python", 81, 0, 2, 15, 98 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/config/logging.py", "Python", 104, 0, 4, 17, 125 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/config/misc.py", "Python", 40, 0, 0, 9, 49 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/config/paths.py", "Python", 40, 0, 0, 13, 53 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/enums/__init__.py", "Python", 0, 0, 0, 1, 1 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/enums/album.py", "Python", 16, 0, 6, 5, 27 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/enums/source.py", "Python", 40, 0, 1, 8, 49 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/exception/__init__.py", "Python", 1, 0, 0, 1, 2 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/exception/config.py", "Python", 14, 0, 8, 7, 29 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/functions.py", "Python", 3, 0, 0, 1, 4 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/object_handeling.py", "Python", 19, 0, 0, 6, 25 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/path_manager/__init__.py", "Python", 2, 0, 0, 2, 4 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/path_manager/config_directory.py", "Python", 4, 0, 0, 4, 8 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/path_manager/locations.py", "Python", 16, 0, 0, 9, 25 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/path_manager/music_directory.py", "Python", 36, 0, 9, 14, 59 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/phonetic_compares.py", "Python", 39, 0, 2, 17, 58 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/regex.py", "Python", 1, 0, 0, 2, 3 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/shared.py", "Python", 63, 0, 22, 21, 106 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/string_processing.py", "Python", 16, 0, 5, 11, 32 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/support_classes/__init__.py", "Python", 3, 0, 0, 1, 4 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/support_classes/default_target.py", "Python", 56, 0, 0, 15, 71 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/support_classes/download_result.py", "Python", 69, 0, 0, 21, 90 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/support_classes/query.py", "Python", 24, 0, 0, 9, 33 -"/home/lars/Projects/music-downloader/src/music_kraken_cli.py", "Python", 3, 0, 0, 3, 6 -"/home/lars/Projects/music-downloader/src/music_kraken_gtk.py", "Python", 3, 0, 0, 2, 5 -"/home/lars/Projects/music-downloader/src/musify_search.py", "Python", 38, 0, 0, 14, 52 -"/home/lars/Projects/music-downloader/src/tests/__init__.py", "Python", 0, 0, 0, 1, 1 -"/home/lars/Projects/music-downloader/src/tests/conftest.py", "Python", 3, 0, 1, 2, 6 -"/home/lars/Projects/music-downloader/src/tests/test_building_objects.py", "Python", 81, 0, 1, 13, 95 -"/home/lars/Projects/music-downloader/src/tests/test_download.py", "Python", 30, 0, 1, 12, 43 -"/home/lars/Projects/music-downloader/src/tests/test_objects.py", "Python", 173, 0, 15, 51, 239 -"Total", "-", 6329, 207, 1157, 1980, 9673 \ No newline at end of file diff --git a/.VSCodeCounter/2023-05-24_11-17-57/results.json b/.VSCodeCounter/2023-05-24_11-17-57/results.json deleted file mode 100644 index 5668823..0000000 --- a/.VSCodeCounter/2023-05-24_11-17-57/results.json +++ /dev/null @@ -1 +0,0 @@ -{"file:///home/lars/Projects/music-downloader/src/musify_search.py":{"language":"Python","code":38,"comment":0,"blank":14},"file:///home/lars/Projects/music-downloader/src/__init__.py":{"language":"Python","code":0,"comment":0,"blank":1},"file:///home/lars/Projects/music-downloader/src/music_kraken/__init__.py":{"language":"Python","code":189,"comment":21,"blank":60},"file:///home/lars/Projects/music-downloader/src/music_kraken/audio/__init__.py":{"language":"Python","code":7,"comment":0,"blank":3},"file:///home/lars/Projects/music-downloader/src/music_kraken/audio/codec.py":{"language":"Python","code":25,"comment":0,"blank":8},"file:///home/lars/Projects/music-downloader/src/music_kraken/audio/metadata.py":{"language":"Python","code":60,"comment":4,"blank":24},"file:///home/lars/Projects/music-downloader/src/music_kraken/database/database.py":{"language":"Python","code":104,"comment":47,"blank":38},"file:///home/lars/Projects/music-downloader/src/music_kraken/database/__init__.py":{"language":"Python","code":0,"comment":0,"blank":1},"file:///home/lars/Projects/music-downloader/src/music_kraken/database/data_models.py":{"language":"Python","code":122,"comment":24,"blank":52},"file:///home/lars/Projects/music-downloader/src/music_kraken/download/multiple_options.py":{"language":"Python","code":69,"comment":0,"blank":31},"file:///home/lars/Projects/music-downloader/src/music_kraken/download/search.py":{"language":"Python","code":130,"comment":24,"blank":56},"file:///home/lars/Projects/music-downloader/src/music_kraken/download/download.py":{"language":"Python","code":35,"comment":0,"blank":14},"file:///home/lars/Projects/music-downloader/src/music_kraken/download/__init__.py":{"language":"Python","code":2,"comment":0,"blank":1},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/__init__.py":{"language":"Python","code":2,"comment":1,"blank":2},"file:///home/lars/Projects/music-downloader/src/music_kraken/download/page_attributes.py":{"language":"Python","code":21,"comment":1,"blank":10},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/exception/__init__.py":{"language":"Python","code":1,"comment":0,"blank":1},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/exception/config.py":{"language":"Python","code":14,"comment":8,"blank":7},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/string_processing.py":{"language":"Python","code":16,"comment":5,"blank":11},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/regex.py":{"language":"Python","code":1,"comment":0,"blank":2},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/object_handeling.py":{"language":"Python","code":19,"comment":0,"blank":6},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/enums/album.py":{"language":"Python","code":16,"comment":6,"blank":5},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/enums/__init__.py":{"language":"Python","code":0,"comment":0,"blank":1},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/enums/source.py":{"language":"Python","code":40,"comment":1,"blank":8},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/phonetic_compares.py":{"language":"Python","code":39,"comment":2,"blank":17},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/support_classes/query.py":{"language":"Python","code":24,"comment":0,"blank":9},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/support_classes/__init__.py":{"language":"Python","code":3,"comment":0,"blank":1},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/support_classes/download_result.py":{"language":"Python","code":69,"comment":0,"blank":21},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/config/config.py":{"language":"Python","code":92,"comment":16,"blank":30},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/support_classes/default_target.py":{"language":"Python","code":56,"comment":0,"blank":15},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/config/paths.py":{"language":"Python","code":40,"comment":0,"blank":13},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/config/__init__.py":{"language":"Python","code":7,"comment":0,"blank":4},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/config/logging.py":{"language":"Python","code":104,"comment":4,"blank":17},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/config/connection.py":{"language":"Python","code":81,"comment":2,"blank":15},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/shared.py":{"language":"Python","code":63,"comment":22,"blank":21},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/config/misc.py":{"language":"Python","code":40,"comment":0,"blank":9},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/config/base_classes.py":{"language":"Python","code":136,"comment":35,"blank":61},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/config/audio.py":{"language":"Python","code":152,"comment":15,"blank":28},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/functions.py":{"language":"Python","code":3,"comment":0,"blank":1},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/path_manager/config_directory.py":{"language":"Python","code":4,"comment":0,"blank":4},"file:///home/lars/Projects/music-downloader/src/music_kraken/pages/__init__.py":{"language":"Python","code":3,"comment":0,"blank":1},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/path_manager/__init__.py":{"language":"Python","code":2,"comment":0,"blank":2},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/path_manager/music_directory.py":{"language":"Python","code":36,"comment":9,"blank":14},"file:///home/lars/Projects/music-downloader/src/music_kraken/pages/abstract.py":{"language":"Python","code":357,"comment":34,"blank":103},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/path_manager/locations.py":{"language":"Python","code":16,"comment":0,"blank":9},"file:///home/lars/Projects/music-downloader/src/music_kraken/pages/preset.py":{"language":"Python","code":43,"comment":1,"blank":16},"file:///home/lars/Projects/music-downloader/src/music_kraken/pages/musify.py":{"language":"Python","code":629,"comment":289,"blank":187},"file:///home/lars/Projects/music-downloader/src/music_kraken/pages/youtube.py":{"language":"Python","code":25,"comment":16,"blank":6},"file:///home/lars/Projects/music-downloader/src/music_kraken/pages/encyclopaedia_metallum.py":{"language":"Python","code":432,"comment":90,"blank":127},"file:///home/lars/Projects/music-downloader/src/music_kraken/connection/rotating.py":{"language":"Python","code":27,"comment":3,"blank":14},"file:///home/lars/Projects/music-downloader/src/music_kraken/connection/__init__.py":{"language":"Python","code":1,"comment":0,"blank":1},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/__init__.py":{"language":"Python","code":14,"comment":0,"blank":5},"file:///home/lars/Projects/music-downloader/src/music_kraken/connection/connection.py":{"language":"Python","code":162,"comment":1,"blank":30},"file:///home/lars/Projects/music-downloader/src/music_kraken/static_files/new_db.sql":{"language":"SQLite","code":72,"comment":0,"blank":10},"file:///home/lars/Projects/music-downloader/src/music_kraken/static_files/temp_database_structure.sql":{"language":"SQLite","code":135,"comment":0,"blank":10},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/metadata.py":{"language":"Python","code":261,"comment":62,"blank":61},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/cache.py":{"language":"Python","code":37,"comment":56,"blank":18},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/source.py":{"language":"Python","code":90,"comment":16,"blank":32},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/parents.py":{"language":"Python","code":65,"comment":33,"blank":33},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/target.py":{"language":"Python","code":63,"comment":15,"blank":23},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/lyrics.py":{"language":"Python","code":25,"comment":0,"blank":7},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/option.py":{"language":"Python","code":28,"comment":0,"blank":13},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/song.py":{"language":"Python","code":473,"comment":113,"blank":115},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/collection.py":{"language":"Python","code":91,"comment":31,"blank":39},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/formatted_text.py":{"language":"Python","code":50,"comment":10,"blank":19},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/__init__.py":{"language":"Python","code":0,"comment":0,"blank":1},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/__init__.py":{"language":"Python","code":0,"comment":0,"blank":3},"file:///home/lars/Projects/music-downloader/src/music_kraken/__main__.py":{"language":"Python","code":88,"comment":3,"blank":21},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/fetch_audio.py":{"language":"Python","code":75,"comment":12,"blank":20},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/source.py":{"language":"Python","code":11,"comment":5,"blank":8},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/youtube.py":{"language":"Python","code":71,"comment":4,"blank":24},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/genius.py":{"language":"Python","code":115,"comment":16,"blank":42},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/musify.py":{"language":"Python","code":136,"comment":9,"blank":37},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/__init__.py":{"language":"Python","code":6,"comment":0,"blank":2},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/metadata_search.py":{"language":"Python","code":253,"comment":40,"blank":72},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/fetch_source.py":{"language":"Python","code":54,"comment":1,"blank":16},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/local_files.py":{"language":"Python","code":40,"comment":0,"blank":18},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/sources/__init__.py":{"language":"Python","code":3,"comment":0,"blank":2},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/metadata_fetch.py":{"language":"Python","code":257,"comment":24,"blank":65},"file:///home/lars/Projects/music-downloader/src/tests/__init__.py":{"language":"Python","code":0,"comment":0,"blank":1},"file:///home/lars/Projects/music-downloader/src/tests/test_objects.py":{"language":"Python","code":173,"comment":15,"blank":51},"file:///home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/sources/musicbrainz.py":{"language":"Python","code":42,"comment":6,"blank":12},"file:///home/lars/Projects/music-downloader/src/tests/test_building_objects.py":{"language":"Python","code":81,"comment":1,"blank":13},"file:///home/lars/Projects/music-downloader/src/tests/test_download.py":{"language":"Python","code":30,"comment":1,"blank":12},"file:///home/lars/Projects/music-downloader/src/metal_archives.py":{"language":"Python","code":30,"comment":0,"blank":12},"file:///home/lars/Projects/music-downloader/src/tests/conftest.py":{"language":"Python","code":3,"comment":1,"blank":2},"file:///home/lars/Projects/music-downloader/src/create_custom_objects.py":{"language":"Python","code":58,"comment":0,"blank":6},"file:///home/lars/Projects/music-downloader/src/music_kraken_gtk.py":{"language":"Python","code":3,"comment":0,"blank":2},"file:///home/lars/Projects/music-downloader/src/music_kraken_cli.py":{"language":"Python","code":3,"comment":0,"blank":3},"file:///home/lars/Projects/music-downloader/src/actual_donwload.py":{"language":"Python","code":43,"comment":2,"blank":18}} \ No newline at end of file diff --git a/.VSCodeCounter/2023-05-24_11-17-57/results.md b/.VSCodeCounter/2023-05-24_11-17-57/results.md deleted file mode 100644 index ceeb9f0..0000000 --- a/.VSCodeCounter/2023-05-24_11-17-57/results.md +++ /dev/null @@ -1,46 +0,0 @@ -# Summary - -Date : 2023-05-24 11:17:57 - -Directory /home/lars/Projects/music-downloader/src - -Total : 89 files, 6536 codes, 1157 comments, 1980 blanks, all 9673 lines - -Summary / [Details](details.md) / [Diff Summary](diff.md) / [Diff Details](diff-details.md) - -## Languages -| language | files | code | comment | blank | total | -| :--- | ---: | ---: | ---: | ---: | ---: | -| Python | 87 | 6,329 | 1,157 | 1,960 | 9,446 | -| SQLite | 2 | 207 | 0 | 20 | 227 | - -## Directories -| path | files | code | comment | blank | total | -| :--- | ---: | ---: | ---: | ---: | ---: | -| . | 89 | 6,536 | 1,157 | 1,980 | 9,673 | -| . (Files) | 7 | 175 | 2 | 56 | 233 | -| music_kraken | 77 | 6,074 | 1,137 | 1,845 | 9,056 | -| music_kraken (Files) | 2 | 277 | 24 | 81 | 382 | -| music_kraken/audio | 3 | 92 | 4 | 35 | 131 | -| music_kraken/connection | 3 | 190 | 4 | 45 | 239 | -| music_kraken/database | 3 | 226 | 71 | 91 | 388 | -| music_kraken/download | 5 | 257 | 25 | 112 | 394 | -| music_kraken/not_used_anymore | 14 | 1,063 | 117 | 322 | 1,502 | -| music_kraken/not_used_anymore (Files) | 3 | 129 | 13 | 39 | 181 | -| music_kraken/not_used_anymore/metadata | 5 | 561 | 70 | 153 | 784 | -| music_kraken/not_used_anymore/metadata (Files) | 3 | 516 | 64 | 139 | 719 | -| music_kraken/not_used_anymore/metadata/sources | 2 | 45 | 6 | 14 | 65 | -| music_kraken/not_used_anymore/sources | 6 | 373 | 34 | 130 | 537 | -| music_kraken/objects | 11 | 1,197 | 336 | 365 | 1,898 | -| music_kraken/pages | 6 | 1,489 | 430 | 440 | 2,359 | -| music_kraken/static_files | 2 | 207 | 0 | 20 | 227 | -| music_kraken/utils | 28 | 1,076 | 126 | 334 | 1,536 | -| music_kraken/utils (Files) | 7 | 143 | 30 | 60 | 233 | -| music_kraken/utils/config | 8 | 652 | 72 | 177 | 901 | -| music_kraken/utils/enums | 3 | 56 | 7 | 14 | 77 | -| music_kraken/utils/exception | 2 | 15 | 8 | 8 | 31 | -| music_kraken/utils/path_manager | 4 | 58 | 9 | 29 | 96 | -| music_kraken/utils/support_classes | 4 | 152 | 0 | 46 | 198 | -| tests | 5 | 287 | 18 | 79 | 384 | - -Summary / [Details](details.md) / [Diff Summary](diff.md) / [Diff Details](diff-details.md) \ No newline at end of file diff --git a/.VSCodeCounter/2023-05-24_11-17-57/results.txt b/.VSCodeCounter/2023-05-24_11-17-57/results.txt deleted file mode 100644 index 3dfdbb8..0000000 --- a/.VSCodeCounter/2023-05-24_11-17-57/results.txt +++ /dev/null @@ -1,138 +0,0 @@ -Date : 2023-05-24 11:17:57 -Directory : /home/lars/Projects/music-downloader/src -Total : 89 files, 6536 codes, 1157 comments, 1980 blanks, all 9673 lines - -Languages -+----------+------------+------------+------------+------------+------------+ -| language | files | code | comment | blank | total | -+----------+------------+------------+------------+------------+------------+ -| Python | 87 | 6,329 | 1,157 | 1,960 | 9,446 | -| SQLite | 2 | 207 | 0 | 20 | 227 | -+----------+------------+------------+------------+------------+------------+ - -Directories -+--------------------------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ -| path | files | code | comment | blank | total | -+--------------------------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ -| . | 89 | 6,536 | 1,157 | 1,980 | 9,673 | -| . (Files) | 7 | 175 | 2 | 56 | 233 | -| music_kraken | 77 | 6,074 | 1,137 | 1,845 | 9,056 | -| music_kraken (Files) | 2 | 277 | 24 | 81 | 382 | -| music_kraken/audio | 3 | 92 | 4 | 35 | 131 | -| music_kraken/connection | 3 | 190 | 4 | 45 | 239 | -| music_kraken/database | 3 | 226 | 71 | 91 | 388 | -| music_kraken/download | 5 | 257 | 25 | 112 | 394 | -| music_kraken/not_used_anymore | 14 | 1,063 | 117 | 322 | 1,502 | -| music_kraken/not_used_anymore (Files) | 3 | 129 | 13 | 39 | 181 | -| music_kraken/not_used_anymore/metadata | 5 | 561 | 70 | 153 | 784 | -| music_kraken/not_used_anymore/metadata (Files) | 3 | 516 | 64 | 139 | 719 | -| music_kraken/not_used_anymore/metadata/sources | 2 | 45 | 6 | 14 | 65 | -| music_kraken/not_used_anymore/sources | 6 | 373 | 34 | 130 | 537 | -| music_kraken/objects | 11 | 1,197 | 336 | 365 | 1,898 | -| music_kraken/pages | 6 | 1,489 | 430 | 440 | 2,359 | -| music_kraken/static_files | 2 | 207 | 0 | 20 | 227 | -| music_kraken/utils | 28 | 1,076 | 126 | 334 | 1,536 | -| music_kraken/utils (Files) | 7 | 143 | 30 | 60 | 233 | -| music_kraken/utils/config | 8 | 652 | 72 | 177 | 901 | -| music_kraken/utils/enums | 3 | 56 | 7 | 14 | 77 | -| music_kraken/utils/exception | 2 | 15 | 8 | 8 | 31 | -| music_kraken/utils/path_manager | 4 | 58 | 9 | 29 | 96 | -| music_kraken/utils/support_classes | 4 | 152 | 0 | 46 | 198 | -| tests | 5 | 287 | 18 | 79 | 384 | -+--------------------------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ - -Files -+--------------------------------------------------------------------------------------------------------+----------+------------+------------+------------+------------+ -| filename | language | code | comment | blank | total | -+--------------------------------------------------------------------------------------------------------+----------+------------+------------+------------+------------+ -| /home/lars/Projects/music-downloader/src/__init__.py | Python | 0 | 0 | 1 | 1 | -| /home/lars/Projects/music-downloader/src/actual_donwload.py | Python | 43 | 2 | 18 | 63 | -| /home/lars/Projects/music-downloader/src/create_custom_objects.py | Python | 58 | 0 | 6 | 64 | -| /home/lars/Projects/music-downloader/src/metal_archives.py | Python | 30 | 0 | 12 | 42 | -| /home/lars/Projects/music-downloader/src/music_kraken/__init__.py | Python | 189 | 21 | 60 | 270 | -| /home/lars/Projects/music-downloader/src/music_kraken/__main__.py | Python | 88 | 3 | 21 | 112 | -| /home/lars/Projects/music-downloader/src/music_kraken/audio/__init__.py | Python | 7 | 0 | 3 | 10 | -| /home/lars/Projects/music-downloader/src/music_kraken/audio/codec.py | Python | 25 | 0 | 8 | 33 | -| /home/lars/Projects/music-downloader/src/music_kraken/audio/metadata.py | Python | 60 | 4 | 24 | 88 | -| /home/lars/Projects/music-downloader/src/music_kraken/connection/__init__.py | Python | 1 | 0 | 1 | 2 | -| /home/lars/Projects/music-downloader/src/music_kraken/connection/connection.py | Python | 162 | 1 | 30 | 193 | -| /home/lars/Projects/music-downloader/src/music_kraken/connection/rotating.py | Python | 27 | 3 | 14 | 44 | -| /home/lars/Projects/music-downloader/src/music_kraken/database/__init__.py | Python | 0 | 0 | 1 | 1 | -| /home/lars/Projects/music-downloader/src/music_kraken/database/data_models.py | Python | 122 | 24 | 52 | 198 | -| /home/lars/Projects/music-downloader/src/music_kraken/database/database.py | Python | 104 | 47 | 38 | 189 | -| /home/lars/Projects/music-downloader/src/music_kraken/download/__init__.py | Python | 2 | 0 | 1 | 3 | -| /home/lars/Projects/music-downloader/src/music_kraken/download/download.py | Python | 35 | 0 | 14 | 49 | -| /home/lars/Projects/music-downloader/src/music_kraken/download/multiple_options.py | Python | 69 | 0 | 31 | 100 | -| /home/lars/Projects/music-downloader/src/music_kraken/download/page_attributes.py | Python | 21 | 1 | 10 | 32 | -| /home/lars/Projects/music-downloader/src/music_kraken/download/search.py | Python | 130 | 24 | 56 | 210 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/__init__.py | Python | 0 | 0 | 3 | 3 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/fetch_audio.py | Python | 75 | 12 | 20 | 107 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/fetch_source.py | Python | 54 | 1 | 16 | 71 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/__init__.py | Python | 6 | 0 | 2 | 8 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/metadata_fetch.py | Python | 257 | 24 | 65 | 346 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/metadata_search.py | Python | 253 | 40 | 72 | 365 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/sources/__init__.py | Python | 3 | 0 | 2 | 5 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/sources/musicbrainz.py | Python | 42 | 6 | 12 | 60 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/__init__.py | Python | 0 | 0 | 1 | 1 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/genius.py | Python | 115 | 16 | 42 | 173 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/local_files.py | Python | 40 | 0 | 18 | 58 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/musify.py | Python | 136 | 9 | 37 | 182 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/source.py | Python | 11 | 5 | 8 | 24 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/youtube.py | Python | 71 | 4 | 24 | 99 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/__init__.py | Python | 14 | 0 | 5 | 19 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/cache.py | Python | 37 | 56 | 18 | 111 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/collection.py | Python | 91 | 31 | 39 | 161 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/formatted_text.py | Python | 50 | 10 | 19 | 79 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/lyrics.py | Python | 25 | 0 | 7 | 32 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/metadata.py | Python | 261 | 62 | 61 | 384 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/option.py | Python | 28 | 0 | 13 | 41 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/parents.py | Python | 65 | 33 | 33 | 131 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/song.py | Python | 473 | 113 | 115 | 701 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/source.py | Python | 90 | 16 | 32 | 138 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/target.py | Python | 63 | 15 | 23 | 101 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/__init__.py | Python | 3 | 0 | 1 | 4 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/abstract.py | Python | 357 | 34 | 103 | 494 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/encyclopaedia_metallum.py | Python | 432 | 90 | 127 | 649 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/musify.py | Python | 629 | 289 | 187 | 1,105 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/preset.py | Python | 43 | 1 | 16 | 60 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/youtube.py | Python | 25 | 16 | 6 | 47 | -| /home/lars/Projects/music-downloader/src/music_kraken/static_files/new_db.sql | SQLite | 72 | 0 | 10 | 82 | -| /home/lars/Projects/music-downloader/src/music_kraken/static_files/temp_database_structure.sql | SQLite | 135 | 0 | 10 | 145 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/__init__.py | Python | 2 | 1 | 2 | 5 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/config/__init__.py | Python | 7 | 0 | 4 | 11 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/config/audio.py | Python | 152 | 15 | 28 | 195 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/config/base_classes.py | Python | 136 | 35 | 61 | 232 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/config/config.py | Python | 92 | 16 | 30 | 138 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/config/connection.py | Python | 81 | 2 | 15 | 98 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/config/logging.py | Python | 104 | 4 | 17 | 125 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/config/misc.py | Python | 40 | 0 | 9 | 49 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/config/paths.py | Python | 40 | 0 | 13 | 53 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/enums/__init__.py | Python | 0 | 0 | 1 | 1 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/enums/album.py | Python | 16 | 6 | 5 | 27 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/enums/source.py | Python | 40 | 1 | 8 | 49 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/exception/__init__.py | Python | 1 | 0 | 1 | 2 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/exception/config.py | Python | 14 | 8 | 7 | 29 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/functions.py | Python | 3 | 0 | 1 | 4 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/object_handeling.py | Python | 19 | 0 | 6 | 25 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/path_manager/__init__.py | Python | 2 | 0 | 2 | 4 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/path_manager/config_directory.py | Python | 4 | 0 | 4 | 8 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/path_manager/locations.py | Python | 16 | 0 | 9 | 25 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/path_manager/music_directory.py | Python | 36 | 9 | 14 | 59 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/phonetic_compares.py | Python | 39 | 2 | 17 | 58 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/regex.py | Python | 1 | 0 | 2 | 3 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/shared.py | Python | 63 | 22 | 21 | 106 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/string_processing.py | Python | 16 | 5 | 11 | 32 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/support_classes/__init__.py | Python | 3 | 0 | 1 | 4 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/support_classes/default_target.py | Python | 56 | 0 | 15 | 71 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/support_classes/download_result.py | Python | 69 | 0 | 21 | 90 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/support_classes/query.py | Python | 24 | 0 | 9 | 33 | -| /home/lars/Projects/music-downloader/src/music_kraken_cli.py | Python | 3 | 0 | 3 | 6 | -| /home/lars/Projects/music-downloader/src/music_kraken_gtk.py | Python | 3 | 0 | 2 | 5 | -| /home/lars/Projects/music-downloader/src/musify_search.py | Python | 38 | 0 | 14 | 52 | -| /home/lars/Projects/music-downloader/src/tests/__init__.py | Python | 0 | 0 | 1 | 1 | -| /home/lars/Projects/music-downloader/src/tests/conftest.py | Python | 3 | 1 | 2 | 6 | -| /home/lars/Projects/music-downloader/src/tests/test_building_objects.py | Python | 81 | 1 | 13 | 95 | -| /home/lars/Projects/music-downloader/src/tests/test_download.py | Python | 30 | 1 | 12 | 43 | -| /home/lars/Projects/music-downloader/src/tests/test_objects.py | Python | 173 | 15 | 51 | 239 | -| Total | | 6,536 | 1,157 | 1,980 | 9,673 | -+--------------------------------------------------------------------------------------------------------+----------+------------+------------+------------+------------+ \ No newline at end of file diff --git a/.VSCodeCounter/2023-06-14_07-47-51/details.md b/.VSCodeCounter/2023-06-14_07-47-51/details.md deleted file mode 100644 index 6e15e50..0000000 --- a/.VSCodeCounter/2023-06-14_07-47-51/details.md +++ /dev/null @@ -1,98 +0,0 @@ -# Details - -Date : 2023-06-14 07:47:51 - -Directory /home/lars/Projects/music-downloader/src - -Total : 83 files, 5827 codes, 1152 comments, 1825 blanks, all 8804 lines - -[Summary](results.md) / Details / [Diff Summary](diff.md) / [Diff Details](diff-details.md) - -## Files -| filename | language | code | comment | blank | total | -| :--- | :--- | ---: | ---: | ---: | ---: | -| [src/__init__.py](/src/__init__.py) | Python | 0 | 0 | 1 | 1 | -| [src/actual_donwload.py](/src/actual_donwload.py) | Python | 16 | 0 | 6 | 22 | -| [src/create_custom_objects.py](/src/create_custom_objects.py) | Python | 58 | 0 | 6 | 64 | -| [src/metal_archives.py](/src/metal_archives.py) | Python | 30 | 0 | 12 | 42 | -| [src/music_kraken/__init__.py](/src/music_kraken/__init__.py) | Python | 112 | 6 | 33 | 151 | -| [src/music_kraken/__main__.py](/src/music_kraken/__main__.py) | Python | 98 | 3 | 23 | 124 | -| [src/music_kraken/audio/__init__.py](/src/music_kraken/audio/__init__.py) | Python | 7 | 0 | 3 | 10 | -| [src/music_kraken/audio/codec.py](/src/music_kraken/audio/codec.py) | Python | 25 | 0 | 8 | 33 | -| [src/music_kraken/audio/metadata.py](/src/music_kraken/audio/metadata.py) | Python | 60 | 4 | 24 | 88 | -| [src/music_kraken/cli/__init__.py](/src/music_kraken/cli/__init__.py) | Python | 2 | 0 | 0 | 2 | -| [src/music_kraken/cli/download/__init__.py](/src/music_kraken/cli/download/__init__.py) | Python | 0 | 0 | 1 | 1 | -| [src/music_kraken/cli/download/shell.py](/src/music_kraken/cli/download/shell.py) | Python | 199 | 86 | 78 | 363 | -| [src/music_kraken/cli/options/__init__.py](/src/music_kraken/cli/options/__init__.py) | Python | 3 | 0 | 1 | 4 | -| [src/music_kraken/cli/options/invidious/__init__.py](/src/music_kraken/cli/options/invidious/__init__.py) | Python | 0 | 0 | 1 | 1 | -| [src/music_kraken/cli/options/invidious/shell.py](/src/music_kraken/cli/options/invidious/shell.py) | Python | 66 | 7 | 28 | 101 | -| [src/music_kraken/connection/__init__.py](/src/music_kraken/connection/__init__.py) | Python | 1 | 0 | 1 | 2 | -| [src/music_kraken/connection/connection.py](/src/music_kraken/connection/connection.py) | Python | 168 | 1 | 30 | 199 | -| [src/music_kraken/connection/rotating.py](/src/music_kraken/connection/rotating.py) | Python | 27 | 3 | 14 | 44 | -| [src/music_kraken/database/__init__.py](/src/music_kraken/database/__init__.py) | Python | 0 | 0 | 1 | 1 | -| [src/music_kraken/database/data_models.py](/src/music_kraken/database/data_models.py) | Python | 122 | 24 | 52 | 198 | -| [src/music_kraken/database/database.py](/src/music_kraken/database/database.py) | Python | 104 | 47 | 38 | 189 | -| [src/music_kraken/download/__init__.py](/src/music_kraken/download/__init__.py) | Python | 1 | 0 | 1 | 2 | -| [src/music_kraken/download/multiple_options.py](/src/music_kraken/download/multiple_options.py) | Python | 69 | 0 | 31 | 100 | -| [src/music_kraken/download/page_attributes.py](/src/music_kraken/download/page_attributes.py) | Python | 67 | 1 | 29 | 97 | -| [src/music_kraken/download/results.py](/src/music_kraken/download/results.py) | Python | 62 | 7 | 26 | 95 | -| [src/music_kraken/download/search.py](/src/music_kraken/download/search.py) | Python | 124 | 24 | 56 | 204 | -| [src/music_kraken/objects/__init__.py](/src/music_kraken/objects/__init__.py) | Python | 14 | 0 | 5 | 19 | -| [src/music_kraken/objects/cache.py](/src/music_kraken/objects/cache.py) | Python | 37 | 56 | 18 | 111 | -| [src/music_kraken/objects/collection.py](/src/music_kraken/objects/collection.py) | Python | 91 | 31 | 39 | 161 | -| [src/music_kraken/objects/formatted_text.py](/src/music_kraken/objects/formatted_text.py) | Python | 50 | 10 | 19 | 79 | -| [src/music_kraken/objects/lyrics.py](/src/music_kraken/objects/lyrics.py) | Python | 25 | 0 | 7 | 32 | -| [src/music_kraken/objects/metadata.py](/src/music_kraken/objects/metadata.py) | Python | 272 | 62 | 63 | 397 | -| [src/music_kraken/objects/option.py](/src/music_kraken/objects/option.py) | Python | 28 | 0 | 13 | 41 | -| [src/music_kraken/objects/parents.py](/src/music_kraken/objects/parents.py) | Python | 71 | 35 | 35 | 141 | -| [src/music_kraken/objects/song.py](/src/music_kraken/objects/song.py) | Python | 480 | 113 | 120 | 713 | -| [src/music_kraken/objects/source.py](/src/music_kraken/objects/source.py) | Python | 93 | 16 | 33 | 142 | -| [src/music_kraken/objects/target.py](/src/music_kraken/objects/target.py) | Python | 65 | 15 | 24 | 104 | -| [src/music_kraken/pages/__init__.py](/src/music_kraken/pages/__init__.py) | Python | 4 | 0 | 2 | 6 | -| [src/music_kraken/pages/abstract.py](/src/music_kraken/pages/abstract.py) | Python | 234 | 46 | 93 | 373 | -| [src/music_kraken/pages/encyclopaedia_metallum.py](/src/music_kraken/pages/encyclopaedia_metallum.py) | Python | 432 | 90 | 127 | 649 | -| [src/music_kraken/pages/musify.py](/src/music_kraken/pages/musify.py) | Python | 576 | 275 | 166 | 1,017 | -| [src/music_kraken/pages/preset.py](/src/music_kraken/pages/preset.py) | Python | 47 | 1 | 17 | 65 | -| [src/music_kraken/pages/youtube.py](/src/music_kraken/pages/youtube.py) | Python | 254 | 45 | 79 | 378 | -| [src/music_kraken/static_files/new_db.sql](/src/music_kraken/static_files/new_db.sql) | SQLite | 72 | 0 | 10 | 82 | -| [src/music_kraken/static_files/temp_database_structure.sql](/src/music_kraken/static_files/temp_database_structure.sql) | SQLite | 135 | 0 | 10 | 145 | -| [src/music_kraken/utils/__init__.py](/src/music_kraken/utils/__init__.py) | Python | 2 | 1 | 2 | 5 | -| [src/music_kraken/utils/config/__init__.py](/src/music_kraken/utils/config/__init__.py) | Python | 7 | 0 | 4 | 11 | -| [src/music_kraken/utils/config/audio.py](/src/music_kraken/utils/config/audio.py) | Python | 152 | 15 | 28 | 195 | -| [src/music_kraken/utils/config/base_classes.py](/src/music_kraken/utils/config/base_classes.py) | Python | 136 | 35 | 61 | 232 | -| [src/music_kraken/utils/config/config.py](/src/music_kraken/utils/config/config.py) | Python | 92 | 16 | 30 | 138 | -| [src/music_kraken/utils/config/connection.py](/src/music_kraken/utils/config/connection.py) | Python | 80 | 2 | 15 | 97 | -| [src/music_kraken/utils/config/logging.py](/src/music_kraken/utils/config/logging.py) | Python | 104 | 4 | 17 | 125 | -| [src/music_kraken/utils/config/misc.py](/src/music_kraken/utils/config/misc.py) | Python | 40 | 0 | 9 | 49 | -| [src/music_kraken/utils/config/paths.py](/src/music_kraken/utils/config/paths.py) | Python | 40 | 0 | 13 | 53 | -| [src/music_kraken/utils/enums/__init__.py](/src/music_kraken/utils/enums/__init__.py) | Python | 0 | 0 | 1 | 1 | -| [src/music_kraken/utils/enums/album.py](/src/music_kraken/utils/enums/album.py) | Python | 16 | 6 | 5 | 27 | -| [src/music_kraken/utils/enums/source.py](/src/music_kraken/utils/enums/source.py) | Python | 40 | 1 | 8 | 49 | -| [src/music_kraken/utils/exception/__init__.py](/src/music_kraken/utils/exception/__init__.py) | Python | 1 | 0 | 1 | 2 | -| [src/music_kraken/utils/exception/config.py](/src/music_kraken/utils/exception/config.py) | Python | 14 | 8 | 7 | 29 | -| [src/music_kraken/utils/exception/download.py](/src/music_kraken/utils/exception/download.py) | Python | 8 | 0 | 4 | 12 | -| [src/music_kraken/utils/functions.py](/src/music_kraken/utils/functions.py) | Python | 3 | 0 | 1 | 4 | -| [src/music_kraken/utils/object_handeling.py](/src/music_kraken/utils/object_handeling.py) | Python | 19 | 0 | 6 | 25 | -| [src/music_kraken/utils/path_manager/__init__.py](/src/music_kraken/utils/path_manager/__init__.py) | Python | 2 | 0 | 2 | 4 | -| [src/music_kraken/utils/path_manager/config_directory.py](/src/music_kraken/utils/path_manager/config_directory.py) | Python | 4 | 0 | 4 | 8 | -| [src/music_kraken/utils/path_manager/locations.py](/src/music_kraken/utils/path_manager/locations.py) | Python | 16 | 0 | 9 | 25 | -| [src/music_kraken/utils/path_manager/music_directory.py](/src/music_kraken/utils/path_manager/music_directory.py) | Python | 36 | 9 | 14 | 59 | -| [src/music_kraken/utils/phonetic_compares.py](/src/music_kraken/utils/phonetic_compares.py) | Python | 39 | 2 | 17 | 58 | -| [src/music_kraken/utils/regex.py](/src/music_kraken/utils/regex.py) | Python | 1 | 0 | 2 | 3 | -| [src/music_kraken/utils/shared.py](/src/music_kraken/utils/shared.py) | Python | 66 | 22 | 22 | 110 | -| [src/music_kraken/utils/string_processing.py](/src/music_kraken/utils/string_processing.py) | Python | 16 | 5 | 11 | 32 | -| [src/music_kraken/utils/support_classes/__init__.py](/src/music_kraken/utils/support_classes/__init__.py) | Python | 4 | 0 | 1 | 5 | -| [src/music_kraken/utils/support_classes/default_target.py](/src/music_kraken/utils/support_classes/default_target.py) | Python | 56 | 0 | 15 | 71 | -| [src/music_kraken/utils/support_classes/download_result.py](/src/music_kraken/utils/support_classes/download_result.py) | Python | 69 | 0 | 21 | 90 | -| [src/music_kraken/utils/support_classes/query.py](/src/music_kraken/utils/support_classes/query.py) | Python | 24 | 0 | 9 | 33 | -| [src/music_kraken/utils/support_classes/thread_classes.py](/src/music_kraken/utils/support_classes/thread_classes.py) | Python | 8 | 0 | 4 | 12 | -| [src/music_kraken_cli.py](/src/music_kraken_cli.py) | Python | 3 | 0 | 3 | 6 | -| [src/music_kraken_gtk.py](/src/music_kraken_gtk.py) | Python | 3 | 0 | 2 | 5 | -| [src/musify_search.py](/src/musify_search.py) | Python | 38 | 0 | 14 | 52 | -| [src/tests/__init__.py](/src/tests/__init__.py) | Python | 0 | 0 | 1 | 1 | -| [src/tests/conftest.py](/src/tests/conftest.py) | Python | 3 | 1 | 2 | 6 | -| [src/tests/test_building_objects.py](/src/tests/test_building_objects.py) | Python | 81 | 1 | 13 | 95 | -| [src/tests/test_download.py](/src/tests/test_download.py) | Python | 30 | 1 | 12 | 43 | -| [src/tests/test_objects.py](/src/tests/test_objects.py) | Python | 173 | 15 | 51 | 239 | - -[Summary](results.md) / Details / [Diff Summary](diff.md) / [Diff Details](diff-details.md) \ No newline at end of file diff --git a/.VSCodeCounter/2023-06-14_07-47-51/diff-details.md b/.VSCodeCounter/2023-06-14_07-47-51/diff-details.md deleted file mode 100644 index bf042cd..0000000 --- a/.VSCodeCounter/2023-06-14_07-47-51/diff-details.md +++ /dev/null @@ -1,59 +0,0 @@ -# Diff Details - -Date : 2023-06-14 07:47:51 - -Directory /home/lars/Projects/music-downloader/src - -Total : 44 files, -709 codes, -5 comments, -155 blanks, all -869 lines - -[Summary](results.md) / [Details](details.md) / [Diff Summary](diff.md) / Diff Details - -## Files -| filename | language | code | comment | blank | total | -| :--- | :--- | ---: | ---: | ---: | ---: | -| [src/actual_donwload.py](/src/actual_donwload.py) | Python | -27 | -2 | -12 | -41 | -| [src/music_kraken/__init__.py](/src/music_kraken/__init__.py) | Python | -77 | -15 | -27 | -119 | -| [src/music_kraken/__main__.py](/src/music_kraken/__main__.py) | Python | 10 | 0 | 2 | 12 | -| [src/music_kraken/cli/__init__.py](/src/music_kraken/cli/__init__.py) | Python | 2 | 0 | 0 | 2 | -| [src/music_kraken/cli/download/__init__.py](/src/music_kraken/cli/download/__init__.py) | Python | 0 | 0 | 1 | 1 | -| [src/music_kraken/cli/download/shell.py](/src/music_kraken/cli/download/shell.py) | Python | 199 | 86 | 78 | 363 | -| [src/music_kraken/cli/options/__init__.py](/src/music_kraken/cli/options/__init__.py) | Python | 3 | 0 | 1 | 4 | -| [src/music_kraken/cli/options/invidious/__init__.py](/src/music_kraken/cli/options/invidious/__init__.py) | Python | 0 | 0 | 1 | 1 | -| [src/music_kraken/cli/options/invidious/shell.py](/src/music_kraken/cli/options/invidious/shell.py) | Python | 66 | 7 | 28 | 101 | -| [src/music_kraken/connection/connection.py](/src/music_kraken/connection/connection.py) | Python | 6 | 0 | 0 | 6 | -| [src/music_kraken/download/__init__.py](/src/music_kraken/download/__init__.py) | Python | -1 | 0 | 0 | -1 | -| [src/music_kraken/download/download.py](/src/music_kraken/download/download.py) | Python | -35 | 0 | -14 | -49 | -| [src/music_kraken/download/page_attributes.py](/src/music_kraken/download/page_attributes.py) | Python | 46 | 0 | 19 | 65 | -| [src/music_kraken/download/results.py](/src/music_kraken/download/results.py) | Python | 62 | 7 | 26 | 95 | -| [src/music_kraken/download/search.py](/src/music_kraken/download/search.py) | Python | -6 | 0 | 0 | -6 | -| [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 | -| [src/music_kraken/not_used_anymore/fetch_source.py](/src/music_kraken/not_used_anymore/fetch_source.py) | Python | -54 | -1 | -16 | -71 | -| [src/music_kraken/not_used_anymore/metadata/__init__.py](/src/music_kraken/not_used_anymore/metadata/__init__.py) | Python | -6 | 0 | -2 | -8 | -| [src/music_kraken/not_used_anymore/metadata/metadata_fetch.py](/src/music_kraken/not_used_anymore/metadata/metadata_fetch.py) | Python | -257 | -24 | -65 | -346 | -| [src/music_kraken/not_used_anymore/metadata/metadata_search.py](/src/music_kraken/not_used_anymore/metadata/metadata_search.py) | Python | -253 | -40 | -72 | -365 | -| [src/music_kraken/not_used_anymore/metadata/sources/__init__.py](/src/music_kraken/not_used_anymore/metadata/sources/__init__.py) | Python | -3 | 0 | -2 | -5 | -| [src/music_kraken/not_used_anymore/metadata/sources/musicbrainz.py](/src/music_kraken/not_used_anymore/metadata/sources/musicbrainz.py) | Python | -42 | -6 | -12 | -60 | -| [src/music_kraken/not_used_anymore/sources/__init__.py](/src/music_kraken/not_used_anymore/sources/__init__.py) | Python | 0 | 0 | -1 | -1 | -| [src/music_kraken/not_used_anymore/sources/genius.py](/src/music_kraken/not_used_anymore/sources/genius.py) | Python | -115 | -16 | -42 | -173 | -| [src/music_kraken/not_used_anymore/sources/local_files.py](/src/music_kraken/not_used_anymore/sources/local_files.py) | Python | -40 | 0 | -18 | -58 | -| [src/music_kraken/not_used_anymore/sources/musify.py](/src/music_kraken/not_used_anymore/sources/musify.py) | Python | -136 | -9 | -37 | -182 | -| [src/music_kraken/not_used_anymore/sources/source.py](/src/music_kraken/not_used_anymore/sources/source.py) | Python | -11 | -5 | -8 | -24 | -| [src/music_kraken/not_used_anymore/sources/youtube.py](/src/music_kraken/not_used_anymore/sources/youtube.py) | Python | -71 | -4 | -24 | -99 | -| [src/music_kraken/objects/metadata.py](/src/music_kraken/objects/metadata.py) | Python | 11 | 0 | 2 | 13 | -| [src/music_kraken/objects/parents.py](/src/music_kraken/objects/parents.py) | Python | 6 | 2 | 2 | 10 | -| [src/music_kraken/objects/song.py](/src/music_kraken/objects/song.py) | Python | 7 | 0 | 5 | 12 | -| [src/music_kraken/objects/source.py](/src/music_kraken/objects/source.py) | Python | 3 | 0 | 1 | 4 | -| [src/music_kraken/objects/target.py](/src/music_kraken/objects/target.py) | Python | 2 | 0 | 1 | 3 | -| [src/music_kraken/pages/__init__.py](/src/music_kraken/pages/__init__.py) | Python | 1 | 0 | 1 | 2 | -| [src/music_kraken/pages/abstract.py](/src/music_kraken/pages/abstract.py) | Python | -123 | 12 | -10 | -121 | -| [src/music_kraken/pages/musify.py](/src/music_kraken/pages/musify.py) | Python | -53 | -14 | -21 | -88 | -| [src/music_kraken/pages/preset.py](/src/music_kraken/pages/preset.py) | Python | 4 | 0 | 1 | 5 | -| [src/music_kraken/pages/youtube.py](/src/music_kraken/pages/youtube.py) | Python | 229 | 29 | 73 | 331 | -| [src/music_kraken/utils/config/connection.py](/src/music_kraken/utils/config/connection.py) | Python | -1 | 0 | 0 | -1 | -| [src/music_kraken/utils/exception/download.py](/src/music_kraken/utils/exception/download.py) | Python | 8 | 0 | 4 | 12 | -| [src/music_kraken/utils/shared.py](/src/music_kraken/utils/shared.py) | Python | 3 | 0 | 1 | 4 | -| [src/music_kraken/utils/support_classes/__init__.py](/src/music_kraken/utils/support_classes/__init__.py) | Python | 1 | 0 | 0 | 1 | -| [src/music_kraken/utils/support_classes/thread_classes.py](/src/music_kraken/utils/support_classes/thread_classes.py) | Python | 8 | 0 | 4 | 12 | - -[Summary](results.md) / [Details](details.md) / [Diff Summary](diff.md) / Diff Details \ No newline at end of file diff --git a/.VSCodeCounter/2023-06-14_07-47-51/diff.csv b/.VSCodeCounter/2023-06-14_07-47-51/diff.csv deleted file mode 100644 index d001392..0000000 --- a/.VSCodeCounter/2023-06-14_07-47-51/diff.csv +++ /dev/null @@ -1,46 +0,0 @@ -"filename", "language", "Python", "comment", "blank", "total" -"/home/lars/Projects/music-downloader/src/actual_donwload.py", "Python", -27, -2, -12, -41 -"/home/lars/Projects/music-downloader/src/music_kraken/__init__.py", "Python", -77, -15, -27, -119 -"/home/lars/Projects/music-downloader/src/music_kraken/__main__.py", "Python", 10, 0, 2, 12 -"/home/lars/Projects/music-downloader/src/music_kraken/cli/__init__.py", "Python", 2, 0, 0, 2 -"/home/lars/Projects/music-downloader/src/music_kraken/cli/download/__init__.py", "Python", 0, 0, 1, 1 -"/home/lars/Projects/music-downloader/src/music_kraken/cli/download/shell.py", "Python", 199, 86, 78, 363 -"/home/lars/Projects/music-downloader/src/music_kraken/cli/options/__init__.py", "Python", 3, 0, 1, 4 -"/home/lars/Projects/music-downloader/src/music_kraken/cli/options/invidious/__init__.py", "Python", 0, 0, 1, 1 -"/home/lars/Projects/music-downloader/src/music_kraken/cli/options/invidious/shell.py", "Python", 66, 7, 28, 101 -"/home/lars/Projects/music-downloader/src/music_kraken/connection/connection.py", "Python", 6, 0, 0, 6 -"/home/lars/Projects/music-downloader/src/music_kraken/download/__init__.py", "Python", -1, 0, 0, -1 -"/home/lars/Projects/music-downloader/src/music_kraken/download/download.py", "Python", -35, 0, -14, -49 -"/home/lars/Projects/music-downloader/src/music_kraken/download/page_attributes.py", "Python", 46, 0, 19, 65 -"/home/lars/Projects/music-downloader/src/music_kraken/download/results.py", "Python", 62, 7, 26, 95 -"/home/lars/Projects/music-downloader/src/music_kraken/download/search.py", "Python", -6, 0, 0, -6 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/__init__.py", "Python", 0, 0, -3, -3 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/fetch_audio.py", "Python", -75, -12, -20, -107 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/fetch_source.py", "Python", -54, -1, -16, -71 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/__init__.py", "Python", -6, 0, -2, -8 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/metadata_fetch.py", "Python", -257, -24, -65, -346 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/metadata_search.py", "Python", -253, -40, -72, -365 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/sources/__init__.py", "Python", -3, 0, -2, -5 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/sources/musicbrainz.py", "Python", -42, -6, -12, -60 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/__init__.py", "Python", 0, 0, -1, -1 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/genius.py", "Python", -115, -16, -42, -173 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/local_files.py", "Python", -40, 0, -18, -58 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/musify.py", "Python", -136, -9, -37, -182 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/source.py", "Python", -11, -5, -8, -24 -"/home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/youtube.py", "Python", -71, -4, -24, -99 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/metadata.py", "Python", 11, 0, 2, 13 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/parents.py", "Python", 6, 2, 2, 10 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/song.py", "Python", 7, 0, 5, 12 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/source.py", "Python", 3, 0, 1, 4 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/target.py", "Python", 2, 0, 1, 3 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/__init__.py", "Python", 1, 0, 1, 2 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/abstract.py", "Python", -123, 12, -10, -121 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/musify.py", "Python", -53, -14, -21, -88 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/preset.py", "Python", 4, 0, 1, 5 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/youtube.py", "Python", 229, 29, 73, 331 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/config/connection.py", "Python", -1, 0, 0, -1 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/exception/download.py", "Python", 8, 0, 4, 12 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/shared.py", "Python", 3, 0, 1, 4 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/support_classes/__init__.py", "Python", 1, 0, 0, 1 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/support_classes/thread_classes.py", "Python", 8, 0, 4, 12 -"Total", "-", -709, -5, -155, -869 \ No newline at end of file diff --git a/.VSCodeCounter/2023-06-14_07-47-51/diff.md b/.VSCodeCounter/2023-06-14_07-47-51/diff.md deleted file mode 100644 index f9c1a87..0000000 --- a/.VSCodeCounter/2023-06-14_07-47-51/diff.md +++ /dev/null @@ -1,45 +0,0 @@ -# Diff Summary - -Date : 2023-06-14 07:47:51 - -Directory /home/lars/Projects/music-downloader/src - -Total : 44 files, -709 codes, -5 comments, -155 blanks, all -869 lines - -[Summary](results.md) / [Details](details.md) / Diff Summary / [Diff Details](diff-details.md) - -## Languages -| language | files | code | comment | blank | total | -| :--- | ---: | ---: | ---: | ---: | ---: | -| Python | 44 | -709 | -5 | -155 | -869 | - -## Directories -| path | files | code | comment | blank | total | -| :--- | ---: | ---: | ---: | ---: | ---: | -| . | 44 | -709 | -5 | -155 | -869 | -| . (Files) | 1 | -27 | -2 | -12 | -41 | -| music_kraken | 43 | -682 | -3 | -143 | -828 | -| music_kraken (Files) | 2 | -67 | -15 | -25 | -107 | -| music_kraken/cli | 6 | 270 | 93 | 109 | 472 | -| music_kraken/cli (Files) | 1 | 2 | 0 | 0 | 2 | -| music_kraken/cli/download | 2 | 199 | 86 | 79 | 364 | -| music_kraken/cli/options | 3 | 69 | 7 | 30 | 106 | -| music_kraken/cli/options (Files) | 1 | 3 | 0 | 1 | 4 | -| music_kraken/cli/options/invidious | 2 | 66 | 7 | 29 | 102 | -| music_kraken/connection | 1 | 6 | 0 | 0 | 6 | -| music_kraken/download | 5 | 66 | 7 | 31 | 104 | -| music_kraken/not_used_anymore | 14 | -1,063 | -117 | -322 | -1,502 | -| music_kraken/not_used_anymore (Files) | 3 | -129 | -13 | -39 | -181 | -| music_kraken/not_used_anymore/metadata | 5 | -561 | -70 | -153 | -784 | -| music_kraken/not_used_anymore/metadata (Files) | 3 | -516 | -64 | -139 | -719 | -| music_kraken/not_used_anymore/metadata/sources | 2 | -45 | -6 | -14 | -65 | -| music_kraken/not_used_anymore/sources | 6 | -373 | -34 | -130 | -537 | -| music_kraken/objects | 5 | 29 | 2 | 11 | 42 | -| music_kraken/pages | 5 | 58 | 27 | 44 | 129 | -| music_kraken/utils | 5 | 19 | 0 | 9 | 28 | -| music_kraken/utils (Files) | 1 | 3 | 0 | 1 | 4 | -| music_kraken/utils/config | 1 | -1 | 0 | 0 | -1 | -| music_kraken/utils/exception | 1 | 8 | 0 | 4 | 12 | -| music_kraken/utils/support_classes | 2 | 9 | 0 | 4 | 13 | - -[Summary](results.md) / [Details](details.md) / Diff Summary / [Diff Details](diff-details.md) \ No newline at end of file diff --git a/.VSCodeCounter/2023-06-14_07-47-51/diff.txt b/.VSCodeCounter/2023-06-14_07-47-51/diff.txt deleted file mode 100644 index f87b711..0000000 --- a/.VSCodeCounter/2023-06-14_07-47-51/diff.txt +++ /dev/null @@ -1,92 +0,0 @@ -Date : 2023-06-14 07:47:51 -Directory : /home/lars/Projects/music-downloader/src -Total : 44 files, -709 codes, -5 comments, -155 blanks, all -869 lines - -Languages -+----------+------------+------------+------------+------------+------------+ -| language | files | code | comment | blank | total | -+----------+------------+------------+------------+------------+------------+ -| Python | 44 | -709 | -5 | -155 | -869 | -+----------+------------+------------+------------+------------+------------+ - -Directories -+--------------------------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ -| path | files | code | comment | blank | total | -+--------------------------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ -| . | 44 | -709 | -5 | -155 | -869 | -| . (Files) | 1 | -27 | -2 | -12 | -41 | -| music_kraken | 43 | -682 | -3 | -143 | -828 | -| music_kraken (Files) | 2 | -67 | -15 | -25 | -107 | -| music_kraken/cli | 6 | 270 | 93 | 109 | 472 | -| music_kraken/cli (Files) | 1 | 2 | 0 | 0 | 2 | -| music_kraken/cli/download | 2 | 199 | 86 | 79 | 364 | -| music_kraken/cli/options | 3 | 69 | 7 | 30 | 106 | -| music_kraken/cli/options (Files) | 1 | 3 | 0 | 1 | 4 | -| music_kraken/cli/options/invidious | 2 | 66 | 7 | 29 | 102 | -| music_kraken/connection | 1 | 6 | 0 | 0 | 6 | -| music_kraken/download | 5 | 66 | 7 | 31 | 104 | -| music_kraken/not_used_anymore | 14 | -1,063 | -117 | -322 | -1,502 | -| music_kraken/not_used_anymore (Files) | 3 | -129 | -13 | -39 | -181 | -| music_kraken/not_used_anymore/metadata | 5 | -561 | -70 | -153 | -784 | -| music_kraken/not_used_anymore/metadata (Files) | 3 | -516 | -64 | -139 | -719 | -| music_kraken/not_used_anymore/metadata/sources | 2 | -45 | -6 | -14 | -65 | -| music_kraken/not_used_anymore/sources | 6 | -373 | -34 | -130 | -537 | -| music_kraken/objects | 5 | 29 | 2 | 11 | 42 | -| music_kraken/pages | 5 | 58 | 27 | 44 | 129 | -| music_kraken/utils | 5 | 19 | 0 | 9 | 28 | -| music_kraken/utils (Files) | 1 | 3 | 0 | 1 | 4 | -| music_kraken/utils/config | 1 | -1 | 0 | 0 | -1 | -| music_kraken/utils/exception | 1 | 8 | 0 | 4 | 12 | -| music_kraken/utils/support_classes | 2 | 9 | 0 | 4 | 13 | -+--------------------------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ - -Files -+--------------------------------------------------------------------------------------------------------+----------+------------+------------+------------+------------+ -| filename | language | code | comment | blank | total | -+--------------------------------------------------------------------------------------------------------+----------+------------+------------+------------+------------+ -| /home/lars/Projects/music-downloader/src/actual_donwload.py | Python | -27 | -2 | -12 | -41 | -| /home/lars/Projects/music-downloader/src/music_kraken/__init__.py | Python | -77 | -15 | -27 | -119 | -| /home/lars/Projects/music-downloader/src/music_kraken/__main__.py | Python | 10 | 0 | 2 | 12 | -| /home/lars/Projects/music-downloader/src/music_kraken/cli/__init__.py | Python | 2 | 0 | 0 | 2 | -| /home/lars/Projects/music-downloader/src/music_kraken/cli/download/__init__.py | Python | 0 | 0 | 1 | 1 | -| /home/lars/Projects/music-downloader/src/music_kraken/cli/download/shell.py | Python | 199 | 86 | 78 | 363 | -| /home/lars/Projects/music-downloader/src/music_kraken/cli/options/__init__.py | Python | 3 | 0 | 1 | 4 | -| /home/lars/Projects/music-downloader/src/music_kraken/cli/options/invidious/__init__.py | Python | 0 | 0 | 1 | 1 | -| /home/lars/Projects/music-downloader/src/music_kraken/cli/options/invidious/shell.py | Python | 66 | 7 | 28 | 101 | -| /home/lars/Projects/music-downloader/src/music_kraken/connection/connection.py | Python | 6 | 0 | 0 | 6 | -| /home/lars/Projects/music-downloader/src/music_kraken/download/__init__.py | Python | -1 | 0 | 0 | -1 | -| /home/lars/Projects/music-downloader/src/music_kraken/download/download.py | Python | -35 | 0 | -14 | -49 | -| /home/lars/Projects/music-downloader/src/music_kraken/download/page_attributes.py | Python | 46 | 0 | 19 | 65 | -| /home/lars/Projects/music-downloader/src/music_kraken/download/results.py | Python | 62 | 7 | 26 | 95 | -| /home/lars/Projects/music-downloader/src/music_kraken/download/search.py | Python | -6 | 0 | 0 | -6 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/__init__.py | Python | 0 | 0 | -3 | -3 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/fetch_audio.py | Python | -75 | -12 | -20 | -107 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/fetch_source.py | Python | -54 | -1 | -16 | -71 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/__init__.py | Python | -6 | 0 | -2 | -8 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/metadata_fetch.py | Python | -257 | -24 | -65 | -346 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/metadata_search.py | Python | -253 | -40 | -72 | -365 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/sources/__init__.py | Python | -3 | 0 | -2 | -5 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/metadata/sources/musicbrainz.py | Python | -42 | -6 | -12 | -60 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/__init__.py | Python | 0 | 0 | -1 | -1 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/genius.py | Python | -115 | -16 | -42 | -173 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/local_files.py | Python | -40 | 0 | -18 | -58 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/musify.py | Python | -136 | -9 | -37 | -182 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/source.py | Python | -11 | -5 | -8 | -24 | -| /home/lars/Projects/music-downloader/src/music_kraken/not_used_anymore/sources/youtube.py | Python | -71 | -4 | -24 | -99 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/metadata.py | Python | 11 | 0 | 2 | 13 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/parents.py | Python | 6 | 2 | 2 | 10 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/song.py | Python | 7 | 0 | 5 | 12 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/source.py | Python | 3 | 0 | 1 | 4 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/target.py | Python | 2 | 0 | 1 | 3 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/__init__.py | Python | 1 | 0 | 1 | 2 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/abstract.py | Python | -123 | 12 | -10 | -121 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/musify.py | Python | -53 | -14 | -21 | -88 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/preset.py | Python | 4 | 0 | 1 | 5 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/youtube.py | Python | 229 | 29 | 73 | 331 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/config/connection.py | Python | -1 | 0 | 0 | -1 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/exception/download.py | Python | 8 | 0 | 4 | 12 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/shared.py | Python | 3 | 0 | 1 | 4 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/support_classes/__init__.py | Python | 1 | 0 | 0 | 1 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/support_classes/thread_classes.py | Python | 8 | 0 | 4 | 12 | -| Total | | -709 | -5 | -155 | -869 | -+--------------------------------------------------------------------------------------------------------+----------+------------+------------+------------+------------+ \ No newline at end of file diff --git a/.VSCodeCounter/2023-06-14_07-47-51/results.csv b/.VSCodeCounter/2023-06-14_07-47-51/results.csv deleted file mode 100644 index 0d100fb..0000000 --- a/.VSCodeCounter/2023-06-14_07-47-51/results.csv +++ /dev/null @@ -1,85 +0,0 @@ -"filename", "language", "Python", "SQLite", "comment", "blank", "total" -"/home/lars/Projects/music-downloader/src/__init__.py", "Python", 0, 0, 0, 1, 1 -"/home/lars/Projects/music-downloader/src/actual_donwload.py", "Python", 16, 0, 0, 6, 22 -"/home/lars/Projects/music-downloader/src/create_custom_objects.py", "Python", 58, 0, 0, 6, 64 -"/home/lars/Projects/music-downloader/src/metal_archives.py", "Python", 30, 0, 0, 12, 42 -"/home/lars/Projects/music-downloader/src/music_kraken/__init__.py", "Python", 112, 0, 6, 33, 151 -"/home/lars/Projects/music-downloader/src/music_kraken/__main__.py", "Python", 98, 0, 3, 23, 124 -"/home/lars/Projects/music-downloader/src/music_kraken/audio/__init__.py", "Python", 7, 0, 0, 3, 10 -"/home/lars/Projects/music-downloader/src/music_kraken/audio/codec.py", "Python", 25, 0, 0, 8, 33 -"/home/lars/Projects/music-downloader/src/music_kraken/audio/metadata.py", "Python", 60, 0, 4, 24, 88 -"/home/lars/Projects/music-downloader/src/music_kraken/cli/__init__.py", "Python", 2, 0, 0, 0, 2 -"/home/lars/Projects/music-downloader/src/music_kraken/cli/download/__init__.py", "Python", 0, 0, 0, 1, 1 -"/home/lars/Projects/music-downloader/src/music_kraken/cli/download/shell.py", "Python", 199, 0, 86, 78, 363 -"/home/lars/Projects/music-downloader/src/music_kraken/cli/options/__init__.py", "Python", 3, 0, 0, 1, 4 -"/home/lars/Projects/music-downloader/src/music_kraken/cli/options/invidious/__init__.py", "Python", 0, 0, 0, 1, 1 -"/home/lars/Projects/music-downloader/src/music_kraken/cli/options/invidious/shell.py", "Python", 66, 0, 7, 28, 101 -"/home/lars/Projects/music-downloader/src/music_kraken/connection/__init__.py", "Python", 1, 0, 0, 1, 2 -"/home/lars/Projects/music-downloader/src/music_kraken/connection/connection.py", "Python", 168, 0, 1, 30, 199 -"/home/lars/Projects/music-downloader/src/music_kraken/connection/rotating.py", "Python", 27, 0, 3, 14, 44 -"/home/lars/Projects/music-downloader/src/music_kraken/database/__init__.py", "Python", 0, 0, 0, 1, 1 -"/home/lars/Projects/music-downloader/src/music_kraken/database/data_models.py", "Python", 122, 0, 24, 52, 198 -"/home/lars/Projects/music-downloader/src/music_kraken/database/database.py", "Python", 104, 0, 47, 38, 189 -"/home/lars/Projects/music-downloader/src/music_kraken/download/__init__.py", "Python", 1, 0, 0, 1, 2 -"/home/lars/Projects/music-downloader/src/music_kraken/download/multiple_options.py", "Python", 69, 0, 0, 31, 100 -"/home/lars/Projects/music-downloader/src/music_kraken/download/page_attributes.py", "Python", 67, 0, 1, 29, 97 -"/home/lars/Projects/music-downloader/src/music_kraken/download/results.py", "Python", 62, 0, 7, 26, 95 -"/home/lars/Projects/music-downloader/src/music_kraken/download/search.py", "Python", 124, 0, 24, 56, 204 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/__init__.py", "Python", 14, 0, 0, 5, 19 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/cache.py", "Python", 37, 0, 56, 18, 111 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/collection.py", "Python", 91, 0, 31, 39, 161 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/formatted_text.py", "Python", 50, 0, 10, 19, 79 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/lyrics.py", "Python", 25, 0, 0, 7, 32 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/metadata.py", "Python", 272, 0, 62, 63, 397 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/option.py", "Python", 28, 0, 0, 13, 41 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/parents.py", "Python", 71, 0, 35, 35, 141 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/song.py", "Python", 480, 0, 113, 120, 713 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/source.py", "Python", 93, 0, 16, 33, 142 -"/home/lars/Projects/music-downloader/src/music_kraken/objects/target.py", "Python", 65, 0, 15, 24, 104 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/__init__.py", "Python", 4, 0, 0, 2, 6 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/abstract.py", "Python", 234, 0, 46, 93, 373 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/encyclopaedia_metallum.py", "Python", 432, 0, 90, 127, 649 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/musify.py", "Python", 576, 0, 275, 166, 1017 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/preset.py", "Python", 47, 0, 1, 17, 65 -"/home/lars/Projects/music-downloader/src/music_kraken/pages/youtube.py", "Python", 254, 0, 45, 79, 378 -"/home/lars/Projects/music-downloader/src/music_kraken/static_files/new_db.sql", "SQLite", 0, 72, 0, 10, 82 -"/home/lars/Projects/music-downloader/src/music_kraken/static_files/temp_database_structure.sql", "SQLite", 0, 135, 0, 10, 145 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/__init__.py", "Python", 2, 0, 1, 2, 5 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/config/__init__.py", "Python", 7, 0, 0, 4, 11 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/config/audio.py", "Python", 152, 0, 15, 28, 195 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/config/base_classes.py", "Python", 136, 0, 35, 61, 232 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/config/config.py", "Python", 92, 0, 16, 30, 138 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/config/connection.py", "Python", 80, 0, 2, 15, 97 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/config/logging.py", "Python", 104, 0, 4, 17, 125 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/config/misc.py", "Python", 40, 0, 0, 9, 49 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/config/paths.py", "Python", 40, 0, 0, 13, 53 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/enums/__init__.py", "Python", 0, 0, 0, 1, 1 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/enums/album.py", "Python", 16, 0, 6, 5, 27 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/enums/source.py", "Python", 40, 0, 1, 8, 49 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/exception/__init__.py", "Python", 1, 0, 0, 1, 2 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/exception/config.py", "Python", 14, 0, 8, 7, 29 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/exception/download.py", "Python", 8, 0, 0, 4, 12 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/functions.py", "Python", 3, 0, 0, 1, 4 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/object_handeling.py", "Python", 19, 0, 0, 6, 25 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/path_manager/__init__.py", "Python", 2, 0, 0, 2, 4 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/path_manager/config_directory.py", "Python", 4, 0, 0, 4, 8 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/path_manager/locations.py", "Python", 16, 0, 0, 9, 25 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/path_manager/music_directory.py", "Python", 36, 0, 9, 14, 59 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/phonetic_compares.py", "Python", 39, 0, 2, 17, 58 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/regex.py", "Python", 1, 0, 0, 2, 3 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/shared.py", "Python", 66, 0, 22, 22, 110 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/string_processing.py", "Python", 16, 0, 5, 11, 32 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/support_classes/__init__.py", "Python", 4, 0, 0, 1, 5 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/support_classes/default_target.py", "Python", 56, 0, 0, 15, 71 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/support_classes/download_result.py", "Python", 69, 0, 0, 21, 90 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/support_classes/query.py", "Python", 24, 0, 0, 9, 33 -"/home/lars/Projects/music-downloader/src/music_kraken/utils/support_classes/thread_classes.py", "Python", 8, 0, 0, 4, 12 -"/home/lars/Projects/music-downloader/src/music_kraken_cli.py", "Python", 3, 0, 0, 3, 6 -"/home/lars/Projects/music-downloader/src/music_kraken_gtk.py", "Python", 3, 0, 0, 2, 5 -"/home/lars/Projects/music-downloader/src/musify_search.py", "Python", 38, 0, 0, 14, 52 -"/home/lars/Projects/music-downloader/src/tests/__init__.py", "Python", 0, 0, 0, 1, 1 -"/home/lars/Projects/music-downloader/src/tests/conftest.py", "Python", 3, 0, 1, 2, 6 -"/home/lars/Projects/music-downloader/src/tests/test_building_objects.py", "Python", 81, 0, 1, 13, 95 -"/home/lars/Projects/music-downloader/src/tests/test_download.py", "Python", 30, 0, 1, 12, 43 -"/home/lars/Projects/music-downloader/src/tests/test_objects.py", "Python", 173, 0, 15, 51, 239 -"Total", "-", 5620, 207, 1152, 1825, 8804 \ No newline at end of file diff --git a/.VSCodeCounter/2023-06-14_07-47-51/results.json b/.VSCodeCounter/2023-06-14_07-47-51/results.json deleted file mode 100644 index 283137c..0000000 --- a/.VSCodeCounter/2023-06-14_07-47-51/results.json +++ /dev/null @@ -1 +0,0 @@ -{"file:///home/lars/Projects/music-downloader/src/__init__.py":{"language":"Python","code":0,"comment":0,"blank":1},"file:///home/lars/Projects/music-downloader/src/music_kraken/__init__.py":{"language":"Python","code":112,"comment":6,"blank":33},"file:///home/lars/Projects/music-downloader/src/music_kraken/download/search.py":{"language":"Python","code":124,"comment":24,"blank":56},"file:///home/lars/Projects/music-downloader/src/music_kraken/download/page_attributes.py":{"language":"Python","code":67,"comment":1,"blank":29},"file:///home/lars/Projects/music-downloader/src/music_kraken/download/__init__.py":{"language":"Python","code":1,"comment":0,"blank":1},"file:///home/lars/Projects/music-downloader/src/music_kraken/download/results.py":{"language":"Python","code":62,"comment":7,"blank":26},"file:///home/lars/Projects/music-downloader/src/music_kraken/download/multiple_options.py":{"language":"Python","code":69,"comment":0,"blank":31},"file:///home/lars/Projects/music-downloader/src/music_kraken/audio/__init__.py":{"language":"Python","code":7,"comment":0,"blank":3},"file:///home/lars/Projects/music-downloader/src/music_kraken/database/__init__.py":{"language":"Python","code":0,"comment":0,"blank":1},"file:///home/lars/Projects/music-downloader/src/music_kraken/audio/codec.py":{"language":"Python","code":25,"comment":0,"blank":8},"file:///home/lars/Projects/music-downloader/src/music_kraken/audio/metadata.py":{"language":"Python","code":60,"comment":4,"blank":24},"file:///home/lars/Projects/music-downloader/src/music_kraken/database/database.py":{"language":"Python","code":104,"comment":47,"blank":38},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/__init__.py":{"language":"Python","code":2,"comment":1,"blank":2},"file:///home/lars/Projects/music-downloader/src/music_kraken/database/data_models.py":{"language":"Python","code":122,"comment":24,"blank":52},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/exception/__init__.py":{"language":"Python","code":1,"comment":0,"blank":1},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/exception/config.py":{"language":"Python","code":14,"comment":8,"blank":7},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/exception/download.py":{"language":"Python","code":8,"comment":0,"blank":4},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/enums/__init__.py":{"language":"Python","code":0,"comment":0,"blank":1},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/string_processing.py":{"language":"Python","code":16,"comment":5,"blank":11},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/regex.py":{"language":"Python","code":1,"comment":0,"blank":2},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/object_handeling.py":{"language":"Python","code":19,"comment":0,"blank":6},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/enums/album.py":{"language":"Python","code":16,"comment":6,"blank":5},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/enums/source.py":{"language":"Python","code":40,"comment":1,"blank":8},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/phonetic_compares.py":{"language":"Python","code":39,"comment":2,"blank":17},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/support_classes/__init__.py":{"language":"Python","code":4,"comment":0,"blank":1},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/support_classes/thread_classes.py":{"language":"Python","code":8,"comment":0,"blank":4},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/support_classes/default_target.py":{"language":"Python","code":56,"comment":0,"blank":15},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/support_classes/query.py":{"language":"Python","code":24,"comment":0,"blank":9},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/support_classes/download_result.py":{"language":"Python","code":69,"comment":0,"blank":21},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/config/paths.py":{"language":"Python","code":40,"comment":0,"blank":13},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/config/__init__.py":{"language":"Python","code":7,"comment":0,"blank":4},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/config/connection.py":{"language":"Python","code":80,"comment":2,"blank":15},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/config/logging.py":{"language":"Python","code":104,"comment":4,"blank":17},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/config/config.py":{"language":"Python","code":92,"comment":16,"blank":30},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/config/misc.py":{"language":"Python","code":40,"comment":0,"blank":9},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/config/base_classes.py":{"language":"Python","code":136,"comment":35,"blank":61},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/config/audio.py":{"language":"Python","code":152,"comment":15,"blank":28},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/shared.py":{"language":"Python","code":66,"comment":22,"blank":22},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/functions.py":{"language":"Python","code":3,"comment":0,"blank":1},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/path_manager/__init__.py":{"language":"Python","code":2,"comment":0,"blank":2},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/path_manager/music_directory.py":{"language":"Python","code":36,"comment":9,"blank":14},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/path_manager/locations.py":{"language":"Python","code":16,"comment":0,"blank":9},"file:///home/lars/Projects/music-downloader/src/music_kraken/pages/youtube.py":{"language":"Python","code":254,"comment":45,"blank":79},"file:///home/lars/Projects/music-downloader/src/music_kraken/pages/__init__.py":{"language":"Python","code":4,"comment":0,"blank":2},"file:///home/lars/Projects/music-downloader/src/music_kraken/pages/preset.py":{"language":"Python","code":47,"comment":1,"blank":17},"file:///home/lars/Projects/music-downloader/src/music_kraken/pages/abstract.py":{"language":"Python","code":234,"comment":46,"blank":93},"file:///home/lars/Projects/music-downloader/src/music_kraken/pages/encyclopaedia_metallum.py":{"language":"Python","code":432,"comment":90,"blank":127},"file:///home/lars/Projects/music-downloader/src/music_kraken/pages/musify.py":{"language":"Python","code":576,"comment":275,"blank":166},"file:///home/lars/Projects/music-downloader/src/music_kraken/static_files/temp_database_structure.sql":{"language":"SQLite","code":135,"comment":0,"blank":10},"file:///home/lars/Projects/music-downloader/src/music_kraken/connection/connection.py":{"language":"Python","code":168,"comment":1,"blank":30},"file:///home/lars/Projects/music-downloader/src/music_kraken/static_files/new_db.sql":{"language":"SQLite","code":72,"comment":0,"blank":10},"file:///home/lars/Projects/music-downloader/src/music_kraken/connection/rotating.py":{"language":"Python","code":27,"comment":3,"blank":14},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/__init__.py":{"language":"Python","code":14,"comment":0,"blank":5},"file:///home/lars/Projects/music-downloader/src/music_kraken/connection/__init__.py":{"language":"Python","code":1,"comment":0,"blank":1},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/cache.py":{"language":"Python","code":37,"comment":56,"blank":18},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/song.py":{"language":"Python","code":480,"comment":113,"blank":120},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/metadata.py":{"language":"Python","code":272,"comment":62,"blank":63},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/source.py":{"language":"Python","code":93,"comment":16,"blank":33},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/parents.py":{"language":"Python","code":71,"comment":35,"blank":35},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/target.py":{"language":"Python","code":65,"comment":15,"blank":24},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/option.py":{"language":"Python","code":28,"comment":0,"blank":13},"file:///home/lars/Projects/music-downloader/src/music_kraken/__main__.py":{"language":"Python","code":98,"comment":3,"blank":23},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/collection.py":{"language":"Python","code":91,"comment":31,"blank":39},"file:///home/lars/Projects/music-downloader/src/music_kraken/cli/download/__init__.py":{"language":"Python","code":0,"comment":0,"blank":1},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/lyrics.py":{"language":"Python","code":25,"comment":0,"blank":7},"file:///home/lars/Projects/music-downloader/src/music_kraken/cli/options/__init__.py":{"language":"Python","code":3,"comment":0,"blank":1},"file:///home/lars/Projects/music-downloader/src/music_kraken/cli/options/invidious/__init__.py":{"language":"Python","code":0,"comment":0,"blank":1},"file:///home/lars/Projects/music-downloader/src/music_kraken/objects/formatted_text.py":{"language":"Python","code":50,"comment":10,"blank":19},"file:///home/lars/Projects/music-downloader/src/music_kraken/cli/__init__.py":{"language":"Python","code":2,"comment":0,"blank":0},"file:///home/lars/Projects/music-downloader/src/music_kraken/cli/download/shell.py":{"language":"Python","code":199,"comment":86,"blank":78},"file:///home/lars/Projects/music-downloader/src/music_kraken/cli/options/invidious/shell.py":{"language":"Python","code":66,"comment":7,"blank":28},"file:///home/lars/Projects/music-downloader/src/tests/__init__.py":{"language":"Python","code":0,"comment":0,"blank":1},"file:///home/lars/Projects/music-downloader/src/metal_archives.py":{"language":"Python","code":30,"comment":0,"blank":12},"file:///home/lars/Projects/music-downloader/src/tests/test_objects.py":{"language":"Python","code":173,"comment":15,"blank":51},"file:///home/lars/Projects/music-downloader/src/create_custom_objects.py":{"language":"Python","code":58,"comment":0,"blank":6},"file:///home/lars/Projects/music-downloader/src/tests/conftest.py":{"language":"Python","code":3,"comment":1,"blank":2},"file:///home/lars/Projects/music-downloader/src/tests/test_download.py":{"language":"Python","code":30,"comment":1,"blank":12},"file:///home/lars/Projects/music-downloader/src/music_kraken_cli.py":{"language":"Python","code":3,"comment":0,"blank":3},"file:///home/lars/Projects/music-downloader/src/actual_donwload.py":{"language":"Python","code":16,"comment":0,"blank":6},"file:///home/lars/Projects/music-downloader/src/music_kraken_gtk.py":{"language":"Python","code":3,"comment":0,"blank":2},"file:///home/lars/Projects/music-downloader/src/tests/test_building_objects.py":{"language":"Python","code":81,"comment":1,"blank":13},"file:///home/lars/Projects/music-downloader/src/musify_search.py":{"language":"Python","code":38,"comment":0,"blank":14},"file:///home/lars/Projects/music-downloader/src/music_kraken/utils/path_manager/config_directory.py":{"language":"Python","code":4,"comment":0,"blank":4}} \ No newline at end of file diff --git a/.VSCodeCounter/2023-06-14_07-47-51/results.md b/.VSCodeCounter/2023-06-14_07-47-51/results.md deleted file mode 100644 index 2bbd90d..0000000 --- a/.VSCodeCounter/2023-06-14_07-47-51/results.md +++ /dev/null @@ -1,46 +0,0 @@ -# Summary - -Date : 2023-06-14 07:47:51 - -Directory /home/lars/Projects/music-downloader/src - -Total : 83 files, 5827 codes, 1152 comments, 1825 blanks, all 8804 lines - -Summary / [Details](details.md) / [Diff Summary](diff.md) / [Diff Details](diff-details.md) - -## Languages -| language | files | code | comment | blank | total | -| :--- | ---: | ---: | ---: | ---: | ---: | -| Python | 81 | 5,620 | 1,152 | 1,805 | 8,577 | -| SQLite | 2 | 207 | 0 | 20 | 227 | - -## Directories -| path | files | code | comment | blank | total | -| :--- | ---: | ---: | ---: | ---: | ---: | -| . | 83 | 5,827 | 1,152 | 1,825 | 8,804 | -| . (Files) | 7 | 148 | 0 | 44 | 192 | -| music_kraken | 71 | 5,392 | 1,134 | 1,702 | 8,228 | -| music_kraken (Files) | 2 | 210 | 9 | 56 | 275 | -| music_kraken/audio | 3 | 92 | 4 | 35 | 131 | -| music_kraken/cli | 6 | 270 | 93 | 109 | 472 | -| music_kraken/cli (Files) | 1 | 2 | 0 | 0 | 2 | -| music_kraken/cli/download | 2 | 199 | 86 | 79 | 364 | -| music_kraken/cli/options | 3 | 69 | 7 | 30 | 106 | -| music_kraken/cli/options (Files) | 1 | 3 | 0 | 1 | 4 | -| music_kraken/cli/options/invidious | 2 | 66 | 7 | 29 | 102 | -| music_kraken/connection | 3 | 196 | 4 | 45 | 245 | -| music_kraken/database | 3 | 226 | 71 | 91 | 388 | -| music_kraken/download | 5 | 323 | 32 | 143 | 498 | -| music_kraken/objects | 11 | 1,226 | 338 | 376 | 1,940 | -| music_kraken/pages | 6 | 1,547 | 457 | 484 | 2,488 | -| music_kraken/static_files | 2 | 207 | 0 | 20 | 227 | -| music_kraken/utils | 30 | 1,095 | 126 | 343 | 1,564 | -| music_kraken/utils (Files) | 7 | 146 | 30 | 61 | 237 | -| music_kraken/utils/config | 8 | 651 | 72 | 177 | 900 | -| music_kraken/utils/enums | 3 | 56 | 7 | 14 | 77 | -| music_kraken/utils/exception | 3 | 23 | 8 | 12 | 43 | -| music_kraken/utils/path_manager | 4 | 58 | 9 | 29 | 96 | -| music_kraken/utils/support_classes | 5 | 161 | 0 | 50 | 211 | -| tests | 5 | 287 | 18 | 79 | 384 | - -Summary / [Details](details.md) / [Diff Summary](diff.md) / [Diff Details](diff-details.md) \ No newline at end of file diff --git a/.VSCodeCounter/2023-06-14_07-47-51/results.txt b/.VSCodeCounter/2023-06-14_07-47-51/results.txt deleted file mode 100644 index 530258b..0000000 --- a/.VSCodeCounter/2023-06-14_07-47-51/results.txt +++ /dev/null @@ -1,132 +0,0 @@ -Date : 2023-06-14 07:47:51 -Directory : /home/lars/Projects/music-downloader/src -Total : 83 files, 5827 codes, 1152 comments, 1825 blanks, all 8804 lines - -Languages -+----------+------------+------------+------------+------------+------------+ -| language | files | code | comment | blank | total | -+----------+------------+------------+------------+------------+------------+ -| Python | 81 | 5,620 | 1,152 | 1,805 | 8,577 | -| SQLite | 2 | 207 | 0 | 20 | 227 | -+----------+------------+------------+------------+------------+------------+ - -Directories -+------------------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ -| path | files | code | comment | blank | total | -+------------------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ -| . | 83 | 5,827 | 1,152 | 1,825 | 8,804 | -| . (Files) | 7 | 148 | 0 | 44 | 192 | -| music_kraken | 71 | 5,392 | 1,134 | 1,702 | 8,228 | -| music_kraken (Files) | 2 | 210 | 9 | 56 | 275 | -| music_kraken/audio | 3 | 92 | 4 | 35 | 131 | -| music_kraken/cli | 6 | 270 | 93 | 109 | 472 | -| music_kraken/cli (Files) | 1 | 2 | 0 | 0 | 2 | -| music_kraken/cli/download | 2 | 199 | 86 | 79 | 364 | -| music_kraken/cli/options | 3 | 69 | 7 | 30 | 106 | -| music_kraken/cli/options (Files) | 1 | 3 | 0 | 1 | 4 | -| music_kraken/cli/options/invidious | 2 | 66 | 7 | 29 | 102 | -| music_kraken/connection | 3 | 196 | 4 | 45 | 245 | -| music_kraken/database | 3 | 226 | 71 | 91 | 388 | -| music_kraken/download | 5 | 323 | 32 | 143 | 498 | -| music_kraken/objects | 11 | 1,226 | 338 | 376 | 1,940 | -| music_kraken/pages | 6 | 1,547 | 457 | 484 | 2,488 | -| music_kraken/static_files | 2 | 207 | 0 | 20 | 227 | -| music_kraken/utils | 30 | 1,095 | 126 | 343 | 1,564 | -| music_kraken/utils (Files) | 7 | 146 | 30 | 61 | 237 | -| music_kraken/utils/config | 8 | 651 | 72 | 177 | 900 | -| music_kraken/utils/enums | 3 | 56 | 7 | 14 | 77 | -| music_kraken/utils/exception | 3 | 23 | 8 | 12 | 43 | -| music_kraken/utils/path_manager | 4 | 58 | 9 | 29 | 96 | -| music_kraken/utils/support_classes | 5 | 161 | 0 | 50 | 211 | -| tests | 5 | 287 | 18 | 79 | 384 | -+------------------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ - -Files -+------------------------------------------------------------------------------------------------+----------+------------+------------+------------+------------+ -| filename | language | code | comment | blank | total | -+------------------------------------------------------------------------------------------------+----------+------------+------------+------------+------------+ -| /home/lars/Projects/music-downloader/src/__init__.py | Python | 0 | 0 | 1 | 1 | -| /home/lars/Projects/music-downloader/src/actual_donwload.py | Python | 16 | 0 | 6 | 22 | -| /home/lars/Projects/music-downloader/src/create_custom_objects.py | Python | 58 | 0 | 6 | 64 | -| /home/lars/Projects/music-downloader/src/metal_archives.py | Python | 30 | 0 | 12 | 42 | -| /home/lars/Projects/music-downloader/src/music_kraken/__init__.py | Python | 112 | 6 | 33 | 151 | -| /home/lars/Projects/music-downloader/src/music_kraken/__main__.py | Python | 98 | 3 | 23 | 124 | -| /home/lars/Projects/music-downloader/src/music_kraken/audio/__init__.py | Python | 7 | 0 | 3 | 10 | -| /home/lars/Projects/music-downloader/src/music_kraken/audio/codec.py | Python | 25 | 0 | 8 | 33 | -| /home/lars/Projects/music-downloader/src/music_kraken/audio/metadata.py | Python | 60 | 4 | 24 | 88 | -| /home/lars/Projects/music-downloader/src/music_kraken/cli/__init__.py | Python | 2 | 0 | 0 | 2 | -| /home/lars/Projects/music-downloader/src/music_kraken/cli/download/__init__.py | Python | 0 | 0 | 1 | 1 | -| /home/lars/Projects/music-downloader/src/music_kraken/cli/download/shell.py | Python | 199 | 86 | 78 | 363 | -| /home/lars/Projects/music-downloader/src/music_kraken/cli/options/__init__.py | Python | 3 | 0 | 1 | 4 | -| /home/lars/Projects/music-downloader/src/music_kraken/cli/options/invidious/__init__.py | Python | 0 | 0 | 1 | 1 | -| /home/lars/Projects/music-downloader/src/music_kraken/cli/options/invidious/shell.py | Python | 66 | 7 | 28 | 101 | -| /home/lars/Projects/music-downloader/src/music_kraken/connection/__init__.py | Python | 1 | 0 | 1 | 2 | -| /home/lars/Projects/music-downloader/src/music_kraken/connection/connection.py | Python | 168 | 1 | 30 | 199 | -| /home/lars/Projects/music-downloader/src/music_kraken/connection/rotating.py | Python | 27 | 3 | 14 | 44 | -| /home/lars/Projects/music-downloader/src/music_kraken/database/__init__.py | Python | 0 | 0 | 1 | 1 | -| /home/lars/Projects/music-downloader/src/music_kraken/database/data_models.py | Python | 122 | 24 | 52 | 198 | -| /home/lars/Projects/music-downloader/src/music_kraken/database/database.py | Python | 104 | 47 | 38 | 189 | -| /home/lars/Projects/music-downloader/src/music_kraken/download/__init__.py | Python | 1 | 0 | 1 | 2 | -| /home/lars/Projects/music-downloader/src/music_kraken/download/multiple_options.py | Python | 69 | 0 | 31 | 100 | -| /home/lars/Projects/music-downloader/src/music_kraken/download/page_attributes.py | Python | 67 | 1 | 29 | 97 | -| /home/lars/Projects/music-downloader/src/music_kraken/download/results.py | Python | 62 | 7 | 26 | 95 | -| /home/lars/Projects/music-downloader/src/music_kraken/download/search.py | Python | 124 | 24 | 56 | 204 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/__init__.py | Python | 14 | 0 | 5 | 19 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/cache.py | Python | 37 | 56 | 18 | 111 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/collection.py | Python | 91 | 31 | 39 | 161 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/formatted_text.py | Python | 50 | 10 | 19 | 79 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/lyrics.py | Python | 25 | 0 | 7 | 32 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/metadata.py | Python | 272 | 62 | 63 | 397 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/option.py | Python | 28 | 0 | 13 | 41 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/parents.py | Python | 71 | 35 | 35 | 141 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/song.py | Python | 480 | 113 | 120 | 713 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/source.py | Python | 93 | 16 | 33 | 142 | -| /home/lars/Projects/music-downloader/src/music_kraken/objects/target.py | Python | 65 | 15 | 24 | 104 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/__init__.py | Python | 4 | 0 | 2 | 6 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/abstract.py | Python | 234 | 46 | 93 | 373 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/encyclopaedia_metallum.py | Python | 432 | 90 | 127 | 649 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/musify.py | Python | 576 | 275 | 166 | 1,017 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/preset.py | Python | 47 | 1 | 17 | 65 | -| /home/lars/Projects/music-downloader/src/music_kraken/pages/youtube.py | Python | 254 | 45 | 79 | 378 | -| /home/lars/Projects/music-downloader/src/music_kraken/static_files/new_db.sql | SQLite | 72 | 0 | 10 | 82 | -| /home/lars/Projects/music-downloader/src/music_kraken/static_files/temp_database_structure.sql | SQLite | 135 | 0 | 10 | 145 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/__init__.py | Python | 2 | 1 | 2 | 5 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/config/__init__.py | Python | 7 | 0 | 4 | 11 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/config/audio.py | Python | 152 | 15 | 28 | 195 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/config/base_classes.py | Python | 136 | 35 | 61 | 232 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/config/config.py | Python | 92 | 16 | 30 | 138 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/config/connection.py | Python | 80 | 2 | 15 | 97 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/config/logging.py | Python | 104 | 4 | 17 | 125 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/config/misc.py | Python | 40 | 0 | 9 | 49 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/config/paths.py | Python | 40 | 0 | 13 | 53 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/enums/__init__.py | Python | 0 | 0 | 1 | 1 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/enums/album.py | Python | 16 | 6 | 5 | 27 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/enums/source.py | Python | 40 | 1 | 8 | 49 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/exception/__init__.py | Python | 1 | 0 | 1 | 2 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/exception/config.py | Python | 14 | 8 | 7 | 29 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/exception/download.py | Python | 8 | 0 | 4 | 12 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/functions.py | Python | 3 | 0 | 1 | 4 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/object_handeling.py | Python | 19 | 0 | 6 | 25 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/path_manager/__init__.py | Python | 2 | 0 | 2 | 4 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/path_manager/config_directory.py | Python | 4 | 0 | 4 | 8 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/path_manager/locations.py | Python | 16 | 0 | 9 | 25 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/path_manager/music_directory.py | Python | 36 | 9 | 14 | 59 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/phonetic_compares.py | Python | 39 | 2 | 17 | 58 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/regex.py | Python | 1 | 0 | 2 | 3 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/shared.py | Python | 66 | 22 | 22 | 110 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/string_processing.py | Python | 16 | 5 | 11 | 32 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/support_classes/__init__.py | Python | 4 | 0 | 1 | 5 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/support_classes/default_target.py | Python | 56 | 0 | 15 | 71 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/support_classes/download_result.py | Python | 69 | 0 | 21 | 90 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/support_classes/query.py | Python | 24 | 0 | 9 | 33 | -| /home/lars/Projects/music-downloader/src/music_kraken/utils/support_classes/thread_classes.py | Python | 8 | 0 | 4 | 12 | -| /home/lars/Projects/music-downloader/src/music_kraken_cli.py | Python | 3 | 0 | 3 | 6 | -| /home/lars/Projects/music-downloader/src/music_kraken_gtk.py | Python | 3 | 0 | 2 | 5 | -| /home/lars/Projects/music-downloader/src/musify_search.py | Python | 38 | 0 | 14 | 52 | -| /home/lars/Projects/music-downloader/src/tests/__init__.py | Python | 0 | 0 | 1 | 1 | -| /home/lars/Projects/music-downloader/src/tests/conftest.py | Python | 3 | 1 | 2 | 6 | -| /home/lars/Projects/music-downloader/src/tests/test_building_objects.py | Python | 81 | 1 | 13 | 95 | -| /home/lars/Projects/music-downloader/src/tests/test_download.py | Python | 30 | 1 | 12 | 43 | -| /home/lars/Projects/music-downloader/src/tests/test_objects.py | Python | 173 | 15 | 51 | 239 | -| Total | | 5,827 | 1,152 | 1,825 | 8,804 | -+------------------------------------------------------------------------------------------------+----------+------------+------------+------------+------------+ \ No newline at end of file diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..cb4a589 --- /dev/null +++ b/.env.example @@ -0,0 +1 @@ +STAGE=dev \ No newline at end of file diff --git a/.gitignore b/.gitignore index 28a388a..faa641c 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,5 @@ venv .idea/aws.xml windows + +.env diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml index 105ce2d..dd4c951 100644 --- a/.idea/inspectionProfiles/profiles_settings.xml +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -1,5 +1,6 @@ + diff --git a/.idea/misc.xml b/.idea/misc.xml index 6468d4f..878f755 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,10 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml index 56fe61a..05a1d7b 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -5,6 +5,7 @@ + \ No newline at end of file diff --git a/.idea/music-downloader.iml b/.idea/music-downloader.iml index d3aa814..721bcfd 100644 --- a/.idea/music-downloader.iml +++ b/.idea/music-downloader.iml @@ -3,11 +3,13 @@ + - + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 1a15e6d..09dbadc 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -2,6 +2,6 @@ - + \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 664d07e..4ee5e04 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -14,5 +14,11 @@ "[python]": { "editor.defaultFormatter": "ms-python.autopep8" }, - "python.formatting.provider": "none" + "python.formatting.provider": "none", + "cSpell.words": [ + "Bandcamp", + "dotenv", + "levenshtein", + "OKBLUE" + ] } \ No newline at end of file diff --git a/README.md b/README.md index c4b51a5..6174576 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,10 @@ - [Music Kraken](#music-kraken) - [Installation](#installation) - - [Dependencies](#dependencies) + - [From source](#from-source) - [Notes for WSL](#notes-for-wsl) - [Quick-Guide](#quick-guide) + - [Query](#query) - [CONTRIBUTE](#contribute) - [Matrix Space](#matrix-space) - [TODO till the next release](#todo-till-the-next-release) @@ -50,11 +51,6 @@ alias 🥺='sudo' source ~/.bashrc music-kraken ``` -### Dependencies - -You will need to install these two programms. - -- pandoc ### Notes for WSL @@ -62,35 +58,30 @@ If you choose to run it in WSL, make sure ` ~/.local/bin` is added to your `$PAT ## Quick-Guide -The **Genre** you define at the start is the folder, my programm will download the files into, AS WELL as the value of the ID3 genre field. +The **Genre** you define at the start, is the folder my program will download the files into, as well as the value of the ID3 genre field. When it drops you into the **shell** 2 main things are important: 1. You search with `s: ` 2. You choose an option with just the index number of the option -3. You download with `d: `, where the options are comma seperated -4. You support me by making a pr, or starring my repo. - -Trust me it WILL make sense, once you see it. +3. You download with `d: `, where the options are comma separated ### Query -The syntax for the query is like really simple. +The syntax for the query is really simple. -``` +```mk > s: #a searches for the artist -> s: #a #r +> s: #a #r searches for the release (album) by the artist > s: #r Me #t searches for the track from the release ``` -For a more detailed guid of the downloading shell, see [here](documentation/shell.md). - -LOVE YALL *(except nazis ;-;)* +The escape character is as usual `\`. --- @@ -100,18 +91,17 @@ I am happy about every pull request. To contribute look [here](contribute.md). ## Matrix Space - +music-kraken logo -I decided against creating a discord server, due to piracy communities get often banned from discord. A good and free Alternative are Matrix Spaces. I reccomend the use of the Client [Element](https://element.io/download). It is completely open source. +I decided against creating a discord server, due to various communities get often banned from discord. A good and free Alternative are Matrix Spaces. I recommend the use of the Client [Element](https://element.io/download). It is completely open source. -**Click [this link](https://matrix.to/#/#music-kraken:matrix.org) _([https://matrix.to/#/#music-kraken:matrix.org](https://matrix.to/#/#music-kraken:matrix.org))_ to join.** +**Click [this invitation](https://matrix.to/#/#music-kraken:matrix.org) _([https://matrix.to/#/#music-kraken:matrix.org](https://matrix.to/#/#music-kraken:matrix.org))_ to join.** ## TODO till the next release > These Points will most likely be in the changelogs. -- [x] Update the Documentation of the new cli. -- [ ] Migrate away from pandoc, to a more lightweight alternative, that can be installed over PiPY. +- [x] Migrate away from pandoc, to a more lightweight alternative, that can be installed over PiPY. - [ ] Update the Documentation of the internal structure. _(could be pushed back one release)_ --- @@ -124,7 +114,7 @@ This application is $100\%$ centered around Data. Thus, the most important thing - explanation of the [Data Model](#data-model) - how to use the [Data Objects](#data-objects) -- further Dokumentation of *hopefully* [most relevant classes](documentation/objects.md) +- further Dokumentation of _hopefully_ [most relevant classes](documentation/objects.md) - the [old implementation](documentation/old_implementation.md) ```mermaid diff --git a/build b/build new file mode 100755 index 0000000..3d2b57b --- /dev/null +++ b/build @@ -0,0 +1,48 @@ +#!/bin/bash + +test=false +version_bump="minor" + +while getopts ":b:t" opt; do + case ${opt} in + b ) + version_bump=$OPTARG + ;; + t ) + test=true + ;; + \? ) + echo "Invalid option: $OPTARG" 1>&2 + exit 1 + ;; + : ) + echo "Invalid option: $OPTARG requires an argument" 1>&2 + exit 1 + ;; + esac +done +shift $((OPTIND -1)) + +# install dev dependencies +echo "installing dev dependencies..." +python3 -m pip install -r requirements-dev.txt + +# hatch version ${version_bump} +# git add "music_kraken/__init__.py" +# git commit -m "bump: ${version_bump}" + +# build the wheels +python3 -m build + +# install the newest version +python3 -m pip install . + +if [ "$test" = true ]; +then + echo "just a test" + twine upload --repository testpypi dist/music_kraken* + python3 -m pip install -i https://test.pypi.org/simple/ music-kraken -U + exit +fi + +twine upload dist/music_kraken* diff --git a/build.sh b/build.sh deleted file mode 100755 index 506457e..0000000 --- a/build.sh +++ /dev/null @@ -1,71 +0,0 @@ -#!/bin/bash -test=true - -rm /tmp/music-downloader/*.db -rm /tmp/music-downloader/*.sql - -version=$(cut -d@ -f1 version) -echo version: $version - -git add . -git commit -am "ready for build $version" -git push - -sudo python3 -m pip install -U twine wheel setuptools - -python3 setup.py sdist bdist_wheel -sudo python3 -m pip uninstall music-kraken -y - -python3 -m pip install dist/music-kraken-$version.tar.gz --user -music-kraken - -if [ "$test" = true ]; -then - echo "just a test" - twine upload --repository testpypi dist/music_kraken* - python3 -m pip install -i https://test.pypi.org/simple/ music-kraken -U - exit -fi - -twine upload dist/music_kraken* - -echo "compiling............" -mkdir -p dist/build_files -mkdir -p dist/compiled - -pyinstaller --onefile src/music_kraken_cli.py --specpath dist/build_files --workpath dist/build_files --distpath dist/compiled -exit - -# https://packaging.python.org/en/latest/tutorials/packaging-projects/ -#echo "building............" -#echo "python3 -m pip install --upgrade build" -# python3 -m pip install --upgrade pip -# python3 -m pip install --upgrade build -#echo "python3 -m build" -# python3 -m build -# python3 setup.py sdist bdist_wheel -# python3 setup.py install --user - -# echo "python3 -m pip install dist/music_kraken-1.2.2-py3-none-any.whl --user --force-reinstall" -# python3 -m pip install dist/music_kraken-1.2.2.tar.gz --user --force-reinstall - -# music-kraken - -# open /home/lars/.local/lib/python3.10/site-packages/music_kraken -# echo "uploading............" -#python3 -m pip install --upgrade twine -#twine upload dist/music_kraken* - -# twine upload --repository testpypi dist/music_kraken* -exit - -echo "pushing............" -git add . -git commit -am "new build and upload" -git push - -echo "compiling............" -mkdir -p dist/build_files -mkdir -p dist/compiled - -pyinstaller --onefile src/music_kraken_cli.py --specpath dist/build_files --workpath dist/build_files --distpath dist/compiled diff --git a/src/__init__.py b/development/__init__.py similarity index 100% rename from src/__init__.py rename to development/__init__.py diff --git a/development/actual_donwload.py b/development/actual_donwload.py new file mode 100644 index 0000000..4b242ed --- /dev/null +++ b/development/actual_donwload.py @@ -0,0 +1,15 @@ +import music_kraken + +import logging +print("Setting logging-level to DEBUG") +logging.getLogger().setLevel(logging.DEBUG) + +if __name__ == "__main__": + commands = [ + "s: #a Toxoplasma", + "d: 16", + ] + + + music_kraken.cli.download(genre="test", command_list=commands, process_metadata_anyway=True) + _ = "debug" \ No newline at end of file diff --git a/documentation/html/bandcamp/artist_page.html b/documentation/html/bandcamp/artist_page.html new file mode 100644 index 0000000..06504a2 --- /dev/null +++ b/documentation/html/bandcamp/artist_page.html @@ -0,0 +1,125 @@ + + + + + + + + Music | Only Smile + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + + + +
+ + +
+

about

+ +

+ Only Smile + Russia +

+ + + +
+ +

+ + Contact Only Smile + +

+ + +

+ + Streaming and +
+ Download help +
+

+
+
+
+
+ + diff --git a/documentation/objects.md b/documentation/objects.md index 3891898..2b54dab 100644 --- a/documentation/objects.md +++ b/documentation/objects.md @@ -19,7 +19,7 @@ Additionally it provides an **Interface** to: ### DatabaseObject.merge() -To merge the data of two instances of the same type, the attributes defined in `DatabaseObject.COLLECTION_ATTRIBUTES` and `SIMPLE_ATTRIBUTES` are used. +To merge the data of two instances of the same type, the attributes defined in `DatabaseObject.COLLECTION_STRING_ATTRIBUTES` and `SIMPLE_STRING_ATTRIBUTES` are used. The simple attributes just get carried from the other instance, to the self instance. diff --git a/documentation/shell.md b/documentation/shell.md index de72743..31da1a3 100644 --- a/documentation/shell.md +++ b/documentation/shell.md @@ -10,7 +10,7 @@ > s: r: #a an Artist #r some random Release ``` -Searches for an url, or an query +Searches for an url, or a query ### Query Syntax @@ -33,6 +33,10 @@ To download something, you either need a direct link, or you need to have alread > d: https://musify.club/release/some-random-release-183028492 ``` +## Results + +If options are printed in **bold** they can be downloaded. Else they may or maybe can't be downloaded + ## Misc ### Exit diff --git a/music_kraken/__init__.py b/music_kraken/__init__.py new file mode 100644 index 0000000..43b89dd --- /dev/null +++ b/music_kraken/__init__.py @@ -0,0 +1,67 @@ +import logging +import gc +import sys +from pathlib import Path + +from rich.logging import RichHandler +from rich.console import Console + +from .utils.shared import DEBUG, DEBUG_LOGGING +from .utils.config import logging_settings, main_settings, read_config + +__version__ = "1.13.0" + +read_config() + +console: Console = Console() +def init_logging(): + log_file = main_settings['log_file'] + + if log_file.is_file(): + last_log_file = Path(log_file.parent, "prev." + log_file.name) + + with log_file.open("r", encoding="utf-8") as current_file: + with last_log_file.open("w", encoding="utf-8") as last_file: + last_file.write(current_file.read()) + + rich_handler = RichHandler(rich_tracebacks=True, console=console) + rich_handler.setLevel(logging_settings['log_level'] if not DEBUG_LOGGING else logging.DEBUG) + + file_handler = logging.FileHandler(log_file) + file_handler.setLevel(logging.DEBUG) + + # configure logger default + logging.basicConfig( + level=logging.DEBUG, + format=logging_settings['logging_format'], + datefmt="%Y-%m-%d %H:%M:%S", + handlers=[ + file_handler, + rich_handler, + ] + ) + +init_logging() + +from . import cli + +if DEBUG: + sys.setrecursionlimit(100) + + +if main_settings['modify_gc']: + """ + At the start I modify the garbage collector to run a bit fewer times. + This should increase speed: + https://mkennedy.codes/posts/python-gc-settings-change-this-and-make-your-app-go-20pc-faster/ + """ + # Clean up what might be garbage so far. + gc.collect(2) + + allocs, gen1, gen2 = gc.get_threshold() + allocs = 50_000 # Start the GC sequence every 50K not 700 allocations. + gen1 = gen1 * 2 + gen2 = gen2 * 2 + gc.set_threshold(allocs, gen1, gen2) + + diff --git a/src/music_kraken/__main__.py b/music_kraken/__main__.py similarity index 82% rename from src/music_kraken/__main__.py rename to music_kraken/__main__.py index c04d425..b90757b 100644 --- a/src/music_kraken/__main__.py +++ b/music_kraken/__main__.py @@ -79,6 +79,18 @@ def cli(): action="store_true" ) + parser.add_argument( + "--clear-cache", + help="Deletes the cache.", + action="store_true" + ) + + parser.add_argument( + "--clean-cache", + help="Deletes the outdated cache. (all expired cached files, and not indexed files)", + action="store_true" + ) + arguments = parser.parse_args() if arguments.verbose or arguments.test: @@ -91,8 +103,13 @@ def cli(): from .utils import shared if arguments.r: - from .utils.config import write_config - write_config() + import os + + for file in shared.CONFIG_DIRECTORY.iterdir(): + if file.is_file(): + print(f"Deleting {file}....") + file.unlink() + read_config() exit() @@ -110,6 +127,14 @@ def cli(): if arguments.frontend: cli.set_frontend(silent=False) + if arguments.clear_cache: + from .cli.options import cache + cache.clear_cache() + + if arguments.clean_cache: + from .cli.options import cache + cache.clean_cache() + # getting the genre genre: str = arguments.genre if arguments.test: diff --git a/src/music_kraken/audio/__init__.py b/music_kraken/audio/__init__.py similarity index 100% rename from src/music_kraken/audio/__init__.py rename to music_kraken/audio/__init__.py diff --git a/src/music_kraken/audio/codec.py b/music_kraken/audio/codec.py similarity index 94% rename from src/music_kraken/audio/codec.py rename to music_kraken/audio/codec.py index afb3a0f..9ec70e6 100644 --- a/src/music_kraken/audio/codec.py +++ b/music_kraken/audio/codec.py @@ -1,3 +1,4 @@ +from pathlib import Path from typing import List, Tuple from tqdm import tqdm from ffmpeg_progress_yield import FfmpegProgress @@ -19,8 +20,7 @@ def correct_codec(target: Target, bitrate_kb: int = main_settings["bitrate"], au bitrate_b = int(bitrate_kb / 1024) output_target = Target( - path=target._path, - file=str(target._file) + "." + audio_format + file_path=Path(str(target.file_path) + "." + audio_format) ) # get the select thingie diff --git a/src/music_kraken/audio/metadata.py b/music_kraken/audio/metadata.py similarity index 100% rename from src/music_kraken/audio/metadata.py rename to music_kraken/audio/metadata.py diff --git a/src/music_kraken/cli/__init__.py b/music_kraken/cli/__init__.py similarity index 100% rename from src/music_kraken/cli/__init__.py rename to music_kraken/cli/__init__.py diff --git a/src/music_kraken/cli/informations/__init__.py b/music_kraken/cli/informations/__init__.py similarity index 100% rename from src/music_kraken/cli/informations/__init__.py rename to music_kraken/cli/informations/__init__.py diff --git a/src/music_kraken/cli/informations/paths.py b/music_kraken/cli/informations/paths.py similarity index 76% rename from src/music_kraken/cli/informations/paths.py rename to music_kraken/cli/informations/paths.py index 8af01d2..327b351 100644 --- a/src/music_kraken/cli/informations/paths.py +++ b/music_kraken/cli/informations/paths.py @@ -1,7 +1,6 @@ from ..utils import cli_function from ...utils.path_manager import LOCATIONS -from ...utils import shared from ...utils.config import main_settings @@ -9,9 +8,11 @@ def all_paths(): return { "Temp dir": main_settings["temp_directory"], "Music dir": main_settings["music_directory"], - "Log file": main_settings["log_file"], "Conf dir": LOCATIONS.CONFIG_DIRECTORY, + "Conf file": LOCATIONS.CONFIG_FILE, + "logging file": main_settings["log_file"], "FFMPEG bin": main_settings["ffmpeg_binary"], + "Cache Dir": main_settings["cache_directory"], } diff --git a/src/music_kraken/cli/main_downloader.py b/music_kraken/cli/main_downloader.py similarity index 88% rename from src/music_kraken/cli/main_downloader.py rename to music_kraken/cli/main_downloader.py index 4c3a106..36541fe 100644 --- a/src/music_kraken/cli/main_downloader.py +++ b/music_kraken/cli/main_downloader.py @@ -1,3 +1,4 @@ +import random from typing import Set, Type, Dict, List from pathlib import Path import re @@ -6,16 +7,17 @@ from .utils import cli_function from .options.first_config import initial_config from ..utils.config import write_config, main_settings -from ..utils.regex import URL_PATTERN +from ..utils.shared import URL_PATTERN from ..utils.string_processing import fit_to_file_system -from ..utils.support_classes import Query, DownloadResult +from ..utils.support_classes.query import Query +from ..utils.support_classes.download_result import DownloadResult from ..utils.exception.download import UrlNotFoundException +from ..utils.enums.colors import BColors from ..download.results import Results, Option, PageResults from ..download.page_attributes import Pages from ..pages import Page from ..objects import Song, Album, Artist, DatabaseObject - """ This is the implementation of the Shell @@ -106,6 +108,7 @@ def get_existing_genre() -> List[str]: return existing_genres + def get_genre(): existing_genres = get_existing_genre() for i, genre_option in enumerate(existing_genres): @@ -128,19 +131,18 @@ def get_genre(): verification = input(f"create new genre \"{new_genre}\"? (Y/N): ").lower() if verification in agree_inputs: return new_genre - - + + def help_message(): print() - print(main_settings["happy_messages"]) + print(random.choice(main_settings["happy_messages"])) print() - class Downloader: def __init__( self, - exclude_pages: Set[Type[Page]] = None, + exclude_pages: Set[Type[Page]] = None, exclude_shady: bool = False, max_displayed_options: int = 10, option_digits: int = 3, @@ -148,23 +150,22 @@ class Downloader: process_metadata_anyway: bool = False, ) -> None: self.pages: Pages = Pages(exclude_pages=exclude_pages, exclude_shady=exclude_shady) - + self.page_dict: Dict[str, Type[Page]] = dict() - + self.max_displayed_options = max_displayed_options self.option_digits: int = option_digits - + self.current_results: Results = None self._result_history: List[Results] = [] - + self.genre = genre or get_genre() self.process_metadata_anyway = process_metadata_anyway - + print() print(f"Downloading to: \"{self.genre}\"") print() - def print_current_options(self): self.page_dict = dict() @@ -173,14 +174,16 @@ class Downloader: page_count = 0 for option in self.current_results.formated_generator(max_items_per_page=self.max_displayed_options): if isinstance(option, Option): - print(f"{option.index:0{self.option_digits}} {option.music_object.option_string}") + color = BColors.BOLD.value if self.pages.is_downloadable(option.music_object) else BColors.GREY.value + print(f"{color}{option.index:0{self.option_digits}} {option.music_object.option_string}{BColors.ENDC.value}") else: - prefix = ALPHABET[page_count%len(ALPHABET)] - print(f"({prefix}) ------------------------{option.__name__:{PAGE_NAME_FILL}<{MAX_PAGE_LEN}}------------") - + prefix = ALPHABET[page_count % len(ALPHABET)] + print( + f"{BColors.HEADER.value}({prefix}) ------------------------{option.__name__:{PAGE_NAME_FILL}<{MAX_PAGE_LEN}}------------{BColors.ENDC.value}") + self.page_dict[prefix] = option self.page_dict[option.__name__] = option - + page_count += 1 print() @@ -188,47 +191,47 @@ class Downloader: def set_current_options(self, current_options: Results): if main_settings["result_history"]: self._result_history.append(current_options) - + if main_settings["history_length"] != -1: if len(self._result_history) > main_settings["history_length"]: self._result_history.pop(0) - + self.current_results = current_options - + def previous_option(self) -> bool: if not main_settings["result_history"]: print("History is turned of.\nGo to main_settings, and change the value at 'result_history' to 'true'.") return False - + if len(self._result_history) <= 1: print(f"No results in history.") return False self._result_history.pop() self.current_results = self._result_history[-1] return True - + def _process_parsed(self, key_text: Dict[str, str], query: str) -> Query: song = None if not "t" in key_text else Song(title=key_text["t"], dynamic=True) album = None if not "r" in key_text else Album(title=key_text["r"], dynamic=True) artist = None if not "a" in key_text else Artist(name=key_text["a"], dynamic=True) - + if song is not None: if album is not None: song.album_collection.append(album) if artist is not None: song.main_artist_collection.append(artist) return Query(raw_query=query, music_object=song) - + if album is not None: if artist is not None: album.artist_collection.append(artist) return Query(raw_query=query, music_object=album) - + if artist is not None: return Query(raw_query=query, music_object=artist) - + return Query(raw_query=query) - + def search(self, query: str): if re.match(URL_PATTERN, query) is not None: try: @@ -242,61 +245,57 @@ class Downloader: self.set_current_options(PageResults(page, data_object.options)) self.print_current_options() return - + special_characters = "#\\" query = query + " " - + key_text = {} - + skip_next = False escape_next = False new_text = "" latest_key: str = None for i in range(len(query) - 1): current_char = query[i] - next_char = query[i+1] - + next_char = query[i + 1] + if skip_next: skip_next = False continue - + if escape_next: new_text += current_char escape_next = False - + # escaping if current_char == "\\": if next_char in special_characters: escape_next = True continue - + if current_char == "#": if latest_key is not None: key_text[latest_key] = new_text new_text = "" - + latest_key = next_char skip_next = True continue - + new_text += current_char - + if latest_key is not None: key_text[latest_key] = new_text - - + parsed_query: Query = self._process_parsed(key_text, query) - + self.set_current_options(self.pages.search(parsed_query)) self.print_current_options() - + def goto(self, index: int): page: Type[Page] music_object: DatabaseObject - - if self.current_results is not None: - self.current_results.delete_details(index) - + try: page, music_object = self.current_results.get_music_object_by_index(index) except KeyError: @@ -304,21 +303,22 @@ class Downloader: print(f"The option {index} doesn't exist.") print() return - + self.pages.fetch_details(music_object) - + + print(music_object) + print(music_object.options) self.set_current_options(PageResults(page, music_object.options)) - + self.print_current_options() - - + def download(self, download_str: str, download_all: bool = False) -> bool: to_download: List[DatabaseObject] = [] if re.match(URL_PATTERN, download_str) is not None: _, music_objects = self.pages.fetch_url(download_str) to_download.append(music_objects) - + else: index: str for index in download_str.split(", "): @@ -327,66 +327,68 @@ class Downloader: print(f"Every download thingie has to be an index, not {index}.") print() return False - + for index in download_str.split(", "): to_download.append(self.current_results.get_music_object_by_index(int(index))[1]) - + print() print("Downloading:") for download_object in to_download: print(download_object.option_string) print() - + _result_map: Dict[DatabaseObject, DownloadResult] = dict() - + for database_object in to_download: - r = self.pages.download(music_object=database_object, genre=self.genre, download_all=download_all, process_metadata_anyway=self.process_metadata_anyway) + r = self.pages.download(music_object=database_object, genre=self.genre, download_all=download_all, + process_metadata_anyway=self.process_metadata_anyway) _result_map[database_object] = r - + for music_object, result in _result_map.items(): print() print(music_object.option_string) print(result) - + return True - + def process_input(self, input_str: str) -> bool: input_str = input_str.strip() processed_input: str = input_str.lower() - + if processed_input in EXIT_COMMANDS: return True - + if processed_input == ".": self.print_current_options() return False - + if processed_input == "..": if self.previous_option(): self.print_current_options() return False - + if processed_input.startswith("s: "): self.search(input_str[3:]) return False - + if processed_input.startswith("d: "): return self.download(input_str[3:]) - + if processed_input.isdigit(): self.goto(int(processed_input)) return False - + if processed_input != "help": - print("Invalid input.") + print(f"{BColors.WARNING.value}Invalid input.{BColors.ENDC.value}") help_message() return False - + def mainloop(self): while True: if self.process_input(input("> ")): return + @cli_function def download( genre: str = None, @@ -400,12 +402,12 @@ def download( if code == 0: main_settings["hasnt_yet_started"] = False write_config() - print("Restart the programm to use it.") + print(f"{BColors.OKGREEN.value}Restart the programm to use it.{BColors.ENDC.value}") else: - print("Something went wrong configuring.") - + print(f"{BColors.FAIL.value}Something went wrong configuring.{BColors.ENDC.value}") + shell = Downloader(genre=genre, process_metadata_anyway=process_metadata_anyway) - + if command_list is not None: for command in command_list: shell.process_input(command) @@ -414,5 +416,5 @@ def download( if direct_download_url is not None: if shell.download(direct_download_url, download_all=download_all): return - + shell.mainloop() diff --git a/src/music_kraken/cli/options/__init__.py b/music_kraken/cli/options/__init__.py similarity index 100% rename from src/music_kraken/cli/options/__init__.py rename to music_kraken/cli/options/__init__.py diff --git a/music_kraken/cli/options/cache.py b/music_kraken/cli/options/cache.py new file mode 100644 index 0000000..42cb76b --- /dev/null +++ b/music_kraken/cli/options/cache.py @@ -0,0 +1,26 @@ +from logging import getLogger + +from ..utils import cli_function +from ...connection.cache import Cache + + +@cli_function +def clear_cache(): + """ + Deletes the cache. + :return: + """ + + Cache("main", getLogger("cache")).clear() + print("Cleared cache") + + +@cli_function +def clean_cache(): + """ + Deletes the outdated cache. (all expired cached files, and not indexed files) + :return: + """ + + Cache("main", getLogger("cache")).clean() + print("Cleaned cache") diff --git a/src/music_kraken/cli/options/first_config.py b/music_kraken/cli/options/first_config.py similarity index 100% rename from src/music_kraken/cli/options/first_config.py rename to music_kraken/cli/options/first_config.py diff --git a/src/music_kraken/cli/options/frontend.py b/music_kraken/cli/options/frontend.py similarity index 100% rename from src/music_kraken/cli/options/frontend.py rename to music_kraken/cli/options/frontend.py diff --git a/src/music_kraken/cli/options/settings.py b/music_kraken/cli/options/settings.py similarity index 100% rename from src/music_kraken/cli/options/settings.py rename to music_kraken/cli/options/settings.py diff --git a/src/music_kraken/cli/utils.py b/music_kraken/cli/utils.py similarity index 100% rename from src/music_kraken/cli/utils.py rename to music_kraken/cli/utils.py diff --git a/src/music_kraken/connection/__init__.py b/music_kraken/connection/__init__.py similarity index 100% rename from src/music_kraken/connection/__init__.py rename to music_kraken/connection/__init__.py diff --git a/music_kraken/connection/cache.py b/music_kraken/connection/cache.py new file mode 100644 index 0000000..4bc2b2b --- /dev/null +++ b/music_kraken/connection/cache.py @@ -0,0 +1,200 @@ +import json +from pathlib import Path +from dataclasses import dataclass +from datetime import datetime, timedelta +from typing import List, Optional +from functools import lru_cache +import logging + +from ..utils.config import main_settings + + +@dataclass +class CacheAttribute: + module: str + name: str + + created: datetime + expires: datetime + + @property + def id(self): + return f"{self.module}_{self.name}" + + @property + def is_valid(self): + if isinstance(self.expires, str): + self.expires = datetime.fromisoformat(self.expires) + return datetime.now() < self.expires + + def __eq__(self, other): + return self.__dict__ == other.__dict__ + + +class Cache: + def __init__(self, module: str, logger: logging.Logger): + self.module = module + self.logger: logging.Logger = logger + + self._dir = main_settings["cache_directory"] + self.index = Path(self._dir, "index.json") + + if not self.index.is_file(): + with self.index.open("w") as i: + i.write(json.dumps([])) + + self.cached_attributes: List[CacheAttribute] = [] + self._id_to_attribute = {} + + self._time_fields = {"created", "expires"} + with self.index.open("r") as i: + for c in json.loads(i.read()): + for key in self._time_fields: + c[key] = datetime.fromisoformat(c[key]) + + ca = CacheAttribute(**c) + self.cached_attributes.append(ca) + self._id_to_attribute[ca.id] = ca + + @lru_cache() + def _init_module(self, module: str) -> Path: + """ + :param module: + :return: the module path + """ + r = Path(self._dir, module) + r.mkdir(exist_ok=True) + return r + + def _write_index(self, indent: int = 4): + _json = [] + for c in self.cached_attributes: + d = c.__dict__ + for key in self._time_fields: + d[key] = d[key].isoformat() + + _json.append(d) + + with self.index.open("w") as f: + f.write(json.dumps(_json, indent=indent)) + + def _write_attribute(self, cached_attribute: CacheAttribute, write: bool = True) -> bool: + existing_attribute: Optional[CacheAttribute] = self._id_to_attribute.get(cached_attribute.id) + if existing_attribute is not None: + # the attribute exists + if existing_attribute == cached_attribute: + return True + + if existing_attribute.is_valid: + return False + + existing_attribute.__dict__ = cached_attribute.__dict__ + else: + self.cached_attributes.append(cached_attribute) + self._id_to_attribute[cached_attribute.id] = cached_attribute + + if write: + self._write_index() + + return True + + def set(self, content: bytes, name: str, expires_in: float = 10, module: str = ""): + """ + :param content: + :param module: + :param name: + :param expires_in: the unit is days + :return: + """ + if name == "": + return + + module = self.module if module == "" else module + + module_path = self._init_module(module) + + cache_attribute = CacheAttribute( + module=module, + name=name, + created=datetime.now(), + expires=datetime.now() + timedelta(days=expires_in), + ) + self._write_attribute(cache_attribute) + + cache_path = Path(module_path, name) + with cache_path.open("wb") as content_file: + self.logger.debug(f"writing cache to {cache_path}") + content_file.write(content) + + def get(self, name: str) -> Optional[bytes]: + path = Path(self._dir, self.module, name) + + if not path.is_file(): + return None + + # check if it is outdated + existing_attribute: CacheAttribute = self._id_to_attribute[f"{self.module}_{name}"] + if not existing_attribute.is_valid: + return + + with path.open("rb") as f: + return f.read() + + def clean(self): + keep = set() + + for ca in self.cached_attributes.copy(): + if ca.name == "": + continue + + file = Path(self._dir, ca.module, ca.name) + + if not ca.is_valid: + self.logger.debug(f"deleting cache {ca.id}") + file.unlink() + self.cached_attributes.remove(ca) + del self._id_to_attribute[ca.id] + + else: + keep.add(file) + + # iterate through every module (folder) + for module_path in self._dir.iterdir(): + if not module_path.is_dir(): + continue + + # delete all files not in keep + for path in module_path.iterdir(): + if path not in keep: + self.logger.info(f"Deleting cache {path}") + path.unlink() + + # delete all empty directories + for path in module_path.iterdir(): + if path.is_dir() and not list(path.iterdir()): + self.logger.debug(f"Deleting cache directory {path}") + path.rmdir() + + self._write_index() + + def clear(self): + """ + delete every file in the cache directory + :return: + """ + + for path in self._dir.iterdir(): + if path.is_dir(): + for file in path.iterdir(): + file.unlink() + path.rmdir() + else: + path.unlink() + + self.cached_attributes.clear() + self._id_to_attribute.clear() + + self._write_index() + + def __repr__(self): + return f"" diff --git a/src/music_kraken/connection/connection.py b/music_kraken/connection/connection.py similarity index 54% rename from src/music_kraken/connection/connection.py rename to music_kraken/connection/connection.py index 44d54b7..991c712 100644 --- a/src/music_kraken/connection/connection.py +++ b/music_kraken/connection/connection.py @@ -1,16 +1,23 @@ -import time -from typing import List, Dict, Callable, Optional, Set -from urllib.parse import urlparse, urlunsplit, ParseResult -import logging +from __future__ import annotations +import logging import threading +import time +from typing import List, Dict, Optional, Set +from urllib.parse import urlparse, urlunsplit, ParseResult +import copy +import inspect + import requests +import responses from tqdm import tqdm +from .cache import Cache from .rotating import RotatingProxy -from ..utils.config import main_settings -from ..utils.support_classes import DownloadResult from ..objects import Target +from ..utils.config import main_settings +from ..utils.support_classes.download_result import DownloadResult +from ..utils.hacking import merge_args class Connection: @@ -18,21 +25,24 @@ class Connection: self, host: str, proxies: List[dict] = None, - tries: int = (len(main_settings["proxies"]) + 1) * 4, + tries: int = (len(main_settings["proxies"]) + 1) * main_settings["tries_per_proxy"], timeout: int = 7, logger: logging.Logger = logging.getLogger("connection"), header_values: Dict[str, str] = None, accepted_response_codes: Set[int] = None, semantic_not_found: bool = True, sleep_after_404: float = 0.0, - heartbeat_interval = 0, + heartbeat_interval=0, + module: str = "general", + cache_expiring_duration: float = 10 ): if proxies is None: proxies = main_settings["proxies"] - if header_values is None: - header_values = dict() - self.HEADER_VALUES = header_values + self.cache: Cache = Cache(module=module, logger=logger) + self.cache_expiring_duration = cache_expiring_duration + + self.HEADER_VALUES = dict() if header_values is None else header_values self.LOGGER = logger self.HOST = urlparse(host) @@ -48,45 +58,34 @@ class Connection: self.session.headers = self.get_header(**self.HEADER_VALUES) self.session.proxies = self.rotating_proxy.current_proxy - self.session_is_occupied: bool = False - self.heartbeat_thread = None self.heartbeat_interval = heartbeat_interval - @property - def user_agent(self) -> str: - return self.session.headers.get("user-agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36") - + self.lock: bool = False def start_heartbeat(self): if self.heartbeat_interval <= 0: self.LOGGER.warning(f"Can't start a heartbeat with {self.heartbeat_interval}s in between.") - self.heartbeat_thread = threading.Thread(target=self._heartbeat_loop, args=(self.heartbeat_interval, ), daemon=True) + self.heartbeat_thread = threading.Thread(target=self._heartbeat_loop, args=(self.heartbeat_interval,), daemon=True) self.heartbeat_thread.start() def heartbeat_failed(self): - self.LOGGER.warning(f"I just died... (The heartbeat failed)") - + self.LOGGER.warning(f"The hearth couldn't beat.") def heartbeat(self): # Your code to send heartbeat requests goes here - print("the hearth is beating, but it needs to be implemented ;-;\nFuck youuuu for setting heartbeat in the constructor to true, but not implementing the method Connection.hearbeat()") + raise NotImplementedError("please implement the heartbeat function.") def _heartbeat_loop(self, interval: float): def heartbeat_wrapper(): - self.session_is_occupied = True - self.LOGGER.debug(f"I am living. (sending a heartbeat)") + self.LOGGER.debug(f"The hearth is beating.") self.heartbeat() - self.LOGGER.debug(f"finished the heartbeat") - self.session_is_occupied = False while True: heartbeat_wrapper() time.sleep(interval) - - def base_url(self, url: ParseResult = None): if url is None: url = self.HOST @@ -95,10 +94,12 @@ class Connection: def get_header(self, **header_values) -> Dict[str, str]: return { - "user-agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36", + "user-agent": main_settings["user_agent"], + "User-Agent": main_settings["user_agent"], "Connection": "keep-alive", - # "Host": self.HOST.netloc, + "Host": self.HOST.netloc, "Referer": self.base_url(), + "Accept-Language": main_settings["language"], **header_values } @@ -111,28 +112,76 @@ class Connection: refer_from_origin: bool, url: ParseResult ) -> Dict[str, str]: - if headers is None: - headers = dict() - + headers = self.get_header(**(headers or {})) if not refer_from_origin: headers["Referer"] = self.base_url(url=url) return headers - def _request( + def save(self, r: requests.Response, name: str, error: bool = False, **kwargs): + n_kwargs = {} + if error: + n_kwargs["module"] = "failed_requests" + + self.cache.set(r.content, name, expires_in=kwargs.get("expires_in", self.cache_expiring_duration), **n_kwargs) + + def request( self, - request: Callable, - try_count: int, - accepted_response_codes: set, url: str, - timeout: float, - headers: dict, + timeout: float = None, + headers: Optional[dict] = None, + try_count: int = 0, + accepted_response_codes: set = None, refer_from_origin: bool = True, raw_url: bool = False, + raw_headers: bool = False, sleep_after_404: float = None, is_heartbeat: bool = False, + disable_cache: bool = None, + method: str = None, + name: str = "", **kwargs ) -> Optional[requests.Response]: + if method is None: + raise AttributeError("method is not set.") + method = method.upper() + headers = dict() if headers is None else headers + disable_cache = headers.get("Cache-Control", "").lower() == "no-cache" if disable_cache is None else disable_cache + accepted_response_codes = self.ACCEPTED_RESPONSE_CODES if accepted_response_codes is None else accepted_response_codes + + current_kwargs = copy.copy(locals()) + current_kwargs.pop("kwargs") + current_kwargs.update(**kwargs) + + + parsed_url = urlparse(url) + + if not raw_headers: + _headers = copy.copy(self.HEADER_VALUES) + _headers.update(headers) + + headers = self._update_headers( + headers=_headers, + refer_from_origin=refer_from_origin, + url=parsed_url + ) + else: + headers = headers or {} + + request_url = parsed_url.geturl() if not raw_url else url + + if name != "" and not disable_cache: + cached = self.cache.get(name) + + if cached is not None: + with responses.RequestsMock() as resp: + resp.add( + method=method, + url=request_url, + body=cached, + ) + return requests.request(method=method, url=url, timeout=timeout, headers=headers, **kwargs) + if sleep_after_404 is None: sleep_after_404 = self.sleep_after_404 if try_count >= self.TRIES: @@ -141,32 +190,27 @@ class Connection: if timeout is None: timeout = self.TIMEOUT - parsed_url = urlparse(url) - - headers = self._update_headers( - headers=headers, - refer_from_origin=refer_from_origin, - url=parsed_url - ) - - request_url = parsed_url.geturl() if not raw_url else url - + r = None connection_failed = False try: - if self.session_is_occupied and not is_heartbeat: + if self.lock: self.LOGGER.info(f"Waiting for the heartbeat to finish.") - while self.session_is_occupied and not is_heartbeat: + while self.lock and not is_heartbeat: pass - - r: requests.Response = request(request_url, timeout=timeout, headers=headers, **kwargs) + + self.lock = True + r: requests.Response = requests.request(method=method, url=url, timeout=timeout, headers=headers, **kwargs) if r.status_code in accepted_response_codes: + if not disable_cache: + self.save(r, name, **kwargs) return r if self.SEMANTIC_NOT_FOUND and r.status_code == 404: self.LOGGER.warning(f"Couldn't find url (404): {request_url}") return None + # the server rejected the request, or the internet is lacking except requests.exceptions.Timeout: self.LOGGER.warning(f"Request timed out at \"{request_url}\": ({try_count}-{self.TRIES})") connection_failed = True @@ -174,130 +218,89 @@ class Connection: self.LOGGER.warning(f"Couldn't connect to \"{request_url}\": ({try_count}-{self.TRIES})") connection_failed = True + # this is important for thread safety + finally: + self.lock = False + if not connection_failed: - self.LOGGER.warning(f"{self.HOST.netloc} responded wit {r.status_code} " - f"at {url}. ({try_count}-{self.TRIES})") - self.LOGGER.debug(r.content) + self.LOGGER.warning(f"{self.HOST.netloc} responded wit {r.status_code} at {url}. ({try_count}-{self.TRIES})") + if r is not None: + self.LOGGER.debug("request headers:\n\t"+ "\n\t".join(f"{k}\t=\t{v}" for k, v in r.request.headers.items())) + self.LOGGER.debug("response headers:\n\t"+ "\n\t".join(f"{k}\t=\t{v}" for k, v in r.headers.items())) + self.LOGGER.debug(r.content) + + if name != "": + self.save(r, name, error=True, **kwargs) + if sleep_after_404 != 0: self.LOGGER.warning(f"Waiting for {sleep_after_404} seconds.") time.sleep(sleep_after_404) self.rotate() - if self.heartbeat_interval > 0 and self.heartbeat_thread is None: - self.start_heartbeat() + current_kwargs["try_count"] = current_kwargs.get("try_count", 0) + 1 + return Connection.request(**current_kwargs) - return self._request( - request=request, - try_count=try_count+1, - accepted_response_codes=accepted_response_codes, - url=url, - timeout=timeout, - headers=headers, - sleep_after_404=sleep_after_404, - is_heartbeat=is_heartbeat, + @merge_args(request) + def get(self, *args, **kwargs) -> Optional[requests.Response]: + return self.request( + *args, + method="GET", **kwargs ) - def get( - self, - url: str, - refer_from_origin: bool = True, - stream: bool = False, - accepted_response_codes: set = None, - timeout: float = None, - headers: dict = None, - raw_url: bool = False, - **kwargs - ) -> Optional[requests.Response]: - if accepted_response_codes is None: - accepted_response_codes = self.ACCEPTED_RESPONSE_CODES - - r = self._request( - request=self.session.get, - try_count=0, - accepted_response_codes=accepted_response_codes, - url=url, - timeout=timeout, - headers=headers, - raw_url=raw_url, - refer_from_origin=refer_from_origin, - stream=stream, - **kwargs - ) - if r is None: - self.LOGGER.warning(f"Max attempts ({self.TRIES}) exceeded for: GET:{url}") - return r - + @merge_args(request) def post( self, - url: str, + *args, json: dict = None, - refer_from_origin: bool = True, - stream: bool = False, - accepted_response_codes: set = None, - timeout: float = None, - headers: dict = None, - raw_url: bool = False, **kwargs ) -> Optional[requests.Response]: - r = self._request( - request=self.session.post, - try_count=0, - accepted_response_codes=accepted_response_codes or self.ACCEPTED_RESPONSE_CODES, - url=url, - timeout=timeout, - headers=headers, - refer_from_origin=refer_from_origin, - raw_url=raw_url, + r = self.request( + *args, + method="POST", json=json, - stream=stream, **kwargs ) if r is None: - self.LOGGER.warning(f"Max attempts ({self.TRIES}) exceeded for: GET:{url}") self.LOGGER.warning(f"payload: {json}") return r + @merge_args(request) def stream_into( self, url: str, target: Target, - description: str = "download", - refer_from_origin: bool = True, - accepted_response_codes: set = None, - timeout: float = None, - headers: dict = None, - raw_url: bool = False, + name: str = "download", chunk_size: int = main_settings["chunk_size"], - try_count: int = 0, progress: int = 0, + method: str = "GET", + try_count: int = 0, + accepted_response_codes: set = None, **kwargs ) -> DownloadResult: + accepted_response_codes = self.ACCEPTED_RESPONSE_CODES if accepted_response_codes is None else accepted_response_codes + stream_kwargs = copy.copy(locals()) + stream_kwargs.update(stream_kwargs.pop("kwargs")) + + if "description" in kwargs: + name = kwargs.pop("description") if progress > 0: - if headers is None: - headers = dict() + headers = dict() if headers is None else headers headers["Range"] = f"bytes={target.size}-" - if accepted_response_codes is None: - accepted_response_codes = self.ACCEPTED_RESPONSE_CODES - - r = self._request( - request=self.session.get, - try_count=0, - accepted_response_codes=accepted_response_codes, + r = self.request( url=url, - timeout=timeout, - headers=headers, - raw_url=raw_url, - refer_from_origin=refer_from_origin, + name=name, + method=method, stream=True, + accepted_response_codes=accepted_response_codes, **kwargs ) if r is None: - return DownloadResult(error_message=f"Could not establish connection to: {url}") + return DownloadResult(error_message=f"Could not establish a stream from: {url}") target.create_path() total_size = int(r.headers.get('content-length')) @@ -310,42 +313,29 @@ class Connection: https://en.wikipedia.org/wiki/Kilobyte > The internationally recommended unit symbol for the kilobyte is kB. """ - - with tqdm(total=total_size-target.size, unit='B', unit_scale=True, unit_divisor=1024, desc=description) as t: + + with tqdm(total=total_size, initial=target.size, unit='B', unit_scale=True, unit_divisor=1024, desc=name) as t: try: for chunk in r.iter_content(chunk_size=chunk_size): size = f.write(chunk) progress += size t.update(size) - except requests.exceptions.ConnectionError: + except (requests.exceptions.ConnectionError, requests.exceptions.Timeout, requests.exceptions.ChunkedEncodingError): if try_count >= self.TRIES: self.LOGGER.warning(f"Stream timed out at \"{url}\": to many retries, aborting.") - return DownloadResult(error_message=f"Stream timed out from {url}, reducing the chunksize might help.") + return DownloadResult(error_message=f"Stream timed out from {url}, reducing the chunk_size might help.") self.LOGGER.warning(f"Stream timed out at \"{url}\": ({try_count}-{self.TRIES})") retry = True + try_count += 1 if total_size > progress: retry = True - if retry: self.LOGGER.warning(f"Retrying stream...") accepted_response_codes.add(206) - return self.stream_into( - url = url, - target = target, - description = description, - try_count=try_count+1, - progress=progress, - accepted_response_codes=accepted_response_codes, - timeout=timeout, - headers=headers, - raw_url=raw_url, - refer_from_origin=refer_from_origin, - chunk_size=chunk_size, - **kwargs - ) + return Connection.stream_into(**stream_kwargs) return DownloadResult() diff --git a/src/music_kraken/connection/rotating.py b/music_kraken/connection/rotating.py similarity index 100% rename from src/music_kraken/connection/rotating.py rename to music_kraken/connection/rotating.py diff --git a/src/music_kraken/database/__init__.py b/music_kraken/download/__init__.py similarity index 100% rename from src/music_kraken/database/__init__.py rename to music_kraken/download/__init__.py diff --git a/src/music_kraken/download/page_attributes.py b/music_kraken/download/page_attributes.py similarity index 82% rename from src/music_kraken/download/page_attributes.py rename to music_kraken/download/page_attributes.py index 4ab45d0..e3881f6 100644 --- a/src/music_kraken/download/page_attributes.py +++ b/music_kraken/download/page_attributes.py @@ -5,24 +5,19 @@ from ..objects import DatabaseObject, Source from ..utils.config import youtube_settings from ..utils.enums.source import SourcePages -from ..utils.support_classes import Query, DownloadResult +from ..utils.support_classes.download_result import DownloadResult +from ..utils.support_classes.query import Query from ..utils.exception.download import UrlNotFoundException from ..utils.shared import DEBUG_PAGES -from ..pages import Page, EncyclopaediaMetallum, Musify, YouTube, YoutubeMusic, INDEPENDENT_DB_OBJECTS - -if DEBUG_PAGES: - DEBUGGING_PAGE = YoutubeMusic - print(f"Only downloading from page {DEBUGGING_PAGE}.") - - ALL_PAGES = {DEBUGGING_PAGE} - AUDIO_PAGES = ALL_PAGES.union(AUDIO_PAGES) +from ..pages import Page, EncyclopaediaMetallum, Musify, YouTube, YoutubeMusic, Bandcamp, INDEPENDENT_DB_OBJECTS ALL_PAGES: Set[Type[Page]] = { EncyclopaediaMetallum, Musify, - YoutubeMusic + YoutubeMusic, + Bandcamp } if youtube_settings["use_youtube_alongside_youtube_music"]: @@ -31,13 +26,22 @@ if youtube_settings["use_youtube_alongside_youtube_music"]: AUDIO_PAGES: Set[Type[Page]] = { Musify, YouTube, - YoutubeMusic + YoutubeMusic, + Bandcamp } SHADY_PAGES: Set[Type[Page]] = { Musify, } +if DEBUG_PAGES: + DEBUGGING_PAGE = Bandcamp + print(f"Only downloading from page {DEBUGGING_PAGE}.") + + ALL_PAGES = {DEBUGGING_PAGE} + AUDIO_PAGES = ALL_PAGES.union(AUDIO_PAGES) + + class Pages: def __init__(self, exclude_pages: Set[Type[Page]] = None, exclude_shady: bool = False) -> None: # initialize all page instances @@ -90,12 +94,27 @@ class Pages: music_object.merge(self._page_instances[page_type].fetch_details(music_object=music_object, stop_at_level=stop_at_level)) return music_object + + def is_downloadable(self, music_object: DatabaseObject) -> bool: + _page_types = set(self._source_to_page) + for src in music_object.source_collection.source_pages: + if src in self._source_to_page: + _page_types.add(self._source_to_page[src]) + + audio_pages = self._audio_pages_set.intersection(_page_types) + return len(audio_pages) > 0 def download(self, music_object: DatabaseObject, genre: str, download_all: bool = False, process_metadata_anyway: bool = False) -> DownloadResult: if not isinstance(music_object, INDEPENDENT_DB_OBJECTS): return DownloadResult(error_message=f"{type(music_object).__name__} can't be downloaded.") - - _page_types = set(self._source_to_page[src] for src in music_object.source_collection.source_pages) + + self.fetch_details(music_object) + + _page_types = set(self._source_to_page) + for src in music_object.source_collection.source_pages: + if src in self._source_to_page: + _page_types.add(self._source_to_page[src]) + audio_pages = self._audio_pages_set.intersection(_page_types) for download_page in audio_pages: diff --git a/src/music_kraken/download/results.py b/music_kraken/download/results.py similarity index 89% rename from src/music_kraken/download/results.py rename to music_kraken/download/results.py index 631ad48..c0dff08 100644 --- a/src/music_kraken/download/results.py +++ b/music_kraken/download/results.py @@ -27,15 +27,8 @@ class Results: self._page_by_index = dict() def get_music_object_by_index(self, index: int) -> Tuple[Type[Page], DatabaseObject]: - # if this throws a key error, either the formated generator needs to be iterated, or the option doesn't exist. + # if this throws a key error, either the formatted generator needs to be iterated, or the option doesn't exist. return self._page_by_index[index], self._by_index[index] - - def delete_details(self, exclude_index: int): - for index, music_object in self._by_index.items(): - if index == exclude_index: - continue - - music_object.strip_details() class SearchResults(Results): diff --git a/src/music_kraken/objects/__init__.py b/music_kraken/objects/__init__.py similarity index 70% rename from src/music_kraken/objects/__init__.py rename to music_kraken/objects/__init__.py index 4a17b9b..b4e75cb 100644 --- a/src/music_kraken/objects/__init__.py +++ b/music_kraken/objects/__init__.py @@ -1,5 +1,5 @@ +from typing_extensions import TypeVar from .option import Options -from .parents import DatabaseObject from .metadata import Metadata, Mapping as ID3Mapping, ID3Timestamp @@ -18,3 +18,8 @@ from .formatted_text import FormattedText from .collection import Collection from .country import Country +from .contact import Contact + +from .parents import OuterProxy + +DatabaseObject = TypeVar('T', bound=OuterProxy) diff --git a/src/music_kraken/objects/cache.py b/music_kraken/objects/cache.py similarity index 100% rename from src/music_kraken/objects/cache.py rename to music_kraken/objects/cache.py diff --git a/music_kraken/objects/collection.py b/music_kraken/objects/collection.py new file mode 100644 index 0000000..d2bf515 --- /dev/null +++ b/music_kraken/objects/collection.py @@ -0,0 +1,331 @@ +from __future__ import annotations + +from collections import defaultdict +from typing import TypeVar, Generic, Dict, Optional, Iterable, List, Iterator, Tuple +from .parents import OuterProxy + +T = TypeVar('T', bound=OuterProxy) + + +class Collection(Generic[T]): + __is_collection__ = True + + _data: List[T] + + _indexed_values: Dict[str, set] + _indexed_to_objects: Dict[any, list] + + shallow_list = property(fget=lambda self: self.data) + + def __init__( + self, + data: Optional[Iterable[T]] = None, + sync_on_append: Dict[str, Collection] = None, + contain_given_in_attribute: Dict[str, Collection] = None, + contain_attribute_in_given: Dict[str, Collection] = None, + append_object_to_attribute: Dict[str, T] = None + ) -> None: + self._contains_ids = set() + self._data = [] + + self.parents: List[Collection[T]] = [] + self.children: List[Collection[T]] = [] + + # List of collection attributes that should be modified on append + # Key: collection attribute (str) of appended element + # Value: main collection to sync to + self.contain_given_in_attribute: Dict[str, Collection] = contain_given_in_attribute or {} + self.contain_attribute_in_given: Dict[str, Collection] = contain_attribute_in_given or {} + self.append_object_to_attribute: Dict[str, T] = append_object_to_attribute or {} + + self.contain_self_on_append: List[str] = [] + + self._indexed_values = defaultdict(set) + self._indexed_to_objects = defaultdict(list) + + self.extend(data) + + def _map_element(self, __object: T, from_map: bool = False): + if __object.id in self._contains_ids: + return + + self._contains_ids.add(__object.id) + + for name, value in __object.indexing_values: + if value is None: + continue + + self._indexed_values[name].add(value) + self._indexed_to_objects[value].append(__object) + + if not from_map: + for attribute, new_object in self.contain_given_in_attribute.items(): + __object.__getattribute__(attribute).contain_collection_inside(new_object) + + for attribute, new_object in self.contain_attribute_in_given.items(): + new_object.contain_collection_inside(__object.__getattribute__(attribute)) + + for attribute, new_object in self.append_object_to_attribute.items(): + __object.__getattribute__(attribute).append(new_object) + + def _unmap_element(self, __object: T): + if __object.id in self._contains_ids: + self._contains_ids.remove(__object.id) + + for name, value in __object.indexing_values: + if value is None: + continue + if value not in self._indexed_values[name]: + continue + + try: + self._indexed_to_objects[value].remove(__object) + except ValueError: + continue + + if not len(self._indexed_to_objects[value]): + self._indexed_values[name].remove(value) + + def _contained_in_self(self, __object: T) -> bool: + if __object.id in self._contains_ids: + return True + + for name, value in __object.indexing_values: + if value is None: + continue + if value in self._indexed_values[name]: + return True + return False + + def _contained_in_sub(self, __object: T, break_at_first: bool = True) -> List[Collection]: + """ + Gets the collection this object is found in, if it is found in any. + + :param __object: + :param break_at_first: + :return: + """ + results = [] + + if self._contained_in_self(__object): + return [self] + + for collection in self.children: + results.extend(collection._contained_in_sub(__object, break_at_first=break_at_first)) + + if break_at_first: + return results + + return results + + def _get_root_collections(self) -> List[Collection]: + if not len(self.parents): + return [self] + + root_collections = [] + for upper_collection in self.parents: + root_collections.extend(upper_collection._get_root_collections()) + return root_collections + + @property + def _is_root(self) -> bool: + return len(self.parents) <= 0 + + def _get_parents_of_multiple_contained_children(self, __object: T): + results = [] + if len(self.children) < 2 or self._contained_in_self(__object): + return results + + count = 0 + + for collection in self.children: + sub_results = collection._get_parents_of_multiple_contained_children(__object) + + if len(sub_results) > 0: + count += 1 + results.extend(sub_results) + + if count >= 2: + results.append(self) + + return results + + def merge_into_self(self, __object: T, from_map: bool = False): + """ + 1. find existing objects + 2. merge into existing object + 3. remap existing object + """ + if __object.id in self._contains_ids: + return + + existing_object: T = None + + for name, value in __object.indexing_values: + if value is None: + continue + + if value in self._indexed_values[name]: + existing_object = self._indexed_to_objects[value][0] + if existing_object.id == __object.id: + return None + + break + + if existing_object is None: + return None + + existing_object.merge(__object) + + # just a check if it really worked + if existing_object.id != __object.id: + raise ValueError("This should NEVER happen. Merging doesn't work.") + + self._map_element(existing_object, from_map=from_map) + + def contains(self, __object: T) -> bool: + return len(self._contained_in_sub(__object)) > 0 + + def _append(self, __object: T, from_map: bool = False): + print(self, __object) + self._map_element(__object, from_map=from_map) + self._data.append(__object) + + def _find_object_in_self(self, __object: T) -> Optional[T]: + for name, value in __object.indexing_values: + if value is None: + continue + if value in self._indexed_values[name]: + return self._indexed_to_objects[value][0] + + def _find_object(self, __object: T) -> Tuple[Collection[T], Optional[T]]: + other_object = self._find_object_in_self(__object) + if other_object is not None: + return self, other_object + + for c in self.children: + o, other_object = c._find_object(__object) + if other_object is not None: + return o, other_object + + return self, None + + def append(self, __object: Optional[T], already_is_parent: bool = False, from_map: bool = False): + """ + If an object, that represents the same entity exists in a relevant collection, + merge into this object. (and remap) + Else append to this collection. + + :param __object: + :param already_is_parent: + :param from_map: + :return: + """ + + if __object is None or __object.id in self._contains_ids: + return + + append_to, existing_object = self._find_object(__object) + + if existing_object is None: + # append + # print("appending", existing_object, __object) + append_to._data.append(__object) + else: + # merge + append_to._unmap_element(existing_object) + existing_object.merge(__object) + + append_to._map_element(__object, from_map=from_map) + + """ + exists_in_collection = self._contained_in_sub(__object) + if len(exists_in_collection) and self is exists_in_collection[0]: + # assuming that the object already is contained in the correct collections + if not already_is_parent: + self.merge_into_self(__object, from_map=from_map) + return + + if not len(exists_in_collection): + self._append(__object, from_map=from_map) + else: + exists_in_collection[0].merge_into_self(__object, from_map=from_map) + + if not already_is_parent or not self._is_root: + for parent_collection in self._get_parents_of_multiple_contained_children(__object): + pass + parent_collection.append(__object, already_is_parent=True, from_map=from_map) + """ + + def extend(self, __iterable: Optional[Iterable[T]], from_map: bool = False): + if __iterable is None: + return + + for __object in __iterable: + self.append(__object, from_map=from_map) + + def sync_with_other_collection(self, equal_collection: Collection): + """ + If two collections always need to have the same values, this can be used. + + Internally: + 1. import the data from other to self + - _data + - contained_collections + 2. replace all refs from the other object, with refs from this object + """ + if equal_collection is self: + return + + # don't add the elements from the subelements from the other collection. + # this will be done in the next step. + self.extend(equal_collection._data) + # add all submodules + for equal_sub_collection in equal_collection.children: + self.contain_collection_inside(equal_sub_collection) + + def contain_collection_inside(self, sub_collection: Collection): + """ + This collection will ALWAYS contain everything from the passed in collection + """ + if self is sub_collection or sub_collection in self.children: + return + + self.children.append(sub_collection) + sub_collection.parents.append(self) + + @property + def data(self) -> List[T]: + return [*self._data, + *(__object for collection in self.children for __object in collection.shallow_list)] + + def __len__(self) -> int: + return len(self._data) + sum(len(collection) for collection in self.children) + + @property + def empty(self) -> bool: + return self.__len__() <= 0 + + def __iter__(self) -> Iterator[T]: + for element in self._data: + yield element + + for c in self.children: + for element in c: + yield element + + def __merge__(self, __other: Collection, override: bool = False): + self.extend(__other._data, from_map=True) + + def __getitem__(self, item: int): + if item < len(self._data): + return self._data[item] + + item = item - len(self._data) + + for c in self.children: + if item < len(c): + return c.__getitem__(item) + item = item - len(c._data) + + raise IndexError diff --git a/music_kraken/objects/contact.py b/music_kraken/objects/contact.py new file mode 100644 index 0000000..e2e024f --- /dev/null +++ b/music_kraken/objects/contact.py @@ -0,0 +1,38 @@ +from typing import Optional, List, Tuple + +from ..utils.enums.contact import ContactMethod +from .parents import OuterProxy + + +class Contact(OuterProxy): + COLLECTION_STRING_ATTRIBUTES = tuple() + SIMPLE_STRING_ATTRIBUTES = { + "contact_method": None, + "value": None, + } + + @property + def indexing_values(self) -> List[Tuple[str, object]]: + return [ + ('id', self.id), + ('value', self.value), + ] + + def __init__(self, contact_method: ContactMethod, value: str, **kwargs) -> None: + super().__init__(**kwargs) + self.contact_method: ContactMethod = contact_method + self.value: str = value + + @classmethod + def match_url(cls, url: str) -> Optional["Contact"]: + url = url.strip() + + if url.startswith("mailto:"): + return cls(ContactMethod.EMAIL, url.replace("mailto:", "", 1)) + + if url.startswith("tel:"): + return cls(ContactMethod.PHONE, url.replace("tel:", "", 1)) + + if url.startswith("fax:"): + return cls(ContactMethod.FAX, url.replace("fax:", "", 1)) + diff --git a/src/music_kraken/objects/country.py b/music_kraken/objects/country.py similarity index 93% rename from src/music_kraken/objects/country.py rename to music_kraken/objects/country.py index aed4842..3b60bf5 100644 --- a/src/music_kraken/objects/country.py +++ b/music_kraken/objects/country.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from dataclasses import dataclass import pycountry @@ -9,7 +11,6 @@ CountryTyping = type(list(pycountry.countries)[0]) emoji_map = {'AD': '🇦🇩', 'AE': '🇦🇪', 'AF': '🇦🇫', 'AG': '🇦🇬', 'AI': '🇦🇮', 'AL': '🇦🇱', 'AM': '🇦🇲', 'AO': '🇦🇴', 'AQ': '🇦🇶', 'AR': '🇦🇷', 'AS': '🇦🇸', 'AT': '🇦🇹', 'AU': '🇦🇺', 'AW': '🇦🇼', 'AX': '🇦🇽', 'AZ': '🇦🇿', 'BA': '🇧🇦', 'BB': '🇧🇧', 'BD': '🇧🇩', 'BE': '🇧🇪', 'BF': '🇧🇫', 'BG': '🇧🇬', 'BH': '🇧🇭', 'BI': '🇧🇮', 'BJ': '🇧🇯', 'BL': '🇧🇱', 'BM': '🇧🇲', 'BN': '🇧🇳', 'BO': '🇧🇴', 'BQ': '🇧🇶', 'BR': '🇧🇷', 'BS': '🇧🇸', 'BT': '🇧🇹', 'BV': '🇧🇻', 'BW': '🇧🇼', 'BY': '🇧🇾', 'BZ': '🇧🇿', 'CA': '🇨🇦', 'CC': '🇨🇨', 'CD': '🇨🇩', 'CF': '🇨🇫', 'CG': '🇨🇬', 'CH': '🇨🇭', 'CI': '🇨🇮', 'CK': '🇨🇰', 'CL': '🇨🇱', 'CM': '🇨🇲', 'CN': '🇨🇳', 'CO': '🇨🇴', 'CR': '🇨🇷', 'CU': '🇨🇺', 'CV': '🇨🇻', 'CW': '🇨🇼', 'CX': '🇨🇽', 'CY': '🇨🇾', 'CZ': '🇨🇿', 'DE': '🇩🇪', 'DJ': '🇩🇯', 'DK': '🇩🇰', 'DM': '🇩🇲', 'DO': '🇩🇴', 'DZ': '🇩🇿', 'EC': '🇪🇨', 'EE': '🇪🇪', 'EG': '🇪🇬', 'EH': '🇪🇭', 'ER': '🇪🇷', 'ES': '🇪🇸', 'ET': '🇪🇹', 'FI': '🇫🇮', 'FJ': '🇫🇯', 'FK': '🇫🇰', 'FM': '🇫🇲', 'FO': '🇫🇴', 'FR': '🇫🇷', 'GA': '🇬🇦', 'GB': '🇬🇧', 'GD': '🇬🇩', 'GE': '🇬🇪', 'GF': '🇬🇫', 'GG': '🇬🇬', 'GH': '🇬🇭', 'GI': '🇬🇮', 'GL': '🇬🇱', 'GM': '🇬🇲', 'GN': '🇬🇳', 'GP': '🇬🇵', 'GQ': '🇬🇶', 'GR': '🇬🇷', 'GS': '🇬🇸', 'GT': '🇬🇹', 'GU': '🇬🇺', 'GW': '🇬🇼', 'GY': '🇬🇾', 'HK': '🇭🇰', 'HM': '🇭🇲', 'HN': '🇭🇳', 'HR': '🇭🇷', 'HT': '🇭🇹', 'HU': '🇭🇺', 'ID': '🇮🇩', 'IE': '🇮🇪', 'IL': '🇮🇱', 'IM': '🇮🇲', 'IN': '🇮🇳', 'IO': '🇮🇴', 'IQ': '🇮🇶', 'IR': '🇮🇷', 'IS': '🇮🇸', 'IT': '🇮🇹', 'JE': '🇯🇪', 'JM': '🇯🇲', 'JO': '🇯🇴', 'JP': '🇯🇵', 'KE': '🇰🇪', 'KG': '🇰🇬', 'KH': '🇰🇭', 'KI': '🇰🇮', 'KM': '🇰🇲', 'KN': '🇰🇳', 'KP': '🇰🇵', 'KR': '🇰🇷', 'KW': '🇰🇼', 'KY': '🇰🇾', 'KZ': '🇰🇿', 'LA': '🇱🇦', 'LB': '🇱🇧', 'LC': '🇱🇨', 'LI': '🇱🇮', 'LK': '🇱🇰', 'LR': '🇱🇷', 'LS': '🇱🇸', 'LT': '🇱🇹', 'LU': '🇱🇺', 'LV': '🇱🇻', 'LY': '🇱🇾', 'MA': '🇲🇦', 'MC': '🇲🇨', 'MD': '🇲🇩', 'ME': '🇲🇪', 'MF': '🇲🇫', 'MG': '🇲🇬', 'MH': '🇲🇭', 'MK': '🇲🇰', 'ML': '🇲🇱', 'MM': '🇲🇲', 'MN': '🇲🇳', 'MO': '🇲🇴', 'MP': '🇲🇵', 'MQ': '🇲🇶', 'MR': '🇲🇷', 'MS': '🇲🇸', 'MT': '🇲🇹', 'MU': '🇲🇺', 'MV': '🇲🇻', 'MW': '🇲🇼', 'MX': '🇲🇽', 'MY': '🇲🇾', 'MZ': '🇲🇿', 'NA': '🇳🇦', 'NC': '🇳🇨', 'NE': '🇳🇪', 'NF': '🇳🇫', 'NG': '🇳🇬', 'NI': '🇳🇮', 'NL': '🇳🇱', 'NO': '🇳🇴', 'NP': '🇳🇵', 'NR': '🇳🇷', 'NU': '🇳🇺', 'NZ': '🇳🇿', 'OM': '🇴🇲', 'PA': '🇵🇦', 'PE': '🇵🇪', 'PF': '🇵🇫', 'PG': '🇵🇬', 'PH': '🇵🇭', 'PK': '🇵🇰', 'PL': '🇵🇱', 'PM': '🇵🇲', 'PN': '🇵🇳', 'PR': '🇵🇷', 'PS': '🇵🇸', 'PT': '🇵🇹', 'PW': '🇵🇼', 'PY': '🇵🇾', 'QA': '🇶🇦', 'RE': '🇷🇪', 'RO': '🇷🇴', 'RS': '🇷🇸', 'RU': '🇷🇺', 'RW': '🇷🇼', 'SA': '🇸🇦', 'SB': '🇸🇧', 'SC': '🇸🇨', 'SD': '🇸🇩', 'SE': '🇸🇪', 'SG': '🇸🇬', 'SH': '🇸🇭', 'SI': '🇸🇮', 'SJ': '🇸🇯', 'SK': '🇸🇰', 'SL': '🇸🇱', 'SM': '🇸🇲', 'SN': '🇸🇳', 'SO': '🇸🇴', 'SR': '🇸🇷', 'SS': '🇸🇸', 'ST': '🇸🇹', 'SV': '🇸🇻', 'SX': '🇸🇽', 'SY': '🇸🇾', 'SZ': '🇸🇿', 'TC': '🇹🇨', 'TD': '🇹🇩', 'TF': '🇹🇫', 'TG': '🇹🇬', 'TH': '🇹🇭', 'TJ': '🇹🇯', 'TK': '🇹🇰', 'TL': '🇹🇱', 'TM': '🇹🇲', 'TN': '🇹🇳', 'TO': '🇹🇴', 'TR': '🇹🇷', 'TT': '🇹🇹', 'TV': '🇹🇻', 'TW': '🇹🇼', 'TZ': '🇹🇿', 'UA': '🇺🇦', 'UG': '🇺🇬', 'UM': '🇺🇲', 'US': '🇺🇸', 'UY': '🇺🇾', 'UZ': '🇺🇿', 'VA': '🇻🇦', 'VC': '🇻🇨', 'VE': '🇻🇪', 'VG': '🇻🇬', 'VI': '🇻🇮', 'VN': '🇻🇳', 'VU': '🇻🇺', 'WF': '🇼🇫', 'WS': '🇼🇸', 'YE': '🇾🇪', 'YT': '🇾🇹', 'ZA': '🇿🇦', 'ZM': '🇿🇲', 'ZW': '🇿🇼', '🇦🇩': 'AD', '🇦🇪': 'AE', '🇦🇫': 'AF', '🇦🇬': 'AG', '🇦🇮': 'AI', '🇦🇱': 'AL', '🇦🇲': 'AM', '🇦🇴': 'AO', '🇦🇶': 'AQ', '🇦🇷': 'AR', '🇦🇸': 'AS', '🇦🇹': 'AT', '🇦🇺': 'AU', '🇦🇼': 'AW', '🇦🇽': 'AX', '🇦🇿': 'AZ', '🇧🇦': 'BA', '🇧🇧': 'BB', '🇧🇩': 'BD', '🇧🇪': 'BE', '🇧🇫': 'BF', '🇧🇬': 'BG', '🇧🇭': 'BH', '🇧🇮': 'BI', '🇧🇯': 'BJ', '🇧🇱': 'BL', '🇧🇲': 'BM', '🇧🇳': 'BN', '🇧🇴': 'BO', '🇧🇶': 'BQ', '🇧🇷': 'BR', '🇧🇸': 'BS', '🇧🇹': 'BT', '🇧🇻': 'BV', '🇧🇼': 'BW', '🇧🇾': 'BY', '🇧🇿': 'BZ', '🇨🇦': 'CA', '🇨🇨': 'CC', '🇨🇩': 'CD', '🇨🇫': 'CF', '🇨🇬': 'CG', '🇨🇭': 'CH', '🇨🇮': 'CI', '🇨🇰': 'CK', '🇨🇱': 'CL', '🇨🇲': 'CM', '🇨🇳': 'CN', '🇨🇴': 'CO', '🇨🇷': 'CR', '🇨🇺': 'CU', '🇨🇻': 'CV', '🇨🇼': 'CW', '🇨🇽': 'CX', '🇨🇾': 'CY', '🇨🇿': 'CZ', '🇩🇪': 'DE', '🇩🇯': 'DJ', '🇩🇰': 'DK', '🇩🇲': 'DM', '🇩🇴': 'DO', '🇩🇿': 'DZ', '🇪🇨': 'EC', '🇪🇪': 'EE', '🇪🇬': 'EG', '🇪🇭': 'EH', '🇪🇷': 'ER', '🇪🇸': 'ES', '🇪🇹': 'ET', '🇫🇮': 'FI', '🇫🇯': 'FJ', '🇫🇰': 'FK', '🇫🇲': 'FM', '🇫🇴': 'FO', '🇫🇷': 'FR', '🇬🇦': 'GA', '🇬🇧': 'GB', '🇬🇩': 'GD', '🇬🇪': 'GE', '🇬🇫': 'GF', '🇬🇬': 'GG', '🇬🇭': 'GH', '🇬🇮': 'GI', '🇬🇱': 'GL', '🇬🇲': 'GM', '🇬🇳': 'GN', '🇬🇵': 'GP', '🇬🇶': 'GQ', '🇬🇷': 'GR', '🇬🇸': 'GS', '🇬🇹': 'GT', '🇬🇺': 'GU', '🇬🇼': 'GW', '🇬🇾': 'GY', '🇭🇰': 'HK', '🇭🇲': 'HM', '🇭🇳': 'HN', '🇭🇷': 'HR', '🇭🇹': 'HT', '🇭🇺': 'HU', '🇮🇩': 'ID', '🇮🇪': 'IE', '🇮🇱': 'IL', '🇮🇲': 'IM', '🇮🇳': 'IN', '🇮🇴': 'IO', '🇮🇶': 'IQ', '🇮🇷': 'IR', '🇮🇸': 'IS', '🇮🇹': 'IT', '🇯🇪': 'JE', '🇯🇲': 'JM', '🇯🇴': 'JO', '🇯🇵': 'JP', '🇰🇪': 'KE', '🇰🇬': 'KG', '🇰🇭': 'KH', '🇰🇮': 'KI', '🇰🇲': 'KM', '🇰🇳': 'KN', '🇰🇵': 'KP', '🇰🇷': 'KR', '🇰🇼': 'KW', '🇰🇾': 'KY', '🇰🇿': 'KZ', '🇱🇦': 'LA', '🇱🇧': 'LB', '🇱🇨': 'LC', '🇱🇮': 'LI', '🇱🇰': 'LK', '🇱🇷': 'LR', '🇱🇸': 'LS', '🇱🇹': 'LT', '🇱🇺': 'LU', '🇱🇻': 'LV', '🇱🇾': 'LY', '🇲🇦': 'MA', '🇲🇨': 'MC', '🇲🇩': 'MD', '🇲🇪': 'ME', '🇲🇫': 'MF', '🇲🇬': 'MG', '🇲🇭': 'MH', '🇲🇰': 'MK', '🇲🇱': 'ML', '🇲🇲': 'MM', '🇲🇳': 'MN', '🇲🇴': 'MO', '🇲🇵': 'MP', '🇲🇶': 'MQ', '🇲🇷': 'MR', '🇲🇸': 'MS', '🇲🇹': 'MT', '🇲🇺': 'MU', '🇲🇻': 'MV', '🇲🇼': 'MW', '🇲🇽': 'MX', '🇲🇾': 'MY', '🇲🇿': 'MZ', '🇳🇦': 'NA', '🇳🇨': 'NC', '🇳🇪': 'NE', '🇳🇫': 'NF', '🇳🇬': 'NG', '🇳🇮': 'NI', '🇳🇱': 'NL', '🇳🇴': 'NO', '🇳🇵': 'NP', '🇳🇷': 'NR', '🇳🇺': 'NU', '🇳🇿': 'NZ', '🇴🇲': 'OM', '🇵🇦': 'PA', '🇵🇪': 'PE', '🇵🇫': 'PF', '🇵🇬': 'PG', '🇵🇭': 'PH', '🇵🇰': 'PK', '🇵🇱': 'PL', '🇵🇲': 'PM', '🇵🇳': 'PN', '🇵🇷': 'PR', '🇵🇸': 'PS', '🇵🇹': 'PT', '🇵🇼': 'PW', '🇵🇾': 'PY', '🇶🇦': 'QA', '🇷🇪': 'RE', '🇷🇴': 'RO', '🇷🇸': 'RS', '🇷🇺': 'RU', '🇷🇼': 'RW', '🇸🇦': 'SA', '🇸🇧': 'SB', '🇸🇨': 'SC', '🇸🇩': 'SD', '🇸🇪': 'SE', '🇸🇬': 'SG', '🇸🇭': 'SH', '🇸🇮': 'SI', '🇸🇯': 'SJ', '🇸🇰': 'SK', '🇸🇱': 'SL', '🇸🇲': 'SM', '🇸🇳': 'SN', '🇸🇴': 'SO', '🇸🇷': 'SR', '🇸🇸': 'SS', '🇸🇹': 'ST', '🇸🇻': 'SV', '🇸🇽': 'SX', '🇸🇾': 'SY', '🇸🇿': 'SZ', '🇹🇨': 'TC', '🇹🇩': 'TD', '🇹🇫': 'TF', '🇹🇬': 'TG', '🇹🇭': 'TH', '🇹🇯': 'TJ', '🇹🇰': 'TK', '🇹🇱': 'TL', '🇹🇲': 'TM', '🇹🇳': 'TN', '🇹🇴': 'TO', '🇹🇷': 'TR', '🇹🇹': 'TT', '🇹🇻': 'TV', '🇹🇼': 'TW', '🇹🇿': 'TZ', '🇺🇦': 'UA', '🇺🇬': 'UG', '🇺🇲': 'UM', '🇺🇸': 'US', '🇺🇾': 'UY', '🇺🇿': 'UZ', '🇻🇦': 'VA', '🇻🇨': 'VC', '🇻🇪': 'VE', '🇻🇬': 'VG', '🇻🇮': 'VI', '🇻🇳': 'VN', '🇻🇺': 'VU', '🇼🇫': 'WF', '🇼🇸': 'WS', '🇾🇪': 'YE', '🇾🇹': 'YT', '🇿🇦': 'ZA', '🇿🇲': 'ZM', '🇿🇼': 'ZW'} - @dataclass class Country: alpha_2: str @@ -19,7 +20,7 @@ class Country: emoji: str @classmethod - def by_pycountry(cls, country: CountryTyping, emoji: str = "") -> "Country": + def by_pycountry(cls, country: CountryTyping, emoji: str = "") -> "Country": emoji = "" alpha_2 = country.alpha_2.upper() @@ -40,7 +41,7 @@ class Country: return cls.by_pycountry(pycountry.countries.get(alpha_2=alpha_2)) @classmethod - def by_apha_3(cls, alpha_3: str) -> "Country": + def by_alpha_3(cls, alpha_3: str) -> "Country": return cls.by_pycountry(pycountry.countries.get(alpha_3=alpha_3)) @classmethod @@ -63,4 +64,29 @@ class Country: return hash(self.alpha_3) def __eq__(self, __value: object) -> bool: - return self.__hash__() == __value.__hash__() \ No newline at end of file + return self.__hash__() == __value.__hash__() + + +@dataclass +class Language: + alpha_2: str + alpha_3: str + name: str + + @classmethod + def by_pycountry(cls, language) -> Language: + alpha_2 = language.alpha_2.upper() + + return cls( + alpha_2=alpha_2, + alpha_3=language.alpha_3, + name=language.name, + ) + + @classmethod + def by_alpha_2(cls, alpha_2: str) -> Language: + return cls.by_pycountry(pycountry.languages.get(alpha_2=alpha_2.upper())) + + @classmethod + def by_alpha_3(cls, alpha_3: str) -> Language: + return cls.by_pycountry(pycountry.languages.get(alpha_3=alpha_3)) diff --git a/music_kraken/objects/formatted_text.py b/music_kraken/objects/formatted_text.py new file mode 100644 index 0000000..7a828fe --- /dev/null +++ b/music_kraken/objects/formatted_text.py @@ -0,0 +1,37 @@ +import mistune +import html2markdown + +class FormattedText: + html = "" + + def __init__( + self, + markdown: str = None, + html: str = None + ) -> None: + if html is not None: + self.html = html + elif markdown is not None: + self.html = mistune.markdown(markdown) + + @property + def is_empty(self) -> bool: + return self.doc is None + + def __eq__(self, other) -> False: + if type(other) != type(self): + return False + if self.is_empty and other.is_empty: + return True + + return self.doc == other.doc + + @property + def markdown(self) -> str: + return html2markdown.convert(self.html) + + def __str__(self) -> str: + return self.markdown + + plaintext = markdown + diff --git a/music_kraken/objects/lint_default_factories.py b/music_kraken/objects/lint_default_factories.py new file mode 100644 index 0000000..a39f407 --- /dev/null +++ b/music_kraken/objects/lint_default_factories.py @@ -0,0 +1,96 @@ +from typing import List, TypeVar, Type + +from .country import Language +from .lyrics import Lyrics +from .parents import OuterProxy +from .song import Song, Album, Artist, Label +from .source import Source +from .target import Target + +T = TypeVar('T', bound=OuterProxy) +ALL_CLASSES: List[Type[T]] = [Song, Album, Artist, Label, Source, Target, Lyrics] + + +def print_lint_res(missing_values: dict): + print("_default_factories = {") + for key, value in missing_values.items(): + print(f'\t"{key}": {value},') + print("}") + +# def __init__(self, foo: str, bar) -> None: ... + +def lint_type(cls: T): + all_values: dict = {} + missing_values: dict = {} + + for key, value in cls.__annotations__.items(): + if value is None: + continue + + if (not key.islower()) or key.startswith("_") or (key.startswith("__") and key.endswith("__")): + continue + + if key in cls._default_factories: + continue + + factory = "lambda: None" + if isinstance(value, str): + if value == "SourceCollection": + factory = "SourceCollection" + elif "collection" in value.lower(): + factory = "Collection" + elif value.istitle(): + factory = value + else: + if value is Language: + factory = 'Language.by_alpha_2("en")' + else: + try: + value() + factory = value.__name__ + except TypeError: + pass + + missing_values[key] = factory + + if len(missing_values) > 0: + print(f"{cls.__name__}:") + print_lint_res(missing_values) + print() + else: + print(f"Everything is fine at {cls.__name__}") + + p = [] + s = [] + for key, value in cls.__annotations__.items(): + has_default = key in cls._default_factories + + if not isinstance(value, str): + value = value.__name__ + + if key.endswith("_collection"): + key = key.replace("_collection", "_list") + + if isinstance(value, str): + if value.startswith("Collection[") and value.endswith("]"): + value = value.replace("Collection", "List") + + if isinstance(value, str) and has_default: + value = value + " = None" + + p.append(f'{key}: {value}') + s.append(f'{key}={key}') + p.append("**kwargs") + s.append("**kwargs") + + print("# This is automatically generated") + print(f"def __init__(self, {', '.join(p)}) -> None:") + print(f"\tsuper().__init__({', '.join(s)})") + print() + + +def lint(): + for i in ALL_CLASSES: + lint_type(i) + + print() diff --git a/music_kraken/objects/lyrics.py b/music_kraken/objects/lyrics.py new file mode 100644 index 0000000..3650bfa --- /dev/null +++ b/music_kraken/objects/lyrics.py @@ -0,0 +1,39 @@ +from typing import List +from collections import defaultdict +import pycountry + +from .parents import OuterProxy +from .source import Source, SourceCollection +from .formatted_text import FormattedText +from .country import Language +from .metadata import ( + Mapping as id3Mapping, + ID3Timestamp, + Metadata +) + + +class Lyrics(OuterProxy): + text: FormattedText + language: Language + + source_collection: SourceCollection + + _default_factories = { + "text": FormattedText, + "language": lambda: Language.by_alpha_2("en"), + + "source_collection": SourceCollection, + } + + # This is automatically generated + def __init__(self, text: FormattedText = None, language: Language = None, source_list: SourceCollection = None, + **kwargs) -> None: + super().__init__(text=text, language=language, source_list=source_list, **kwargs) + + @property + def metadata(self) -> Metadata: + return Metadata({ + id3Mapping.UNSYNCED_LYRICS: self.text.html + }) + diff --git a/src/music_kraken/objects/metadata.py b/music_kraken/objects/metadata.py similarity index 100% rename from src/music_kraken/objects/metadata.py rename to music_kraken/objects/metadata.py diff --git a/music_kraken/objects/new_collection.py b/music_kraken/objects/new_collection.py new file mode 100644 index 0000000..1a556b6 --- /dev/null +++ b/music_kraken/objects/new_collection.py @@ -0,0 +1,257 @@ +from __future__ import annotations + +from collections import defaultdict +from typing import TypeVar, Generic, Dict, Optional, Iterable, List +from .parents import OuterProxy + +T = TypeVar('T', bound=OuterProxy) + + +class Collection(Generic[T]): + _data: List[T] + + _indexed_values: Dict[str, set] + _indexed_to_objects: Dict[any, list] + + shallow_list = property(fget=lambda self: self.data) + + def __init__( + self, + data: Optional[Iterable[T]] = None, + sync_on_append: Dict[str, "Collection"] = None, + contain_given_in_attribute: Dict[str, "Collection"] = None, + contain_attribute_in_given: Dict[str, "Collection"] = None, + append_object_to_attribute: Dict[str, T] = None + ) -> None: + self._contains_ids = set() + self._data = [] + self.upper_collections: List[Collection[T]] = [] + self.contained_collections: List[Collection[T]] = [] + + # List of collection attributes that should be modified on append + # Key: collection attribute (str) of appended element + # Value: main collection to sync to + self.sync_on_append: Dict[str, Collection] = sync_on_append or {} + self.contain_given_in_attribute: Dict[str, Collection] = contain_given_in_attribute or {} + self.contain_attribute_in_given: Dict[str, Collection] = contain_attribute_in_given or {} + self.append_object_to_attribute: Dict[str, T] = append_object_to_attribute or {} + + self.contain_self_on_append: List[str] = [] + + self._indexed_values = defaultdict(set) + self._indexed_to_objects = defaultdict(list) + + self.extend(data) + + def _map_element(self, __object: T, from_map: bool = False): + self._contains_ids.add(__object.id) + + for name, value in __object.indexing_values: + if value is None: + continue + + self._indexed_values[name].add(value) + self._indexed_to_objects[value].append(__object) + + if not from_map: + for attribute, new_object in self.contain_given_in_attribute.items(): + __object.__getattribute__(attribute).contain_collection_inside(new_object) + + for attribute, new_object in self.contain_given_in_attribute.items(): + new_object.contain_collection_inside(__object.__getattribute__(attribute)) + + for attribute, new_object in self.append_object_to_attribute.items(): + __object.__getattribute__(attribute).append(new_object, from_map=True) + + def _unmap_element(self, __object: T): + self._contains_ids.remove(__object.id) + + for name, value in __object.indexing_values: + if value is None: + continue + if value not in self._indexed_values[name]: + continue + + try: + self._indexed_to_objects[value].remove(__object) + except ValueError: + continue + + if not len(self._indexed_to_objects[value]): + self._indexed_values[name].remove(value) + + def _contained_in_self(self, __object: T) -> bool: + if __object.id in self._contains_ids: + return True + + for name, value in __object.indexing_values: + if value is None: + continue + if value in self._indexed_values[name]: + return True + return False + + def _get_root_collections(self) -> List["Collection"]: + if not len(self.upper_collections): + return [self] + + root_collections = [] + for upper_collection in self.upper_collections: + root_collections.extend(upper_collection._get_root_collections()) + return root_collections + + @property + def _is_root(self) -> bool: + return len(self.upper_collections) <= 0 + + def _contained_in_sub(self, __object: T, break_at_first: bool = True) -> List["Collection"]: + results = [] + + if self._contained_in_self(__object): + return [self] + + for collection in self.contained_collections: + results.extend(collection._contained_in_sub(__object, break_at_first=break_at_first)) + if break_at_first: + return results + + return results + + def _get_parents_of_multiple_contained_children(self, __object: T): + results = [] + if len(self.contained_collections) < 2 or self._contained_in_self(__object): + return results + + count = 0 + + for collection in self.contained_collections: + sub_results = collection._get_parents_of_multiple_contained_children(__object) + + if len(sub_results) > 0: + count += 1 + results.extend(sub_results) + + if count >= 2: + results.append(self) + + return results + + def _merge_in_self(self, __object: T, from_map: bool = False): + """ + 1. find existing objects + 2. merge into existing object + 3. remap existing object + """ + if __object.id in self._contains_ids: + return + + existing_object: DatabaseObject = None + + for name, value in __object.indexing_values: + if value is None: + continue + if value in self._indexed_values[name]: + existing_object = self._indexed_to_objects[value][0] + if existing_object.id == __object.id: + return None + + break + + if existing_object is None: + return None + + existing_object.merge(__object, replace_all_refs=True) + + # just a check if it really worked + if existing_object.id != __object.id: + raise ValueError("This should NEVER happen. Merging doesn't work.") + + self._map_element(existing_object, from_map=from_map) + + def contains(self, __object: T) -> bool: + return len(self._contained_in_sub(__object)) > 0 + + def _append(self, __object: T, from_map: bool = False): + for attribute, to_sync_with in self.sync_on_append.items(): + pass + to_sync_with.sync_with_other_collection(__object.__getattribute__(attribute)) + + self._map_element(__object, from_map=from_map) + self._data.append(__object) + + def append(self, __object: Optional[T], already_is_parent: bool = False, from_map: bool = False): + if __object is None: + return + if __object.id in self._contains_ids: + return + + exists_in_collection = self._contained_in_sub(__object) + if len(exists_in_collection) and self is exists_in_collection[0]: + # assuming that the object already is contained in the correct collections + if not already_is_parent: + self._merge_in_self(__object, from_map=from_map) + return + + if not len(exists_in_collection): + self._append(__object, from_map=from_map) + else: + pass + exists_in_collection[0]._merge_in_self(__object, from_map=from_map) + + if not already_is_parent or not self._is_root: + for parent_collection in self._get_parents_of_multiple_contained_children(__object): + pass + parent_collection.append(__object, already_is_parent=True, from_map=from_map) + + def extend(self, __iterable: Optional[Iterable[T]]): + if __iterable is None: + return + + for __object in __iterable: + self.append(__object) + + def sync_with_other_collection(self, equal_collection: "Collection"): + """ + If two collections always need to have the same values, this can be used. + + Internally: + 1. import the data from other to self + - _data + - contained_collections + 2. replace all refs from the other object, with refs from this object + """ + if equal_collection is self: + return + + # don't add the elements from the subelements from the other collection. + # this will be done in the next step. + self.extend(equal_collection._data) + # add all submodules + for equal_sub_collection in equal_collection.contained_collections: + self.contain_collection_inside(equal_sub_collection) + + # now the ugly part + # replace all refs of the other element with this one + self._risky_merge(equal_collection) + + def contain_collection_inside(self, sub_collection: "Collection"): + """ + This collection will ALWAYS contain everything from the passed in collection + """ + if sub_collection in self.contained_collections: + return + + self.contained_collections.append(sub_collection) + sub_collection.upper_collections.append(self) + + @property + def data(self) -> List[T]: + return [*self._data, + *(__object for collection in self.contained_collections for __object in collection.shallow_list)] + + def __len__(self) -> int: + return len(self._data) + sum(len(collection) for collection in self.contained_collections) + + def __iter__(self) -> Iterator[T]: + for element in self._data: + yield element diff --git a/music_kraken/objects/old_collection.py b/music_kraken/objects/old_collection.py new file mode 100644 index 0000000..4aa8f21 --- /dev/null +++ b/music_kraken/objects/old_collection.py @@ -0,0 +1,256 @@ +from typing import List, Iterable, Iterator, Optional, TypeVar, Generic, Dict, Type +from collections import defaultdict + +from .parents import DatabaseObject +from ..utils.support_classes.hacking import MetaClass + +T = TypeVar('T', bound=DatabaseObject) + + +class Collection(Generic[T]): + _data: List[T] + + _indexed_values: Dict[str, set] + _indexed_to_objects: Dict[any, list] + + shallow_list = property(fget=lambda self: self.data) + + def __init__( + self, data: Optional[Iterable[T]] = None, + sync_on_append: Dict[str, "Collection"] = None, + contain_given_in_attribute: Dict[str, "Collection"] = None, + contain_attribute_in_given: Dict[str, "Collection"] = None, + append_object_to_attribute: Dict[str, DatabaseObject] = None + ) -> None: + self._contains_ids = set() + self._data = [] + self.upper_collections: List[Collection[T]] = [] + self.contained_collections: List[Collection[T]] = [] + + # List of collection attributes that should be modified on append + # Key: collection attribute (str) of appended element + # Value: main collection to sync to + self.sync_on_append: Dict[str, Collection] = sync_on_append or {} + self.contain_given_in_attribute: Dict[str, Collection] = contain_given_in_attribute or {} + self.contain_attribute_in_given: Dict[str, Collection] = contain_attribute_in_given or {} + self.append_object_to_attribute: Dict[str, DatabaseObject] = append_object_to_attribute or {} + + self.contain_self_on_append: List[str] = [] + + self._indexed_values = defaultdict(set) + self._indexed_to_objects = defaultdict(list) + + self.extend(data) + + def _map_element(self, __object: T, from_map: bool = False): + self._contains_ids.add(__object.id) + + for name, value in __object.indexing_values: + if value is None: + continue + + self._indexed_values[name].add(value) + self._indexed_to_objects[value].append(__object) + + if not from_map: + for attribute, new_object in self.contain_given_in_attribute.items(): + __object.__getattribute__(attribute).contain_collection_inside(new_object) + + for attribute, new_object in self.contain_given_in_attribute.items(): + new_object.contain_collection_inside(__object.__getattribute__(attribute)) + + for attribute, new_object in self.append_object_to_attribute.items(): + __object.__getattribute__(attribute).append(new_object, from_map=True) + + def _unmap_element(self, __object: T): + self._contains_ids.remove(__object.id) + + for name, value in __object.indexing_values: + if value is None: + continue + if value not in self._indexed_values[name]: + continue + + try: + self._indexed_to_objects[value].remove(__object) + except ValueError: + continue + + if not len(self._indexed_to_objects[value]): + self._indexed_values[name].remove(value) + + def _contained_in_self(self, __object: T) -> bool: + if __object.id in self._contains_ids: + return True + + for name, value in __object.indexing_values: + if value is None: + continue + if value in self._indexed_values[name]: + return True + return False + + def _get_root_collections(self) -> List["Collection"]: + if not len(self.upper_collections): + return [self] + + root_collections = [] + for upper_collection in self.upper_collections: + root_collections.extend(upper_collection._get_root_collections()) + return root_collections + + @property + def _is_root(self) -> bool: + return len(self.upper_collections) <= 0 + + def _contained_in_sub(self, __object: T, break_at_first: bool = True) -> List["Collection"]: + results = [] + + if self._contained_in_self(__object): + return [self] + + for collection in self.contained_collections: + results.extend(collection._contained_in_sub(__object, break_at_first=break_at_first)) + if break_at_first: + return results + + return results + + def _get_parents_of_multiple_contained_children(self, __object: T): + results = [] + if len(self.contained_collections) < 2 or self._contained_in_self(__object): + return results + + count = 0 + + for collection in self.contained_collections: + sub_results = collection._get_parents_of_multiple_contained_children(__object) + + if len(sub_results) > 0: + count += 1 + results.extend(sub_results) + + if count >= 2: + results.append(self) + + return results + + def _merge_in_self(self, __object: T, from_map: bool = False): + """ + 1. find existing objects + 2. merge into existing object + 3. remap existing object + """ + if __object.id in self._contains_ids: + return + + existing_object: DatabaseObject = None + + for name, value in __object.indexing_values: + if value is None: + continue + if value in self._indexed_values[name]: + existing_object = self._indexed_to_objects[value][0] + if existing_object.id == __object.id: + return None + + break + + if existing_object is None: + return None + + existing_object.merge(__object, replace_all_refs=True) + + # just a check if it really worked + if existing_object.id != __object.id: + raise ValueError("This should NEVER happen. Merging doesn't work.") + + self._map_element(existing_object, from_map=from_map) + + def contains(self, __object: T) -> bool: + return len(self._contained_in_sub(__object)) > 0 + + def _append(self, __object: T, from_map: bool = False): + for attribute, to_sync_with in self.sync_on_append.items(): + pass + to_sync_with.sync_with_other_collection(__object.__getattribute__(attribute)) + + self._map_element(__object, from_map=from_map) + self._data.append(__object) + + def append(self, __object: Optional[T], already_is_parent: bool = False, from_map: bool = False): + if __object is None: + return + if __object.id in self._contains_ids: + return + + exists_in_collection = self._contained_in_sub(__object) + if len(exists_in_collection) and self is exists_in_collection[0]: + # assuming that the object already is contained in the correct collections + if not already_is_parent: + self._merge_in_self(__object, from_map=from_map) + return + + if not len(exists_in_collection): + self._append(__object, from_map=from_map) + else: + pass + exists_in_collection[0]._merge_in_self(__object, from_map=from_map) + + if not already_is_parent or not self._is_root: + for parent_collection in self._get_parents_of_multiple_contained_children(__object): + pass + parent_collection.append(__object, already_is_parent=True, from_map=from_map) + + def extend(self, __iterable: Optional[Iterable[T]]): + if __iterable is None: + return + + for __object in __iterable: + self.append(__object) + + def sync_with_other_collection(self, equal_collection: "Collection"): + """ + If two collections always need to have the same values, this can be used. + + Internally: + 1. import the data from other to self + - _data + - contained_collections + 2. replace all refs from the other object, with refs from this object + """ + if equal_collection is self: + return + + # don't add the elements from the subelements from the other collection. + # this will be done in the next step. + self.extend(equal_collection._data) + # add all submodules + for equal_sub_collection in equal_collection.contained_collections: + self.contain_collection_inside(equal_sub_collection) + + # now the ugly part + # replace all refs of the other element with this one + self._risky_merge(equal_collection) + + def contain_collection_inside(self, sub_collection: "Collection"): + """ + This collection will ALWAYS contain everything from the passed in collection + """ + if sub_collection in self.contained_collections: + return + + self.contained_collections.append(sub_collection) + sub_collection.upper_collections.append(self) + + @property + def data(self) -> List[T]: + return [*self._data, + *(__object for collection in self.contained_collections for __object in collection.shallow_list)] + + def __len__(self) -> int: + return len(self._data) + sum(len(collection) for collection in self.contained_collections) + + def __iter__(self) -> Iterator[T]: + for element in self._data: + yield element \ No newline at end of file diff --git a/src/music_kraken/objects/option.py b/music_kraken/objects/option.py similarity index 100% rename from src/music_kraken/objects/option.py rename to music_kraken/objects/option.py diff --git a/music_kraken/objects/parents.py b/music_kraken/objects/parents.py new file mode 100644 index 0000000..13ae41a --- /dev/null +++ b/music_kraken/objects/parents.py @@ -0,0 +1,243 @@ +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 .metadata import Metadata +from ..utils import get_unix_time +from ..utils.config import logging_settings, main_settings +from ..utils.shared import HIGHEST_ID +from ..utils.hacking import MetaClass + +LOGGER = logging_settings["object_logger"] + +P = TypeVar("P", bound="OuterProxy") + + +class InnerData: + """ + This is the core class, which is used for every Data class. + The attributes are set, and can be merged. + + The concept is, that the outer class proxies this class. + If the data in the wrapper class has to be merged, then this class is just replaced and garbage collected. + """ + + def __init__(self, **kwargs): + for key, value in kwargs.items(): + self.__setattr__(key, value) + + def __merge__(self, __other: InnerData, override: bool = False): + """ + TODO + is default is totally ignored + + :param __other: + :param override: + :return: + """ + + for key, value in __other.__dict__.copy().items(): + # just set the other value if self doesn't already have it + if key not in self.__dict__: + self.__setattr__(key, value) + continue + + # if the object of value implemented __merge__, it merges + existing = self.__getattribute__(key) + if hasattr(type(existing), "__merge__"): + existing.__merge__(value, override) + continue + + # override the existing value if requested + if override: + self.__setattr__(key, value) + + +class OuterProxy: + """ + Wraps the inner data, and provides apis, to naturally access those values. + """ + + _default_factories: dict = {} + _outer_attribute: Set[str] = {"options", "metadata", "indexing_values", "option_string"} + + DOWNWARDS_COLLECTION_STRING_ATTRIBUTES = tuple() + UPWARDS_COLLECTION_STRING_ATTRIBUTES = tuple() + + TITEL = "id" + + def __init__(self, _id: int = None, dynamic: bool = False, **kwargs): + _automatic_id: bool = False + + if _id is None and not dynamic: + """ + generates a random integer id + the range is defined in the config + """ + _id = random.randint(0, HIGHEST_ID) + _automatic_id = True + + kwargs["automatic_id"] = _automatic_id + kwargs["id"] = _id + kwargs["dynamic"] = dynamic + + for name, factory in type(self)._default_factories.items(): + if kwargs.get(name, None) is None: + kwargs[name] = factory() + + collection_data: Dict[str, list] = {} + for name, value in kwargs.copy().items(): + if isinstance(value, list) and name.endswith("_list"): + collection_name = name.replace("_list", "_collection") + collection_data[collection_name] = value + + del kwargs[name] + + self._fetched_from: dict = {} + self._inner: InnerData = InnerData(**kwargs) + self.__init_collections__() + + for name, data_list in collection_data.items(): + collection = self._inner.__getattribute__(name) + collection.extend(data_list) + + self._inner.__setattr__(name, collection) + + def __init_collections__(self): + pass + + def __getattribute__(self, __name: str) -> Any: + """ + Returns the attribute of _inner if the attribute exists, + else it returns the attribute of self. + + That the _inner gets checked first is essential for the type hints. + :param __name: + :return: + """ + + if __name.startswith("_") or __name in self._outer_attribute or __name.isupper(): + return object.__getattribute__(self, __name) + + _inner: InnerData = super().__getattribute__("_inner") + try: + return _inner.__getattribute__(__name) + except AttributeError: + return super().__getattribute__(__name) + + def __setattr__(self, __name, __value): + if not __name.startswith("_") and hasattr(self, "_inner"): + _inner: InnerData = super().__getattribute__("_inner") + return _inner.__setattr__(__name, __value) + + return super().__setattr__(__name, __value) + + def _add_other_db_objects(self, object_type: Type[OuterProxy], object_list: List[OuterProxy]): + pass + + def add_list_of_other_objects(self, object_list: List[OuterProxy]): + d: Dict[Type[OuterProxy], List[OuterProxy]] = defaultdict(list) + + for db_object in object_list: + d[type(db_object)].append(db_object) + + for key, value in d.items(): + self._add_other_db_objects(key, value) + + def __hash__(self): + """ + :raise: IsDynamicException + :return: + """ + + if self.dynamic: + return id(self._inner) + + return self.id + + def __eq__(self, other: Any): + return self.__hash__() == other.__hash__() + + def merge(self, __other: Optional[OuterProxy], override: bool = False): + """ + 1. merges the data of __other in self + 2. replaces the data of __other with the data of self + + :param __other: + :param override: + :return: + """ + if __other is None: + _ = "debug" + return + + self._inner.__merge__(__other._inner, override=override) + __other._inner = self._inner + + def mark_as_fetched(self, *url_hash_list: List[str]): + for url_hash in url_hash_list: + self._fetched_from[url_hash] = { + "time": get_unix_time(), + "url": url_hash, + } + + def already_fetched_from(self, url_hash: str) -> bool: + res = self._fetched_from.get(url_hash, None) + + if res is None: + return False + + return get_unix_time() - res["time"] < main_settings["refresh_after"] + + @property + def metadata(self) -> Metadata: + """ + This is an interface. + :return: + """ + return Metadata() + + @property + def options(self) -> List[P]: + return [self] + + @property + def indexing_values(self) -> List[Tuple[str, object]]: + """ + This is an interface. + It is supposed to return a map of the name and values for all important attributes. + This helps in comparing classes for equal data (e.g. being the same song but different attributes) + + TODO + Rewrite this approach into a approach, that is centered around statistics, and not binaries. + Instead of: one of this matches, it is the same + This: If enough attributes are similar enough, they are the same + + Returns: + List[Tuple[str, object]]: the first element in the tuple is the name of the attribute, the second the value. + """ + + return [] + + @property + @lru_cache() + def all_collections(self): + r = [] + + for key in self._default_factories: + val = self._inner.__getattribute__(key) + if hasattr(val, "__is_collection__"): + r.append(val) + + return r + + def __repr__(self): + return f"{type(self).__name__}({', '.join(key + ': ' + str(val) for key, val in self.indexing_values)})" + + @property + def title_string(self) -> str: + return str(self.__getattribute__(self.TITEL)) diff --git a/music_kraken/objects/song.py b/music_kraken/objects/song.py new file mode 100644 index 0000000..0116616 --- /dev/null +++ b/music_kraken/objects/song.py @@ -0,0 +1,689 @@ +from __future__ import annotations + +import random +from collections import defaultdict +from typing import List, Optional, Dict, Tuple, Type, Union + +import pycountry + +from ..utils.enums.album import AlbumType, AlbumStatus +from .collection import Collection +from .formatted_text import FormattedText +from .lyrics import Lyrics +from .contact import Contact +from .metadata import ( + Mapping as id3Mapping, + ID3Timestamp, + Metadata +) +from .option import Options +from .parents import OuterProxy, P +from .source import Source, SourceCollection +from .target import Target +from .country import Language, Country +from ..utils.string_processing import unify + +from .parents import OuterProxy as Base + +from ..utils.config import main_settings + +""" +All Objects dependent +""" + +CountryTyping = type(list(pycountry.countries)[0]) +OPTION_STRING_DELIMITER = " | " + + +class Song(Base): + title: str + unified_title: str + isrc: str + length: int + genre: str + note: FormattedText + tracksort: int + + source_collection: SourceCollection + target_collection: Collection[Target] + lyrics_collection: Collection[Lyrics] + main_artist_collection: Collection[Artist] + feature_artist_collection: Collection[Artist] + album_collection: Collection[Album] + + _default_factories = { + "note": FormattedText, + "length": lambda: 0, + "source_collection": SourceCollection, + "target_collection": Collection, + "lyrics_collection": Collection, + + "main_artist_collection": Collection, + "album_collection": Collection, + "feature_artist_collection": Collection, + + "title": lambda: "", + "unified_title": lambda: None, + "isrc": lambda: None, + "genre": lambda: None, + + "tracksort": lambda: 0, + } + + def __init__(self, title: str = "", unified_title: str = None, isrc: str = None, length: int = None, + genre: str = None, note: FormattedText = None, source_list: List[Source] = None, + target_list: List[Target] = None, lyrics_list: List[Lyrics] = None, + main_artist_list: List[Artist] = None, feature_artist_list: List[Artist] = None, + album_list: List[Album] = None, tracksort: int = 0, **kwargs) -> None: + + Base.__init__(**locals()) + + UPWARDS_COLLECTION_STRING_ATTRIBUTES = ("album_collection", "main_artist_collection", "feature_artist_collection") + TITEL = "title" + + def __init_collections__(self) -> None: + self.album_collection.contain_given_in_attribute = { + "artist_collection": self.main_artist_collection, + } + self.album_collection.append_object_to_attribute = { + "song_collection": self, + } + + self.main_artist_collection.contain_given_in_attribute = { + "main_album_collection": self.album_collection + } + self.feature_artist_collection.append_object_to_attribute = { + "feature_song_collection": self + } + + def _add_other_db_objects(self, object_type: Type[OuterProxy], object_list: List[OuterProxy]): + if object_type is Song: + return + + if isinstance(object_list, Lyrics): + self.lyrics_collection.extend(object_list) + return + + if isinstance(object_list, Artist): + self.main_artist_collection.extend(object_list) + return + + if isinstance(object_list, Album): + self.album_collection.extend(object_list) + return + + @property + def indexing_values(self) -> List[Tuple[str, object]]: + return [ + ('id', self.id), + ('title', self.unified_title), + ('isrc', self.isrc), + *[('url', source.url) for source in self.source_collection] + ] + + @property + def metadata(self) -> Metadata: + metadata = Metadata({ + id3Mapping.TITLE: [self.title], + id3Mapping.ISRC: [self.isrc], + id3Mapping.LENGTH: [self.length], + id3Mapping.GENRE: [self.genre], + id3Mapping.TRACKNUMBER: [self.tracksort_str], + id3Mapping.COMMENT: [self.note.markdown], + }) + + # metadata.merge_many([s.get_song_metadata() for s in self.source_collection]) album sources have no relevant metadata for id3 + metadata.merge_many([a.metadata for a in self.album_collection]) + metadata.merge_many([a.metadata for a in self.main_artist_collection]) + metadata.merge_many([a.metadata for a in self.feature_artist_collection]) + metadata.merge_many([lyrics.metadata for lyrics in self.lyrics_collection]) + + return metadata + + def get_artist_credits(self) -> str: + main_artists = ", ".join([artist.name for artist in self.main_artist_collection]) + feature_artists = ", ".join([artist.name for artist in self.feature_artist_collection]) + + if len(feature_artists) == 0: + return main_artists + return f"{main_artists} feat. {feature_artists}" + + def __repr__(self) -> str: + return f"Song(\"{self.title}\")" + + @property + def option_string(self) -> str: + return f"{self.__repr__()} " \ + f"from Album({OPTION_STRING_DELIMITER.join(album.title for album in self.album_collection)}) " \ + f"by Artist({OPTION_STRING_DELIMITER.join(artist.name for artist in self.main_artist_collection)}) " \ + f"feat. Artist({OPTION_STRING_DELIMITER.join(artist.name for artist in self.feature_artist_collection)})" + + @property + def options(self) -> List[P]: + options = self.main_artist_collection.shallow_list + options.extend(self.feature_artist_collection) + options.extend(self.album_collection) + options.append(self) + return options + + @property + def tracksort_str(self) -> str: + """ + if the album tracklist is empty, it sets it length to 1, this song has to be on the Album + :returns id3_tracksort: {song_position}/{album.length_of_tracklist} + """ + if len(self.album_collection) == 0: + return f"{self.tracksort}" + + return f"{self.tracksort}/{len(self.album_collection[0].song_collection) or 1}" + + +""" +All objects dependent on Album +""" + + +class Album(Base): + title: str + unified_title: str + album_status: AlbumStatus + album_type: AlbumType + language: Language + date: ID3Timestamp + barcode: str + albumsort: int + notes: FormattedText + + source_collection: SourceCollection + artist_collection: Collection[Artist] + song_collection: Collection[Song] + label_collection: Collection[Label] + + _default_factories = { + "title": lambda: None, + "unified_title": lambda: None, + "album_status": lambda: None, + "barcode": lambda: None, + "albumsort": lambda: None, + + "album_type": lambda: AlbumType.OTHER, + "language": lambda: Language.by_alpha_2("en"), + "date": ID3Timestamp, + "notes": FormattedText, + + "source_collection": SourceCollection, + "artist_collection": Collection, + "song_collection": Collection, + "label_collection": Collection, + } + + TITEL = "title" + + # This is automatically generated + def __init__(self, title: str = None, unified_title: str = None, album_status: AlbumStatus = None, + album_type: AlbumType = None, language: Language = None, date: ID3Timestamp = None, + barcode: str = None, albumsort: int = None, notes: FormattedText = None, + source_list: List[Source] = None, artist_list: List[Artist] = None, song_list: List[Song] = None, + label_list: List[Label] = None, **kwargs) -> None: + super().__init__(title=title, unified_title=unified_title, album_status=album_status, album_type=album_type, + language=language, date=date, barcode=barcode, albumsort=albumsort, notes=notes, + source_list=source_list, artist_list=artist_list, song_list=song_list, label_list=label_list, + **kwargs) + + DOWNWARDS_COLLECTION_STRING_ATTRIBUTES = ("song_collection",) + UPWARDS_COLLECTION_STRING_ATTRIBUTES = ("artist_collection", "label_collection") + + def __init_collections__(self): + self.song_collection.contain_attribute_in_given = { + "main_artist_collection": self.artist_collection + } + + def _add_other_db_objects(self, object_type: Type[OuterProxy], object_list: List[OuterProxy]): + if object_type is Song: + self.song_collection.extend(object_list) + return + + if object_type is Artist: + self.artist_collection.extend(object_list) + return + + if object_type is Album: + return + + if object_type is Label: + self.label_collection.extend(object_list) + return + + @property + def indexing_values(self) -> List[Tuple[str, object]]: + return [ + ('id', self.id), + ('title', self.unified_title), + ('barcode', self.barcode), + *[('url', source.url) for source in self.source_collection] + ] + + @property + def metadata(self) -> Metadata: + """ + TODO + - barcode + :return: + """ + return Metadata({ + id3Mapping.ALBUM: [self.title], + id3Mapping.COPYRIGHT: [self.copyright], + id3Mapping.LANGUAGE: [self.iso_639_2_lang], + id3Mapping.ALBUM_ARTIST: [a.name for a in self.artist_collection], + id3Mapping.DATE: [self.date.strftime("%d%m")] if self.date.has_year and self.date.has_month else [], + id3Mapping.TIME: [self.date.strftime(("%H%M"))] if self.date.has_hour and self.date.has_minute else [], + id3Mapping.YEAR: [str(self.date.year).zfill(4)] if self.date.has_year else [], + id3Mapping.RELEASE_DATE: [self.date.timestamp], + id3Mapping.ORIGINAL_RELEASE_DATE: [self.date.timestamp], + id3Mapping.ALBUMSORTORDER: [str(self.albumsort)] if self.albumsort is not None else [] + }) + + def __repr__(self): + return f"Album(\"{self.title}\")" + + @property + def option_string(self) -> str: + return f"{self.__repr__()} " \ + f"by Artist({OPTION_STRING_DELIMITER.join([artist.name for artist in self.artist_collection])}) " \ + f"under Label({OPTION_STRING_DELIMITER.join([label.name for label in self.label_collection])})" + + @property + def options(self) -> List[P]: + options = [*self.artist_collection, self, *self.song_collection] + + return options + + def update_tracksort(self): + """ + This updates the tracksort attributes, of the songs in + `self.song_collection`, and sorts the songs, if possible. + + It is advised to only call this function, once all the tracks are + added to the songs. + + :return: + """ + + if self.song_collection.empty: + return + + tracksort_map: Dict[int, Song] = { + song.tracksort: song for song in self.song_collection if song.tracksort is not None + } + + # place the songs, with set tracksort attribute according to it + for tracksort, song in tracksort_map.items(): + index = tracksort - 1 + + """ + I ONLY modify the `Collection._data` attribute directly, + to bypass the mapping of the attributes, because I will add the item in the next step + """ + + """ + but for some reason, neither + `self.song_collection._data.index(song)` + `self.song_collection._data.remove(song)` + get the right object. + + I have NO FUCKING CLUE why xD + But I just implemented it myself. + """ + for old_index, temp_song in enumerate(self.song_collection._data): + if song is temp_song: + break + + # the list can't be empty + del self.song_collection._data[old_index] + self.song_collection._data.insert(index, song) + + # fill in the empty tracksort attributes + for i, song in enumerate(self.song_collection): + if song.tracksort is not None: + continue + song.tracksort = i + 1 + + def compile(self, merge_into: bool = False): + """ + compiles the recursive structures, + and does depending on the object some other stuff. + + no need to override if only the recursive structure should be built. + override self.build_recursive_structures() instead + """ + + self.update_tracksort() + self._build_recursive_structures(build_version=random.randint(0, 99999), merge=merge_into) + + @property + def copyright(self) -> str: + if self.date is None: + return "" + if self.date.has_year or len(self.label_collection) == 0: + return "" + + return f"{self.date.year} {self.label_collection[0].name}" + + @property + def iso_639_2_lang(self) -> Optional[str]: + if self.language is None: + return None + + return self.language.alpha_3 + + @property + def is_split(self) -> bool: + """ + A split Album is an Album from more than one Artists + usually half the songs are made by one Artist, the other half by the other one. + In this case split means either that or one artist featured by all songs. + :return: + """ + return len(self.artist_collection) > 1 + + @property + def album_type_string(self) -> str: + return self.album_type.value + + +""" +All objects dependent on Artist +""" + + +class Artist(Base): + name: str + unified_name: str + country: Country + formed_in: ID3Timestamp + notes: FormattedText + lyrical_themes: List[str] + + general_genre: str + unformated_location: str + + source_collection: SourceCollection + contact_collection: Collection[Contact] + + feature_song_collection: Collection[Song] + main_album_collection: Collection[Album] + label_collection: Collection[Label] + + _default_factories = { + "name": str, + "unified_name": lambda: None, + "country": lambda: None, + "unformated_location": lambda: None, + + "formed_in": ID3Timestamp, + "notes": FormattedText, + "lyrical_themes": list, + "general_genre": lambda: "", + + "source_collection": SourceCollection, + "feature_song_collection": Collection, + "main_album_collection": Collection, + "contact_collection": Collection, + "label_collection": Collection, + } + + TITEL = "name" + + # This is automatically generated + def __init__(self, name: str = "", unified_name: str = None, country: Country = None, + formed_in: ID3Timestamp = None, notes: FormattedText = None, lyrical_themes: List[str] = None, + general_genre: str = None, unformated_location: str = None, source_list: List[Source] = None, + contact_list: List[Contact] = None, feature_song_list: List[Song] = None, + main_album_list: List[Album] = None, label_list: List[Label] = None, **kwargs) -> None: + + super().__init__(name=name, unified_name=unified_name, country=country, formed_in=formed_in, notes=notes, + lyrical_themes=lyrical_themes, general_genre=general_genre, + unformated_location=unformated_location, source_list=source_list, contact_list=contact_list, + feature_song_list=feature_song_list, main_album_list=main_album_list, label_list=label_list, + **kwargs) + + DOWNWARDS_COLLECTION_STRING_ATTRIBUTES = ("feature_song_collection", "main_album_collection") + UPWARDS_COLLECTION_STRING_ATTRIBUTES = ("label_collection",) + + def __init_collections__(self): + self.feature_song_collection.append_object_to_attribute = { + "feature_artist_collection": self + } + + self.main_album_collection.append_object_to_attribute = { + "artist_collection": self + } + + self.label_collection.append_object_to_attribute = { + "current_artist_collection": self + } + + def _add_other_db_objects(self, object_type: Type[OuterProxy], object_list: List[OuterProxy]): + if object_type is Song: + # this doesn't really make sense + # self.feature_song_collection.extend(object_list) + return + + if object_type is Artist: + return + + if object_type is Album: + self.main_album_collection.extend(object_list) + return + + if object_type is Label: + self.label_collection.extend(object_list) + return + + @property + def options(self) -> List[P]: + options = [self, *self.main_album_collection.shallow_list, *self.feature_album] + print(options) + return options + + def update_albumsort(self): + """ + This updates the albumsort attributes, of the albums in + `self.main_album_collection`, and sorts the albums, if possible. + + It is advised to only call this function, once all the albums are + added to the artist. + + :return: + """ + if len(self.main_album_collection) <= 0: + return + + type_section: Dict[AlbumType, int] = defaultdict(lambda: 2, { + AlbumType.OTHER: 0, # if I don't know it, I add it to the first section + AlbumType.STUDIO_ALBUM: 0, + AlbumType.EP: 0, + AlbumType.SINGLE: 1 + }) if main_settings["sort_album_by_type"] else defaultdict(lambda: 0) + + sections = defaultdict(list) + + # order albums in the previously defined section + album: Album + for album in self.main_album_collection: + sections[type_section[album.album_type]].append(album) + + def sort_section(_section: List[Album], last_albumsort: int) -> int: + # album is just a value used in loops + nonlocal album + + if main_settings["sort_by_date"]: + _section.sort(key=lambda _album: _album.date, reverse=True) + + new_last_albumsort = last_albumsort + + for album_index, album in enumerate(_section): + if album.albumsort is None: + album.albumsort = new_last_albumsort = album_index + 1 + last_albumsort + + _section.sort(key=lambda _album: _album.albumsort) + + return new_last_albumsort + + # sort the sections individually + _last_albumsort = 1 + for section_index in sorted(sections): + _last_albumsort = sort_section(sections[section_index], _last_albumsort) + + # merge all sections again + album_list = [] + for section_index in sorted(sections): + album_list.extend(sections[section_index]) + + # replace the old collection with the new one + self.main_album_collection: Collection = Collection(data=album_list, element_type=Album) + + @property + def indexing_values(self) -> List[Tuple[str, object]]: + return [ + ('id', self.id), + ('name', self.unified_name), + *[('url', source.url) for source in self.source_collection], + *[('contact', contact.value) for contact in self.contact_collection] + ] + + @property + def metadata(self) -> Metadata: + metadata = Metadata({ + id3Mapping.ARTIST: [self.name] + }) + metadata.merge_many([s.get_artist_metadata() for s in self.source_collection]) + + return metadata + + """ + def __str__(self, include_notes: bool = False): + string = self.name or "" + if include_notes: + plaintext_notes = self.notes.get_plaintext() + if plaintext_notes is not None: + string += "\n" + plaintext_notes + return string + """ + + def __repr__(self): + return f"Artist(\"{self.name}\")" + + @property + def option_string(self) -> str: + return f"{self.__repr__()} " \ + f"under Label({OPTION_STRING_DELIMITER.join([label.name for label in self.label_collection])})" + + @property + def options(self) -> List[P]: + options = [self] + options.extend(self.main_album_collection) + options.extend(self.feature_song_collection) + return options + + @property + def feature_album(self) -> Album: + return Album( + title="features", + album_status=AlbumStatus.UNRELEASED, + album_type=AlbumType.COMPILATION_ALBUM, + is_split=True, + albumsort=666, + dynamic=True, + song_list=self.feature_song_collection.shallow_list + ) + + def get_all_songs(self) -> List[Song]: + """ + returns a list of all Songs. + probably not that useful, because it is unsorted + """ + collection = self.feature_song_collection.copy() + for album in self.discography: + collection.extend(album.song_collection) + + return collection + + @property + def discography(self) -> List[Album]: + flat_copy_discography = self.main_album_collection.copy() + flat_copy_discography.append(self.feature_album) + + return flat_copy_discography + + +""" +Label +""" + + +class Label(Base): + COLLECTION_STRING_ATTRIBUTES = ("album_collection", "current_artist_collection") + + DOWNWARDS_COLLECTION_STRING_ATTRIBUTES = COLLECTION_STRING_ATTRIBUTES + + name: str + unified_name: str + notes: FormattedText + + source_collection: SourceCollection + contact_collection: Collection[Contact] + + album_collection: Collection[Album] + current_artist_collection: Collection[Artist] + + _default_factories = { + "notes": FormattedText, + "album_collection": Collection, + "current_artist_collection": Collection, + "source_collection": SourceCollection, + "contact_collection": Collection, + "name": lambda: None, + "unified_name": lambda: None, + } + + TITEL = "name" + + def __init__(self, name: str = None, unified_name: str = None, notes: FormattedText = None, + source_list: List[Source] = None, contact_list: List[Contact] = None, + album_list: List[Album] = None, current_artist_list: List[Artist] = None, **kwargs) -> None: + super().__init__(name=name, unified_name=unified_name, notes=notes, source_list=source_list, + contact_list=contact_list, album_list=album_list, current_artist_list=current_artist_list, + **kwargs) + + @property + def indexing_values(self) -> List[Tuple[str, object]]: + return [ + ('id', self.id), + ('name', self.unified_name), + *[('url', source.url) for source in self.source_collection] + ] + + def _add_other_db_objects(self, object_type: Type[OuterProxy], object_list: List[OuterProxy]): + if object_type is Song: + return + + if object_type is Artist: + self.current_artist_collection.extend(object_list) + return + + if object_type is Album: + self.album_collection.extend(object_list) + return + + @property + def options(self) -> List[P]: + options = [self] + options.extend(self.current_artist_collection.shallow_list) + options.extend(self.album_collection.shallow_list) + + return options + + @property + def option_string(self): + return self.__repr__() diff --git a/src/music_kraken/objects/source.py b/music_kraken/objects/source.py similarity index 75% rename from src/music_kraken/objects/source.py rename to music_kraken/objects/source.py index 38fd062..a60ce6d 100644 --- a/src/music_kraken/objects/source.py +++ b/music_kraken/objects/source.py @@ -1,47 +1,36 @@ from collections import defaultdict from enum import Enum -from typing import List, Dict, Set, Tuple, Optional +from typing import List, Dict, Set, Tuple, Optional, Iterable from urllib.parse import urlparse from ..utils.enums.source import SourcePages, SourceTypes from ..utils.config import youtube_settings from .metadata import Mapping, Metadata -from .parents import DatabaseObject +from .parents import OuterProxy from .collection import Collection -class Source(DatabaseObject): - """ - create somehow like that - ```python - # url won't be a valid one due to it being just an example - Source(src="youtube", url="https://youtu.be/dfnsdajlhkjhsd") - ``` - """ - COLLECTION_ATTRIBUTES = tuple() - SIMPLE_ATTRIBUTES = { - "page_enum": None, - "url": None, - "referer_page": None, - "audio_url": None +class Source(OuterProxy): + url: str + + page_enum: SourcePages + referer_page: SourcePages + + audio_url: str + + _default_factories = { + "audio_url": lambda: None, } - def __init__( - self, - page_enum: SourcePages, - url: str = None, - id_: str = None, - referer_page: SourcePages = None, - adio_url: str = None - ) -> None: - DatabaseObject.__init__(self, id_=id_) + # This is automatically generated + def __init__(self, page_enum: SourcePages, url: str, referer_page: SourcePages = None, audio_url: str = None, + **kwargs) -> None: - self.page_enum = page_enum - self.referer_page = page_enum if referer_page is None else referer_page + if referer_page is None: + referer_page = page_enum - self.url = url - self.audio_url = adio_url + super().__init__(url=url, page_enum=page_enum, referer_page=referer_page, audio_url=audio_url, **kwargs) @classmethod def match_url(cls, url: str, referer_page: SourcePages) -> Optional["Source"]: @@ -97,6 +86,10 @@ class Source(DatabaseObject): Mapping.ARTIST_WEBPAGE_URL: [self.url] }) + @property + def hash_url(self) -> str: + return self.url.strip().lower().lstrip("https://").lstrip("http://") + @property def metadata(self) -> Metadata: return self.get_song_metadata() @@ -121,15 +114,15 @@ class Source(DatabaseObject): class SourceCollection(Collection): - def __init__(self, source_list: List[Source]): + def __init__(self, data: Optional[Iterable[Source]] = None, **kwargs): self._page_to_source_list: Dict[SourcePages, List[Source]] = defaultdict(list) - super().__init__(data=source_list, element_type=Source) + super().__init__(data=data, **kwargs) - def map_element(self, source: Source): - super().map_element(source) + def _map_element(self, __object: Source, **kwargs): + super()._map_element(__object, **kwargs) - self._page_to_source_list[source.page_enum].append(source) + self._page_to_source_list[__object.page_enum].append(__object) @property def source_pages(self) -> Set[SourcePages]: diff --git a/src/music_kraken/objects/target.py b/music_kraken/objects/target.py similarity index 73% rename from src/music_kraken/objects/target.py rename to music_kraken/objects/target.py index ab405a4..35afd4a 100644 --- a/src/music_kraken/objects/target.py +++ b/music_kraken/objects/target.py @@ -1,11 +1,13 @@ +from __future__ import annotations + from pathlib import Path -from typing import List, Tuple, TextIO +from typing import List, Tuple, TextIO, Union import logging import requests from tqdm import tqdm -from .parents import DatabaseObject +from .parents import OuterProxy from ..utils.config import main_settings, logging_settings from ..utils.string_processing import fit_to_file_system @@ -13,7 +15,7 @@ from ..utils.string_processing import fit_to_file_system LOGGER = logging.getLogger("target") -class Target(DatabaseObject): +class Target(OuterProxy): """ create somehow like that ```python @@ -22,32 +24,26 @@ class Target(DatabaseObject): ``` """ - SIMPLE_ATTRIBUTES = { - "_file": None, - "_path": None - } - COLLECTION_ATTRIBUTES = tuple() + file_path: Path - def __init__( - self, - file: str = None, - path: str = None, - dynamic: bool = False, - relative_to_music_dir: bool = False - ) -> None: - super().__init__(dynamic=dynamic) - self._file: Path = Path(fit_to_file_system(file)) - self._path: Path = fit_to_file_system(Path(main_settings["music_directory"], path) if relative_to_music_dir else Path(path)) + _default_factories = { + } + + # This is automatically generated + def __init__(self, file_path: Union[Path, str], relative_to_music_dir: bool = False, **kwargs) -> None: + if not isinstance(file_path, Path): + file_path = Path(file_path) + + if relative_to_music_dir: + file_path = Path(main_settings["music_directory"], file_path) + + super().__init__(file_path=fit_to_file_system(file_path), **kwargs) self.is_relative_to_music_dir: bool = relative_to_music_dir def __repr__(self) -> str: return str(self.file_path) - @property - def file_path(self) -> Path: - return Path(self._path, self._file) - @property def indexing_values(self) -> List[Tuple[str, object]]: return [('filepath', self.file_path)] @@ -68,14 +64,14 @@ class Target(DatabaseObject): return self.file_path.stat().st_size def create_path(self): - self._path.mkdir(parents=True, exist_ok=True) + self.file_path.parent.mkdir(parents=True, exist_ok=True) - def copy_content(self, copy_to: "Target"): + def copy_content(self, copy_to: Target): if not self.exists: LOGGER.warning(f"No file exists at: {self.file_path}") return - with self.open("rb") as read_from: + with open(self.file_path, "rb") as read_from: copy_to.create_path() with open(copy_to.file_path, "wb") as write_to: write_to.write(read_from.read()) @@ -88,7 +84,7 @@ class Target(DatabaseObject): total_size = int(r.headers.get('content-length')) - with self.open('wb') as f: + with open(self.file_path, 'wb') as f: try: """ https://en.wikipedia.org/wiki/Kilobyte diff --git a/src/music_kraken/pages/__init__.py b/music_kraken/pages/__init__.py similarity index 86% rename from src/music_kraken/pages/__init__.py rename to music_kraken/pages/__init__.py index a317147..5757a2c 100644 --- a/src/music_kraken/pages/__init__.py +++ b/music_kraken/pages/__init__.py @@ -2,5 +2,6 @@ from .encyclopaedia_metallum import EncyclopaediaMetallum from .musify import Musify from .youtube import YouTube from .youtube_music import YoutubeMusic +from .bandcamp import Bandcamp from .abstract import Page, INDEPENDENT_DB_OBJECTS diff --git a/src/music_kraken/pages/abstract.py b/music_kraken/pages/abstract.py similarity index 75% rename from src/music_kraken/pages/abstract.py rename to music_kraken/pages/abstract.py index bffc15c..9547298 100644 --- a/src/music_kraken/pages/abstract.py +++ b/music_kraken/pages/abstract.py @@ -1,6 +1,8 @@ import logging import random +import re from copy import copy +from pathlib import Path from typing import Optional, Union, Type, Dict, Set, List, Tuple from string import Formatter @@ -26,7 +28,7 @@ from ..utils.config import main_settings from ..utils.support_classes.query import Query from ..utils.support_classes.download_result import DownloadResult from ..utils.string_processing import fit_to_file_system - +from ..utils import trace INDEPENDENT_DB_OBJECTS = Union[Label, Album, Artist, Song] INDEPENDENT_DB_TYPES = Union[Type[Song], Type[Album], Type[Artist], Type[Label]] @@ -41,22 +43,22 @@ class NamingDict(dict): "album": "album.title", "album_type": "album.album_type_string" } - + def __init__(self, values: dict, object_mappings: Dict[str, DatabaseObject] = None): self.object_mappings: Dict[str, DatabaseObject] = object_mappings or dict() - + super().__init__(values) self["audio_format"] = main_settings["audio_format"] - + def add_object(self, music_object: DatabaseObject): self.object_mappings[type(music_object).__name__.lower()] = music_object - + def copy(self) -> dict: return type(self)(super().copy(), self.object_mappings.copy()) - + def __getitem__(self, key: str) -> str: return fit_to_file_system(super().__getitem__(key)) - + def default_value_for_name(self, name: str) -> str: return f'Various {name.replace("_", " ").title()}' @@ -66,23 +68,23 @@ class NamingDict(dict): return self.default_value_for_name(key) key = self.CUSTOM_KEYS[key] - + frag_list = key.split(".") - + object_name = frag_list[0].strip().lower() attribute_name = frag_list[-1].strip().lower() if object_name not in self.object_mappings: return self.default_value_for_name(attribute_name) - + music_object = self.object_mappings[object_name] try: value = getattr(music_object, attribute_name) if value is None: return self.default_value_for_name(attribute_name) - + return str(value) - + except AttributeError: return self.default_value_for_name(attribute_name) @@ -132,122 +134,96 @@ def _clean_song(song: Song, collections: Dict[INDEPENDENT_DB_TYPES, Collection]) _clean_collection(song.feature_artist_collection, collections) _clean_collection(song.main_artist_collection, collections) -def clean_object(dirty_object: DatabaseObject) -> DatabaseObject: - if isinstance(dirty_object, INDEPENDENT_DB_OBJECTS): - collections = { - Label: Collection(element_type=Label), - Artist: Collection(element_type=Artist), - Album: Collection(element_type=Album), - Song: Collection(element_type=Song) - } - - if isinstance(dirty_object, Song): - return dirty_object - - _clean_music_object(dirty_object, collections) - return dirty_object - -def build_new_object(new_object: DatabaseObject) -> DatabaseObject: - new_object = clean_object(new_object) - new_object.compile(merge_into=False) - - return new_object - -def merge_together(old_object: DatabaseObject, new_object: DatabaseObject, do_compile: bool = True) -> DatabaseObject: - new_object = clean_object(new_object) - - old_object.merge(new_object) - if do_compile and False: - old_object.compile(merge_into=False) - - return old_object - class Page: """ This is an abstract class, laying out the functionality for every other class fetching something """ - + SOURCE_TYPE: SourcePages LOGGER = logging.getLogger("this shouldn't be used") - + # set this to true, if all song details can also be fetched by fetching album details NO_ADDITIONAL_DATA_FROM_SONG = False - - - def __init__(self): - super().__init__() - - """ - CODE I NEED WHEN I START WITH MULTITHREADING - - def __init__(self, end_event: EndThread, search_queue: Queue, search_result_queue: Queue): - self.end_event = end_event - - self.search_queue = search_queue - self.search_result_queue = search_result_queue - - super().__init__() - - @property - def _empty_working_queues(self): - return self.search_queue.empty() - def run(self) -> None: - while bool(self.end_event) and self._empty_working_queues: - if not self.search_queue.empty(): - self.search(self.search_queue.get()) - self.search_result_queue.put(FinishedSearch()) - continue - """ - + def _search_regex(self, pattern, string, default=None, fatal=True, flags=0, group=None): + """ + Perform a regex search on the given string, using a single or a list of + patterns returning the first matching group. + In case of failure return a default value or raise a WARNING or a + RegexNotFoundError, depending on fatal, specifying the field name. + """ + + if isinstance(pattern, str): + mobj = re.search(pattern, string, flags) + else: + for p in pattern: + mobj = re.search(p, string, flags) + if mobj: + break + + if mobj: + if group is None: + # return the first matching group + return next(g for g in mobj.groups() if g is not None) + elif isinstance(group, (list, tuple)): + return tuple(mobj.group(g) for g in group) + else: + return mobj.group(group) + + return default + def get_source_type(self, source: Source) -> Optional[Type[DatabaseObject]]: return None - + def get_soup_from_response(self, r: requests.Response) -> BeautifulSoup: return BeautifulSoup(r.content, "html.parser") # to search stuff def search(self, query: Query) -> List[DatabaseObject]: music_object = query.music_object - + search_functions = { Song: self.song_search, Album: self.album_search, Artist: self.artist_search, Label: self.label_search } - + if type(music_object) in search_functions: r = search_functions[type(music_object)](music_object) if r is not None and len(r) > 0: return r - + r = [] for default_query in query.default_search: for single_option in self.general_search(default_query): r.append(single_option) - + return r - + def general_search(self, search_query: str) -> List[DatabaseObject]: return [] - + def label_search(self, label: Label) -> List[Label]: return [] - + def artist_search(self, artist: Artist) -> List[Artist]: return [] - + def album_search(self, album: Album) -> List[Album]: return [] - + def song_search(self, song: Song) -> List[Song]: return [] - - def fetch_details(self, music_object: DatabaseObject, stop_at_level: int = 1, post_process: bool = True) -> DatabaseObject: + def fetch_details( + self, + music_object: DatabaseObject, + stop_at_level: int = 1, + post_process: bool = True + ) -> DatabaseObject: """ when a music object with lacking data is passed in, it returns the SAME object **(no copy)** with more detailed data. @@ -263,24 +239,48 @@ class Page: this gets ignored :return detailed_music_object: IT MODIFIES THE INPUT OBJ """ - # creating a new object, of the same type - new_music_object: DatabaseObject = type(music_object)() + new_music_object: Optional[DatabaseObject] = None + fetched_from_url: List[str] = [] # only certain database objects, have a source list if isinstance(music_object, INDEPENDENT_DB_OBJECTS): source: Source for source in music_object.source_collection.get_sources_from_page(self.SOURCE_TYPE): - new_music_object.merge(self.fetch_object_from_source( - source=source, - enforce_type=type(music_object), - stop_at_level=stop_at_level, - post_process=False - )) + if music_object.already_fetched_from(source.hash_url): + continue - return music_object.merge(new_music_object) + tmp = self.fetch_object_from_source( + source=source, + enforce_type=type(music_object), + stop_at_level=stop_at_level, + post_process=False, + type_string=type(music_object).__name__, + title_string=music_object.title_string, + ) + + if new_music_object is None: + new_music_object = tmp + else: + new_music_object.merge(tmp) + fetched_from_url.append(source.hash_url) + + if new_music_object is not None: + music_object.merge(new_music_object) + + music_object.mark_as_fetched(*fetched_from_url) + return music_object + + def fetch_object_from_source( + self, + source: Source, + stop_at_level: int = 2, + enforce_type: Type[DatabaseObject] = None, + post_process: bool = True, + type_string: str = "", + title_string: str = "", + ) -> Optional[DatabaseObject]: - def fetch_object_from_source(self, source: Source, stop_at_level: int = 2, enforce_type: Type[DatabaseObject] = None, post_process: bool = True) -> Optional[DatabaseObject]: obj_type = self.get_source_type(source) if obj_type is None: @@ -289,32 +289,35 @@ class Page: if enforce_type != obj_type and enforce_type is not None: self.LOGGER.warning(f"Object type isn't type to enforce: {enforce_type}, {obj_type}") return None - + music_object: DatabaseObject = None - + fetch_map = { Song: self.fetch_song, Album: self.fetch_album, Artist: self.fetch_artist, Label: self.fetch_label } - + if obj_type in fetch_map: music_object = fetch_map[obj_type](source, stop_at_level) else: self.LOGGER.warning(f"Can't fetch details of type: {obj_type}") return None - if stop_at_level > 1: + if stop_at_level > 0: + trace(f"fetching {type_string} [{title_string}] [stop_at_level={stop_at_level}]") + collection: Collection for collection_str in music_object.DOWNWARDS_COLLECTION_STRING_ATTRIBUTES: collection = music_object.__getattribute__(collection_str) for sub_element in collection: - sub_element.merge(self.fetch_details(sub_element, stop_at_level=stop_at_level-1, post_process=False)) - + sub_element.merge( + self.fetch_details(sub_element, stop_at_level=stop_at_level - 1, post_process=False)) + return music_object - + def fetch_song(self, source: Source, stop_at_level: int = 1) -> Song: return Song() @@ -327,41 +330,57 @@ class Page: def fetch_label(self, source: Source, stop_at_level: int = 1) -> Label: return Label() - def download(self, music_object: DatabaseObject, genre: str, download_all: bool = False, process_metadata_anyway: bool = False) -> DownloadResult: + def download( + self, + music_object: DatabaseObject, + genre: str, + download_all: bool = False, + process_metadata_anyway: bool = False + ) -> DownloadResult: naming_dict: NamingDict = NamingDict({"genre": genre}) - + def fill_naming_objects(naming_music_object: DatabaseObject): nonlocal naming_dict - + for collection_name in naming_music_object.UPWARDS_COLLECTION_STRING_ATTRIBUTES: collection: Collection = getattr(naming_music_object, collection_name) - + if collection.empty: continue dom_ordered_music_object: DatabaseObject = collection[0] naming_dict.add_object(dom_ordered_music_object) return fill_naming_objects(dom_ordered_music_object) - + fill_naming_objects(music_object) - + return self._download(music_object, naming_dict, download_all, process_metadata_anyway=process_metadata_anyway) - - def _download(self, music_object: DatabaseObject, naming_dict: NamingDict, download_all: bool = False, skip_details: bool = False, process_metadata_anyway: bool = False) -> DownloadResult: + def _download( + self, + music_object: DatabaseObject, + naming_dict: NamingDict, + download_all: bool = False, + skip_details: bool = False, + process_metadata_anyway: bool = False + ) -> DownloadResult: + trace(f"downloading {type(music_object).__name__} [{music_object.title_string}]") skip_next_details = skip_details - + # Skips all releases, that are defined in shared.ALBUM_TYPE_BLACKLIST, if download_all is False if isinstance(music_object, Album): if self.NO_ADDITIONAL_DATA_FROM_SONG: skip_next_details = True - + if not download_all and music_object.album_type.value in main_settings["album_type_blacklist"]: return DownloadResult() - if not isinstance(music_object, Song) or not self.NO_ADDITIONAL_DATA_FROM_SONG: - self.fetch_details(music_object=music_object, stop_at_level=2) - + if not (isinstance(music_object, Song) and self.NO_ADDITIONAL_DATA_FROM_SONG): + self.fetch_details(music_object=music_object, stop_at_level=1) + + if isinstance(music_object, Song): + trace(music_object.option_string) + naming_dict.add_object(music_object) if isinstance(music_object, Song): @@ -374,7 +393,9 @@ class Page: sub_ordered_music_object: DatabaseObject for sub_ordered_music_object in collection: - download_result.merge(self._download(sub_ordered_music_object, naming_dict.copy(), download_all, skip_details=skip_next_details, process_metadata_anyway=process_metadata_anyway)) + download_result.merge(self._download(sub_ordered_music_object, naming_dict.copy(), download_all, + skip_details=skip_next_details, + process_metadata_anyway=process_metadata_anyway)) return download_result @@ -389,11 +410,12 @@ class Page: file_parts = Formatter().parse(main_settings["download_file"]) new_target = Target( relative_to_music_dir=True, - path=main_settings["download_path"].format(**{part[1]: naming_dict[part[1]] for part in path_parts}), - file=main_settings["download_file"].format(**{part[1]: naming_dict[part[1]] for part in file_parts}) + file_path=Path( + main_settings["download_path"].format(**{part[1]: naming_dict[part[1]] for part in path_parts}), + main_settings["download_file"].format(**{part[1]: naming_dict[part[1]] for part in file_parts}) + ) ) - if song.target_collection.empty: song.target_collection.append(new_target) @@ -402,10 +424,13 @@ class Page: return DownloadResult(error_message=f"No source found for {song.title} as {self.__class__.__name__}.") temp_target: Target = Target( - path=main_settings["temp_directory"], - file=str(random.randint(0, 999999)) + relative_to_music_dir=False, + file_path=Path( + main_settings["temp_directory"], + str(song.id) + ) ) - + r = DownloadResult(1) found_on_disc = False @@ -415,10 +440,10 @@ class Page: if process_metadata_anyway: target.copy_content(temp_target) found_on_disc = True - + r.found_on_disk += 1 r.add_target(target) - + if found_on_disc and not process_metadata_anyway: self.LOGGER.info(f"{song.option_string} already exists, thus not downloading again.") return r @@ -427,18 +452,18 @@ class Page: if not found_on_disc: r = self.download_song_to_target(source=source, target=temp_target, desc=song.title) - if not r.is_fatal_error: - r.merge(self._post_process_targets(song, temp_target, [] if found_on_disc else self.get_skip_intervals(song, source))) + r.merge(self._post_process_targets(song, temp_target, + [] if found_on_disc else self.get_skip_intervals(song, source))) return r - + def _post_process_targets(self, song: Song, temp_target: Target, interval_list: List) -> DownloadResult: correct_codec(temp_target, interval_list=interval_list) - + self.post_process_hook(song, temp_target) - + write_metadata_to_target(song.metadata, temp_target) r = DownloadResult() @@ -448,17 +473,17 @@ class Page: if temp_target is not target: temp_target.copy_content(target) r.add_target(target) - + temp_target.delete() r.sponsor_segments += len(interval_list) - + return r - + def get_skip_intervals(self, song: Song, source: Source) -> List[Tuple[float, float]]: return [] - + def post_process_hook(self, song: Song, temp_target: Target, **kwargs): pass - + def download_song_to_target(self, source: Source, target: Target, desc: str = None) -> DownloadResult: return DownloadResult() diff --git a/music_kraken/pages/bandcamp.py b/music_kraken/pages/bandcamp.py new file mode 100644 index 0000000..f9a3a45 --- /dev/null +++ b/music_kraken/pages/bandcamp.py @@ -0,0 +1,361 @@ +from typing import List, Optional, Type +from urllib.parse import urlparse, urlunparse +import json +from enum import Enum +from bs4 import BeautifulSoup +import pycountry + +from ..objects import Source, DatabaseObject +from .abstract import Page +from ..objects import ( + Artist, + Source, + SourcePages, + Song, + Album, + Label, + Target, + Contact, + ID3Timestamp, + Lyrics, + FormattedText +) +from ..connection import Connection +from ..utils.support_classes.download_result import DownloadResult +from ..utils.config import main_settings, logging_settings +from ..utils.shared import DEBUG + +if DEBUG: + from ..utils import dump_to_file + + +def _parse_artist_url(url: str) -> str: + parsed = urlparse(url) + return urlunparse((parsed.scheme, parsed.netloc, "/music/", "", "", "")) + + +def _get_host(source: Source) -> str: + parsed = urlparse(source.url) + return urlunparse((parsed.scheme, parsed.netloc, "", "", "", "")) + + +class BandcampTypes(Enum): + ARTIST = "b" + ALBUM = "a" + SONG = "t" + + +class Bandcamp(Page): + # CHANGE + SOURCE_TYPE = SourcePages.BANDCAMP + LOGGER = logging_settings["bandcamp_logger"] + + def __init__(self, *args, **kwargs): + self.connection: Connection = Connection( + host="https://bandcamp.com/", + logger=self.LOGGER, + module="bandcamp", + ) + + super().__init__(*args, **kwargs) + + def get_source_type(self, source: Source) -> Optional[Type[DatabaseObject]]: + parsed_url = urlparse(source.url) + path = parsed_url.path.replace("/", "") + + if path == "" or path.startswith("music"): + return Artist + if path.startswith("album"): + return Album + if path.startswith("track"): + return Song + + return super().get_source_type(source) + + def _parse_autocomplete_api_result(self, data: dict) -> DatabaseObject: + try: + object_type = BandcampTypes(data["type"]) + except ValueError: + return + + url = data["item_url_root"] + if "item_url_path" in data: + url = data["item_url_path"] + + source_list = [Source(self.SOURCE_TYPE, url)] + name = data["name"] + + if data.get("is_label", False): + return Label( + name=name, + source_list=source_list + ) + + if object_type is BandcampTypes.ARTIST: + source_list = [Source(self.SOURCE_TYPE, _parse_artist_url(url))] + return Artist( + name=name, + source_list=source_list + ) + + if object_type is BandcampTypes.ALBUM: + return Album( + title=name, + source_list=source_list, + artist_list=[ + Artist( + name=data["band_name"].strip(), + source_list=[ + Source(self.SOURCE_TYPE, data["item_url_root"]) + ] + ) + ] + ) + + if object_type is BandcampTypes.SONG: + return Song( + title=name.strip(), + source_list=source_list, + main_artist_list=[ + Artist( + name=data["band_name"], + source_list=[ + Source(self.SOURCE_TYPE, data["item_url_root"]) + ] + ) + ] + ) + + def general_search(self, search_query: str, filter_string: str = "") -> List[DatabaseObject]: + results = [] + + r = self.connection.post("https://bandcamp.com/api/bcsearch_public_api/1/autocomplete_elastic", json={ + "fan_id": None, + "full_page": True, + "search_filter": filter_string, + "search_text": search_query, + }) + if r is None: + return results + + if DEBUG: + dump_to_file("bandcamp_search_response.json", r.text, is_json=True, exit_after_dump=False) + + data = r.json() + + for element in data.get("auto", {}).get("results", []): + r = self._parse_autocomplete_api_result(element) + if r is not None: + results.append(r) + + return results + + def label_search(self, label: Label) -> List[Label]: + return self.general_search(label.name, filter_string="b") + + def artist_search(self, artist: Artist) -> List[Artist]: + return self.general_search(artist.name, filter_string="b") + + def album_search(self, album: Album) -> List[Album]: + return self.general_search(album.title, filter_string="a") + + def song_search(self, song: Song) -> List[Song]: + return self.general_search(song.title, filter_string="t") + + def fetch_label(self, source: Source, stop_at_level: int = 1) -> Label: + return Label() + + def _parse_artist_details(self, soup: BeautifulSoup) -> Artist: + name: str = None + source_list: List[Source] = [] + contact_list: List[Contact] = [] + + band_name_location: BeautifulSoup = soup.find("p", {"id": "band-name-location"}) + if band_name_location is not None: + title_span = band_name_location.find("span", {"class": "title"}) + if title_span is not None: + name = title_span.text.strip() + + link_container: BeautifulSoup = soup.find("ol", {"id": "band-links"}) + if link_container is not None: + li: BeautifulSoup + for li in link_container.find_all("a"): + if li is None and li['href'] is not None: + continue + + source_list.append(Source.match_url(_parse_artist_url(li['href']), referer_page=self.SOURCE_TYPE)) + + return Artist( + name=name, + source_list=source_list + ) + + def _parse_album(self, soup: BeautifulSoup, initial_source: Source) -> List[Album]: + title = None + source_list: List[Source] = [] + + a = soup.find("a") + if a is not None and a["href"] is not None: + source_list.append(Source(self.SOURCE_TYPE, _get_host(initial_source) + a["href"])) + + title_p = soup.find("p", {"class": "title"}) + if title_p is not None: + title = title_p.text.strip() + + return Album(title=title, source_list=source_list) + + def _parse_artist_data_blob(self, data_blob: dict, artist_url: str): + parsed_artist_url = urlparse(artist_url) + album_list: List[Album] = [] + + for album_json in data_blob.get("buyfulldisco", {}).get("tralbums", []): + album_list.append(Album( + title=album_json["title"].strip(), + source_list=[Source( + self.SOURCE_TYPE, + urlunparse((parsed_artist_url.scheme, parsed_artist_url.netloc, album_json["page_url"], "", "", "")) + )] + )) + + return album_list + + def fetch_artist(self, source: Source, stop_at_level: int = 1) -> Artist: + artist = Artist() + + r = self.connection.get(_parse_artist_url(source.url)) + if r is None: + return artist + + soup = self.get_soup_from_response(r) + + if DEBUG: + dump_to_file("artist_page.html", r.text, exit_after_dump=False) + + artist = self._parse_artist_details(soup=soup.find("div", {"id": "bio-container"})) + + html_music_grid = soup.find("ol", {"id": "music-grid"}) + if html_music_grid is not None: + for subsoup in html_music_grid.find_all("li"): + artist.main_album_collection.append(self._parse_album(soup=subsoup, initial_source=source)) + + for i, data_blob_soup in enumerate(soup.find_all("div", {"id": ["pagedata", "collectors-data"]})): + data_blob = data_blob_soup["data-blob"] + + if DEBUG: + dump_to_file(f"bandcamp_artist_data_blob_{i}.json", data_blob, is_json=True, exit_after_dump=False) + + if data_blob is not None: + artist.main_album_collection.extend( + self._parse_artist_data_blob(json.loads(data_blob), source.url) + ) + + artist.source_collection.append(source) + return artist + + def _parse_track_element(self, track: dict) -> Optional[Song]: + return Song( + title=track["item"]["name"].strip(), + source_list=[Source(self.SOURCE_TYPE, track["item"]["mainEntityOfPage"])], + tracksort=int(track["position"]) + ) + + def fetch_album(self, source: Source, stop_at_level: int = 1) -> Album: + album = Album() + + r = self.connection.get(source.url) + if r is None: + return album + + soup = self.get_soup_from_response(r) + + data_container = soup.find("script", {"type": "application/ld+json"}) + + if DEBUG: + dump_to_file("album_data.json", data_container.text, is_json=True, exit_after_dump=False) + + data = json.loads(data_container.text) + artist_data = data["byArtist"] + + artist_source_list = [] + if "@id" in artist_data: + artist_source_list = [Source(self.SOURCE_TYPE, _parse_artist_url(artist_data["@id"]))] + album = Album( + title=data["name"].strip(), + source_list=[Source(self.SOURCE_TYPE, data.get("mainEntityOfPage", data["@id"]))], + date=ID3Timestamp.strptime(data["datePublished"], "%d %b %Y %H:%M:%S %Z"), + artist_list=[Artist( + name=artist_data["name"].strip(), + source_list=artist_source_list + )] + ) + + for i, track_json in enumerate(data.get("track", {}).get("itemListElement", [])): + if DEBUG: + dump_to_file(f"album_track_{i}.json", json.dumps(track_json), is_json=True, exit_after_dump=False) + + try: + album.song_collection.append(self._parse_track_element(track_json)) + except KeyError: + continue + + album.source_collection.append(source) + return album + + def _fetch_lyrics(self, soup: BeautifulSoup) -> List[Lyrics]: + track_lyrics = soup.find("div", {"class": "lyricsText"}) + if track_lyrics: + self.LOGGER.debug(" Lyrics retrieved..") + return [Lyrics(text=FormattedText(html=track_lyrics.prettify()))] + + return [] + + def fetch_song(self, source: Source, stop_at_level: int = 1) -> Song: + r = self.connection.get(source.url) + if r is None: + return Song() + + soup = self.get_soup_from_response(r) + + data_container = soup.find("script", {"type": "application/ld+json"}) + other_data = {} + + other_data_list = soup.select("script[data-tralbum]") + if len(other_data_list) > 0: + other_data = json.loads(other_data_list[0]["data-tralbum"]) + + if DEBUG: + dump_to_file("bandcamp_song_data.json", data_container.text, is_json=True, exit_after_dump=False) + dump_to_file("bandcamp_song_data_other.json", json.dumps(other_data), is_json=True, exit_after_dump=False) + dump_to_file("bandcamp_song_page.html", r.text, exit_after_dump=False) + + data = json.loads(data_container.text) + album_data = data["inAlbum"] + artist_data = data["byArtist"] + + mp3_url = None + for key, value in other_data.get("trackinfo", [{}])[0].get("file", {"": None}).items(): + mp3_url = value + + song = Song( + title=data["name"].strip(), + source_list=[Source(self.SOURCE_TYPE, data.get("mainEntityOfPage", data["@id"]), audio_url=mp3_url)], + album_list=[Album( + title=album_data["name"].strip(), + date=ID3Timestamp.strptime(data["datePublished"], "%d %b %Y %H:%M:%S %Z"), + source_list=[Source(self.SOURCE_TYPE, album_data["@id"])] + )], + main_artist_list=[Artist( + name=artist_data["name"].strip(), + source_list=[Source(self.SOURCE_TYPE, _parse_artist_url(artist_data["@id"]))] + )], + lyrics_list=self._fetch_lyrics(soup=soup) + ) + + song.source_collection.append(source) + + return song + + def download_song_to_target(self, source: Source, target: Target, desc: str = None) -> DownloadResult: + if source.audio_url is None: + return DownloadResult(error_message="Couldn't find download link.") + return self.connection.stream_into(url=source.audio_url, target=target, description=desc) diff --git a/src/music_kraken/pages/encyclopaedia_metallum.py b/music_kraken/pages/encyclopaedia_metallum.py similarity index 71% rename from src/music_kraken/pages/encyclopaedia_metallum.py rename to music_kraken/pages/encyclopaedia_metallum.py index 87c0f69..d9ce0ca 100644 --- a/src/music_kraken/pages/encyclopaedia_metallum.py +++ b/music_kraken/pages/encyclopaedia_metallum.py @@ -2,14 +2,14 @@ from collections import defaultdict from typing import List, Optional, Dict, Type, Union from bs4 import BeautifulSoup import pycountry -from urllib.parse import urlparse +from urllib.parse import urlparse, urlencode from ..connection import Connection from ..utils.config import logging_settings from .abstract import Page from ..utils.enums.source import SourcePages from ..utils.enums.album import AlbumType -from ..utils.support_classes import Query +from ..utils.support_classes.query import Query from ..objects import ( Lyrics, Artist, @@ -22,6 +22,9 @@ from ..objects import ( Options, DatabaseObject ) +from ..utils.shared import DEBUG +from ..utils import dump_to_file + ALBUM_TYPE_MAP: Dict[str, AlbumType] = defaultdict(lambda: AlbumType.OTHER, { @@ -34,6 +37,10 @@ ALBUM_TYPE_MAP: Dict[str, AlbumType] = defaultdict(lambda: AlbumType.OTHER, { "Compilation": AlbumType.COMPILATION_ALBUM }) +URL_SITE = 'https://www.metal-archives.com/' +URL_IMAGES = 'https://www.metal-archives.com/images/' +URL_CSS = 'https://www.metal-archives.com/css/' + def _song_from_json(artist_html=None, album_html=None, release_type=None, title=None, lyrics_html=None) -> Song: song_id = None @@ -88,7 +95,7 @@ def _album_from_json(album_html=None, release_type=None, artist_html=None) -> Al # Self Loather' soup = BeautifulSoup(album_html, 'html.parser') anchor = soup.find('a') - album_name = anchor.text + album_name = anchor.text.strip() album_url = anchor.get('href') album_id = album_url.split("/")[-1] @@ -106,6 +113,99 @@ def _album_from_json(album_html=None, release_type=None, artist_html=None) -> Al ) +def create_grid( + tableOrId: str = "#searchResultsSong", + nbrPerPage: int = 200, + ajaxUrl: str = "search/ajax-advanced/searching/songs/?songTitle=high&bandName=&releaseTitle=&lyrics=&genre=", + extraOptions: dict = None +): + """ + function createGrid(tableOrId, nbrPerPage, ajaxUrl, extraOptions) { + var table = null; + if (typeof tableOrId == "string") { + table = $(tableOrId); + } else { + table = tableOrId; + } + if (ajaxUrl == undefined) { + ajaxUrl = null; + } + var options = { + bAutoWidth: false, + bFilter: false, + bLengthChange: false, + bProcessing: true, + bServerSide: ajaxUrl != null, + iDisplayLength: nbrPerPage, + sAjaxSource: URL_SITE + ajaxUrl, + sPaginationType: 'full_numbers', + sDom: 'ipl<"block_spacer_5"><"clear"r>frip', + oLanguage: { + sProcessing: 'Loading...', + sEmptyTable: 'No records to display.', + sZeroRecords: 'No records found.' + }, + "fnDrawCallback": autoScrollUp + }; + if (typeof extraOptions == "object") { + for (var key in extraOptions) { + options[key] = extraOptions[key]; + if (key == 'fnDrawCallback') { + var callback = options[key]; + options[key] = function(o) { + autoScrollUp(o); + callback(o); + } + } + } + } + return table.dataTable(options); + } + + :return: + """ + + def onDrawCallback(o): + """ + this gets executed once the ajax request is done + :param o: + :return: + """ + + extraOptions = extraOptions or { + "bSort": False, + "oLanguage": { + "sProcessing": 'Searching, please wait...', + "sEmptyTable": 'No matches found. Please try with different search terms.' + } + } + options = { + "bAutoWidth": False, + "bFilter": False, + "bLengthChange": False, + "bProcessing": True, + "bServerSide": ajaxUrl is not None, + "iDisplayLength": nbrPerPage, + "sAjaxSource": URL_SITE + ajaxUrl, + "sPaginationType": 'full_numbers', + "sDom": 'ipl<"block_spacer_5"><"clear"r>frip', + "oLanguage": { + "sProcessing": 'Loading...', + "sEmptyTable": 'No records to display.', + "sZeroRecords": 'No records found.' + }, + "fnDrawCallback": onDrawCallback + } + + for key, value in extraOptions.items(): + options[key] = value + if key == 'fnDrawCallback': + callback = options[key] + options[key] = lambda o: onDrawCallback(o) and callback(o) + + # implement jquery datatable + + class EncyclopaediaMetallum(Page): SOURCE_TYPE = SourcePages.ENCYCLOPAEDIA_METALLUM LOGGER = logging_settings["metal_archives_logger"] @@ -113,16 +213,20 @@ class EncyclopaediaMetallum(Page): def __init__(self, **kwargs): self.connection: Connection = Connection( host="https://www.metal-archives.com/", - logger=self.LOGGER + logger=self.LOGGER, + module=type(self).__name__ ) super().__init__(**kwargs) def song_search(self, song: Song) -> List[Song]: + endpoint = "https://www.metal-archives.com/search/ajax-advanced/searching/songs/?" + """ endpoint = "https://www.metal-archives.com/search/ajax-advanced/searching/songs/?songTitle={song}&bandName={" \ "artist}&releaseTitle={album}&lyrics=&genre=&sEcho=1&iColumns=5&sColumns=&iDisplayStart=0" \ "&iDisplayLength=200&mDataProp_0=0&mDataProp_1=1&mDataProp_2=2&mDataProp_3=3&mDataProp_4=4&_" \ "=1674550595663" + """ """ The difficult question I am facing is, that if I try every artist, with every song, with every album, @@ -132,17 +236,54 @@ class EncyclopaediaMetallum(Page): Is not good. """ - song_title = song.title - album_titles = ["*"] if song.album_collection.empty else [album.title for album in song.album_collection] - artist_titles = ["*"] if song.main_artist_collection.empty else [artist.name for artist in song.main_artist_collection] + search_params = { + "songTitle": song.title, + "bandName": "*", + "releaseTitle": "*", + "lyrics": "", + "genre": "", + "sEcho": 1, + "iColumns": 5, + "sColumns": "", + "iDisplayStart": 0, + "iDisplayLength": 200, + "mDataProp_0": 0, + "mDataProp_1": 1, + "mDataProp_2": 2, + "mDataProp_3": 3, + "mDataProp_4": 4, + "_": 1705946986092 + } + referer_params = { + "songTitle": song.title, + "bandName": "*", + "releaseTitle": "*", + "lyrics": "", + "genre": "", + } + + urlencode(search_params) + + song_title = song.title.strip() + album_titles = ["*"] if song.album_collection.empty else [album.title.strip() for album in song.album_collection] + artist_titles = ["*"] if song.main_artist_collection.empty else [artist.name.strip() for artist in song.main_artist_collection] + search_results = [] for artist in artist_titles: for album in album_titles: - r = self.connection.get( - endpoint.format(song=song_title, artist=artist, album=album) - ) + _search = search_params.copy() + _referer_params = referer_params.copy() + _search["bandName"] = _referer_params["bandName"] = artist + _search["releaseTitle"] = _referer_params["releaseTitle"] = album + + r = self.connection.get(endpoint + urlencode(_search), headers={ + "Referer": "https://www.metal-archives.com/search/advanced/searching/songs?" + urlencode(_referer_params), + "Cache-Control": "no-cache", + "Pragma": "no-cache", + "X-Requested-With": "XMLHttpRequest", + }, name="song_search") if r is None: return [] @@ -158,20 +299,59 @@ class EncyclopaediaMetallum(Page): return search_results def album_search(self, album: Album) -> List[Album]: - endpoint = "https://www.metal-archives.com/search/ajax-advanced/searching/albums/?bandName={" \ - "artist}&releaseTitle={album}&releaseYearFrom=&releaseMonthFrom=&releaseYearTo=&releaseMonthTo" \ - "=&country=&location=&releaseLabelName=&releaseCatalogNumber=&releaseIdentifiers" \ - "=&releaseRecordingInfo=&releaseDescription=&releaseNotes=&genre=&sEcho=1&iColumns=3&sColumns" \ - "=&iDisplayStart=0&iDisplayLength=200&mDataProp_0=0&mDataProp_1=1&mDataProp_2=2&_=1674563943747" + endpoint = "https://www.metal-archives.com/search/ajax-advanced/searching/albums/?" + search_params = { + "bandName": "*", + "releaseTitle": album.title.strip(), + "releaseYearFrom": "", + "releaseMonthFrom": "", + "releaseYearTo": "", + "releaseMonthTo": "", + "country": "", + "location": "", + "releaseLabelName": "", + "releaseCatalogNumber": "", + "releaseIdentifiers": "", + "releaseRecordingInfo": "", + "releaseDescription": "", + "releaseNotes": "", + "genre": "", + "sEcho": 1, + "iColumns": 3, + "sColumns": "", + "iDisplayStart": 0, + "iDisplayLength": 200, + "mDataProp_0": 0, + "mDataProp_1": 1, + "mDataProp_2": 2, + "_": 1705946986092 + } + referer_params = { + "bandName": "*", + "releaseTitle": album.title.strip(), + } album_title = album.title - artist_titles = ["*"] if album.artist_collection.empty else [artist.name for artist in album.artist_collection] + artist_titles = ["*"] if album.artist_collection.empty else [artist.name.strip() for artist in album.artist_collection] search_results = [] for artist in artist_titles: - r = self.connection.get(endpoint.format(artist=artist, album=album_title)) + _search = search_params.copy() + _referer_params = referer_params.copy() + _search["bandName"] = _referer_params["bandName"] = artist + + r = self.connection.get(endpoint + urlencode(_search), headers={ + "Referer": "https://www.metal-archives.com/search/advanced/searching/albums?" + urlencode(_referer_params), + "Cache-Control": "no-cache", + "Pragma": "no-cache", + "X-Requested-With": "XMLHttpRequest", + "Accept": "application/json, text/javascript, */*; q=0.01", + + }) + + #r = self.connection.get(endpoint.format(artist=artist, album=album_title)) if r is None: return [] @@ -182,12 +362,37 @@ class EncyclopaediaMetallum(Page): ) for raw_album in r.json()['aaData']) def artist_search(self, artist: Artist) -> List[Artist]: - endpoint = "https://www.metal-archives.com/search/ajax-advanced/searching/bands/?bandName={" \ - "artist}&genre=&country=&yearCreationFrom=&yearCreationTo=&bandNotes=&status=&themes=&location" \ - "=&bandLabelName=&sEcho=1&iColumns=3&sColumns=&iDisplayStart=0&iDisplayLength=200&mDataProp_0=0" \ - "&mDataProp_1=1&mDataProp_2=2&_=1674565459976" + endpoint = "https://www.metal-archives.com/search/ajax-advanced/searching/bands/?" - r = self.connection.get(endpoint.format(artist=artist.name)) + search_params = { + "bandName": artist.name.strip(), + "genre": "", + "country": "", + "yearCreationFrom": "", + "yearCreationTo": "", + "bandNotes": "", + "status": "", + "themes": "", + "location": "", + "bandLabelName": "", + "sEcho": 1, + "iColumns": 3, + "sColumns": "", + "iDisplayStart": 0, + "iDisplayLength": 200, + "mDataProp_0": 0, + "mDataProp_1": 1, + "mDataProp_2": 2, + "_": 1705946986092 + } + + r = self.connection.get(endpoint + urlencode(search_params), headers={ + "Referer": "https://www.metal-archives.com/search/advanced/searching/bands?" + urlencode({"bandName": artist.name.strip()}), + "Cache-Control": "no-cache", + "Pragma": "no-cache", + "X-Requested-With": "XMLHttpRequest", + "Accept": "application/json, text/javascript, */*; q=0.01", + }, name="artist_search.json") if r is None: return [] @@ -264,20 +469,33 @@ class EncyclopaediaMetallum(Page): soup = self.get_soup_from_response(r) + if DEBUG: + dump_to_file(f"ma_artist_sources_{ma_artist_id}.html", soup.prettify(), exit_after_dump=False) + if soup.find("span", {"id": "noLinks"}) is not None: return [] - artist_source = soup.find("div", {"id": "band_links_Official"}) - """ - TODO - add a Label object to add the label sources from - TODO - maybe do merchandice stuff - """ + source_list = [] + + link_table: BeautifulSoup = soup.find("table", {"id": "linksTablemain"}) + if link_table is not None: + for tr in link_table.find_all("tr"): + anchor: BeautifulSoup = tr.find("a") + if anchor is None: + continue + + href = anchor["href"] + if href is not None: + source_list.append(Source.match_url(href, referer_page=self.SOURCE_TYPE)) + + # The following code is only legacy code, which I just kep because it doesn't harm. + # The way ma returns sources changed. + artist_source = soup.find("div", {"id": "band_links"}) + merchandice_source = soup.find("div", {"id": "band_links_Official_merchandise"}) label_source = soup.find("div", {"id": "band_links_Labels"}) - source_list = [] + if artist_source is not None: for tr in artist_source.find_all("td"): @@ -287,7 +505,7 @@ class EncyclopaediaMetallum(Page): continue source_list.append(Source.match_url(url, referer_page=self.SOURCE_TYPE)) - + return source_list def _parse_artist_attributes(self, artist_soup: BeautifulSoup) -> Artist: @@ -445,10 +663,6 @@ class EncyclopaediaMetallum(Page): artist.notes = band_notes discography: List[Album] = self._fetch_artist_discography(artist_id) - if stop_at_level > 1: - for album in discography: - for source in album.source_collection.get_sources_from_page(self.SOURCE_TYPE): - album.merge(self._fetch_album_from_source(source, stop_at_level=stop_at_level-1)) artist.main_album_collection.extend(discography) return artist @@ -569,13 +783,7 @@ class EncyclopaediaMetallum(Page): soup = self.get_soup_from_response(r) - album = self._parse_album_attributes(soup, stop_at_level=stop_at_level) - - if stop_at_level > 1: - for song in album.song_collection: - for source in song.source_collection.get_sources_from_page(self.SOURCE_TYPE): - song.merge(self._fetch_song_from_source(source=source, stop_at_level=stop_at_level-1)) - + album = self._parse_album_attributes(soup, stop_at_level=stop_at_level) return album def _fetch_lyrics(self, song_id: str) -> Optional[Lyrics]: diff --git a/src/music_kraken/pages/musify.py b/music_kraken/pages/musify.py similarity index 99% rename from src/music_kraken/pages/musify.py rename to music_kraken/pages/musify.py index ddb1a7f..371633a 100644 --- a/src/music_kraken/pages/musify.py +++ b/music_kraken/pages/musify.py @@ -25,7 +25,8 @@ from ..objects import ( ) from ..utils.config import logging_settings from ..utils import string_processing, shared -from ..utils.support_classes import DownloadResult, Query +from ..utils.support_classes.query import Query +from ..utils.support_classes.download_result import DownloadResult """ https://musify.club/artist/ghost-bath-280348?_pjax=#bodyContent @@ -627,7 +628,7 @@ class Musify(Page): source_list.append(Source( self.SOURCE_TYPE, url=current_url, - adio_url=self.HOST + download_href + audio_url=self.HOST + download_href )) return Song( diff --git a/src/music_kraken/pages/preset.py b/music_kraken/pages/preset.py similarity index 93% rename from src/music_kraken/pages/preset.py rename to music_kraken/pages/preset.py index 5e940ba..0755089 100644 --- a/src/music_kraken/pages/preset.py +++ b/music_kraken/pages/preset.py @@ -15,7 +15,8 @@ from ..objects import ( Target ) from ..connection import Connection -from ..utils.support_classes import DownloadResult +from ..utils.support_classes.query import Query +from ..utils.support_classes.download_result import DownloadResult class Preset(Page): # CHANGE diff --git a/src/music_kraken/pages/youtube.py b/music_kraken/pages/youtube.py similarity index 99% rename from src/music_kraken/pages/youtube.py rename to music_kraken/pages/youtube.py index 4c420f0..4ce6633 100644 --- a/src/music_kraken/pages/youtube.py +++ b/music_kraken/pages/youtube.py @@ -20,7 +20,7 @@ from ..objects import ( ) from ..connection import Connection from ..utils.string_processing import clean_song_title -from ..utils.support_classes import DownloadResult +from ..utils.support_classes.download_result import DownloadResult from ..utils.config import youtube_settings, main_settings, logging_settings from .youtube_music.super_youtube import SuperYouTube, YouTubeUrl, get_invidious_url, YouTubeUrlType diff --git a/src/music_kraken/pages/youtube_music/__init__.py b/music_kraken/pages/youtube_music/__init__.py similarity index 100% rename from src/music_kraken/pages/youtube_music/__init__.py rename to music_kraken/pages/youtube_music/__init__.py diff --git a/src/music_kraken/pages/youtube_music/_list_render.py b/music_kraken/pages/youtube_music/_list_render.py similarity index 89% rename from src/music_kraken/pages/youtube_music/_list_render.py rename to music_kraken/pages/youtube_music/_list_render.py index 1acecee..8076e54 100644 --- a/src/music_kraken/pages/youtube_music/_list_render.py +++ b/music_kraken/pages/youtube_music/_list_render.py @@ -15,7 +15,6 @@ from ...objects import ( ) from ._music_object_render import parse_run_list, parse_run_element - LOGGER = logging_settings["youtube_music_logger"] @@ -23,20 +22,24 @@ def music_card_shelf_renderer(renderer: dict) -> List[DatabaseObject]: results = parse_run_list(renderer.get("title", {}).get("runs", [])) for sub_renderer in renderer.get("contents", []): - results.extend(parse_renderer(sub_renderer)) + results.extend(parse_renderer(sub_renderer)) return results + def music_responsive_list_item_flex_column_renderer(renderer: dict) -> List[DatabaseObject]: return parse_run_list(renderer.get("text", {}).get("runs", [])) + def music_responsive_list_item_renderer(renderer: dict) -> List[DatabaseObject]: results = [] - for i, collumn in enumerate(renderer.get("flexColumns", [])): - _r = parse_renderer(collumn) + for i, column in enumerate(renderer.get("flexColumns", [])): + _r = parse_renderer(column) if i == 0 and len(_r) == 0: - renderer["text"] = collumn.get("musicResponsiveListItemFlexColumnRenderer", {}).get("text", {}).get("runs", [{}])[0].get("text") - + renderer["text"] = \ + column.get("musicResponsiveListItemFlexColumnRenderer", {}).get("text", {}).get("runs", [{}])[0].get( + "text") + results.extend(_r) _r = parse_run_element(renderer) @@ -54,7 +57,7 @@ def music_responsive_list_item_renderer(renderer: dict) -> List[DatabaseObject]: for song in song_list: song.album_collection.extend(album_list) song.main_artist_collection.extend(artist_list) - + for album in album_list: album.artist_collection.extend(artist_list) @@ -64,19 +67,22 @@ def music_responsive_list_item_renderer(renderer: dict) -> List[DatabaseObject]: return album_list if len(artist_list) > 0: return artist_list - + return results + def music_shelf_renderer(renderer: dict) -> List[DatabaseObject]: result = [] for subrenderer in renderer.get("contents"): result.extend(parse_renderer(subrenderer)) - + return result + def music_carousel_shelf_renderer(renderer: dict): return music_shelf_renderer(renderer=renderer) + def music_two_row_item_renderer(renderer: dict): return parse_run_list(renderer.get("title", {}).get("runs", [])) @@ -92,6 +98,7 @@ RENDERER_PARSERS = { "itemSectionRenderer": lambda _: [], } + def parse_renderer(renderer: dict) -> List[DatabaseObject]: result: List[DatabaseObject] = [] @@ -99,7 +106,7 @@ def parse_renderer(renderer: dict) -> List[DatabaseObject]: if renderer_name not in RENDERER_PARSERS: LOGGER.warning(f"Can't parse the renderer {renderer_name}.") continue - + result.extend(RENDERER_PARSERS[renderer_name](renderer)) - return result \ No newline at end of file + return result diff --git a/src/music_kraken/pages/youtube_music/_music_object_render.py b/music_kraken/pages/youtube_music/_music_object_render.py similarity index 100% rename from src/music_kraken/pages/youtube_music/_music_object_render.py rename to music_kraken/pages/youtube_music/_music_object_render.py diff --git a/src/music_kraken/pages/youtube_music/super_youtube.py b/music_kraken/pages/youtube_music/super_youtube.py similarity index 95% rename from src/music_kraken/pages/youtube_music/super_youtube.py rename to music_kraken/pages/youtube_music/super_youtube.py index a60d416..d391370 100644 --- a/src/music_kraken/pages/youtube_music/super_youtube.py +++ b/music_kraken/pages/youtube_music/super_youtube.py @@ -1,6 +1,7 @@ from typing import List, Optional, Type, Tuple from urllib.parse import urlparse, urlunparse, parse_qs from enum import Enum +import requests import sponsorblock from sponsorblock.errors import HTTPException, NotFoundException @@ -19,7 +20,7 @@ from ...objects import ( ID3Timestamp ) from ...connection import Connection -from ...utils.support_classes import DownloadResult +from ...utils.support_classes.download_result import DownloadResult from ...utils.config import youtube_settings, logging_settings, main_settings @@ -127,7 +128,7 @@ class SuperYouTube(Page): SOURCE_TYPE = SourcePages.YOUTUBE LOGGER = logging_settings["youtube_logger"] - NO_ADDITIONAL_DATA_FROM_SONG = True + NO_ADDITIONAL_DATA_FROM_SONG = False def __init__(self, *args, **kwargs): self.download_connection: Connection = Connection( @@ -135,6 +136,11 @@ class SuperYouTube(Page): logger=self.LOGGER, sleep_after_404=youtube_settings["sleep_after_youtube_403"] ) + + self.connection: Connection = Connection( + host=get_invidious_url(), + logger=self.LOGGER + ) # the stuff with the connection is, to ensure sponsorblock uses the proxies, my programm does _sponsorblock_connection: Connection = Connection(host="https://sponsor.ajay.app/") @@ -152,7 +158,6 @@ class SuperYouTube(Page): if parsed.url_type in _url_type: return _url_type[parsed.url_type] - def download_song_to_target(self, source: Source, target: Target, desc: str = None) -> DownloadResult: """ 1. getting the optimal source @@ -166,10 +171,11 @@ class SuperYouTube(Page): :param desc: :return: """ - r = self.connection.get(YouTubeUrl(source.url).api) + r: requests.Response = self.connection.get(YouTubeUrl(source.url).api) if r is None: return DownloadResult(error_message="Api didn't even respond, maybe try another invidious Instance") + audio_format = None best_bitrate = 0 @@ -194,8 +200,7 @@ class SuperYouTube(Page): endpoint = audio_format["url"] - return self.download_connection.stream_into(endpoint, target, description=desc, raw_url=True) - + return self.download_connection.stream_into(endpoint, target, name=desc, raw_url=True) def get_skip_intervals(self, song: Song, source: Source) -> List[Tuple[float, float]]: if not youtube_settings["use_sponsor_block"]: diff --git a/src/music_kraken/pages/youtube_music/youtube_music.py b/music_kraken/pages/youtube_music/youtube_music.py similarity index 54% rename from src/music_kraken/pages/youtube_music/youtube_music.py rename to music_kraken/pages/youtube_music/youtube_music.py index ef6f14d..31dae1e 100644 --- a/src/music_kraken/pages/youtube_music/youtube_music.py +++ b/music_kraken/pages/youtube_music/youtube_music.py @@ -1,19 +1,26 @@ +from __future__ import unicode_literals, annotations + from typing import Dict, List, Optional, Set, Type -from urllib.parse import urlparse, urlunparse, quote, parse_qs +from urllib.parse import urlparse, urlunparse, quote, parse_qs, urlencode import logging import random import json from dataclasses import dataclass import re +from functools import lru_cache + +import youtube_dl +from youtube_dl.extractor.youtube import YoutubeIE from ...utils.exception.config import SettingValueError from ...utils.config import main_settings, youtube_settings, logging_settings from ...utils.shared import DEBUG, DEBUG_YOUTUBE_INITIALIZING -from ...utils.functions import get_current_millis -if DEBUG: - from ...utils.debug_utils import dump_to_file +from ...utils.string_processing import clean_song_title +from ...utils import get_current_millis -from ...objects import Source, DatabaseObject +from ...utils import dump_to_file + +from ...objects import Source, DatabaseObject, ID3Timestamp from ..abstract import Page from ...objects import ( Artist, @@ -25,7 +32,7 @@ from ...objects import ( Target ) from ...connection import Connection -from ...utils.support_classes import DownloadResult +from ...utils.support_classes.download_result import DownloadResult from ._list_render import parse_renderer from .super_youtube import SuperYouTube @@ -46,6 +53,7 @@ class YoutubeMusicConnection(Connection): --> average delay in between: 1.8875 min """ + def __init__(self, logger: logging.Logger, accept_language: str): # https://stackoverflow.com/questions/30561260/python-change-accept-language-using-requests super().__init__( @@ -54,25 +62,26 @@ class YoutubeMusicConnection(Connection): heartbeat_interval=113.25, header_values={ "Accept-Language": accept_language - } + }, + module="youtube_music", ) # cookie consent for youtube # https://stackoverflow.com/a/66940841/16804841 doesn't work for cookie_key, cookie_value in youtube_settings["youtube_music_consent_cookies"].items(): self.session.cookies.set( - name=cookie_key, + name=cookie_key, value=cookie_value, path='/', domain='.youtube.com' ) - def heartbeat(self): - r = self.get("https://music.youtube.com/verify_session", is_heartbeat=True) + r = self.get("https://music.youtube.com/verify_session") if r is None: self.heartbeat_failed() - - string = r.content.decode("utf-8") + return + + string = r.text data = json.loads(string[string.index("{"):]) success: bool = data["success"] @@ -92,26 +101,106 @@ class YouTubeMusicCredentials: # the context in requests context: dict + player_url: str + + @property + def player_id(self): + @lru_cache(128) + def _extract_player_info(player_url): + _PLAYER_INFO_RE = ( + r'/s/player/(?P[a-zA-Z0-9_-]{8,})/player', + r'/(?P[a-zA-Z0-9_-]{8,})/player(?:_ias\.vflset(?:/[a-zA-Z]{2,3}_[a-zA-Z]{2,3})?|-plasma-ias-(?:phone|tablet)-[a-z]{2}_[A-Z]{2}\.vflset)/base\.js$', + r'\b(?Pvfl[a-zA-Z0-9_-]+)\b.*?\.js$', + ) + + for player_re in _PLAYER_INFO_RE: + id_m = re.search(player_re, player_url) + if id_m: + break + else: + return + + return id_m.group('id') + + return _extract_player_info(self.player_url) + + +class YTDLLogger: + def __init__(self, logger: logging.Logger): + self.logger = logger + + def debug(self, msg): + self.logger.debug(msg) + + def warning(self, msg): + self.logger.warning(msg) + + def error(self, msg): + self.logger.error(msg) + + +class MusicKrakenYoutubeDL(youtube_dl.YoutubeDL): + def __init__(self, main_instance: YoutubeMusic, ydl_opts: dict, **kwargs): + self.main_instance = main_instance + ydl_opts = ydl_opts or {} + ydl_opts.update({ + "logger": YTDLLogger(self.main_instance.LOGGER), + }) + + super().__init__(ydl_opts, **kwargs) + super().__enter__() + + def __del__(self): + super().__exit__(None, None, None) + + +class MusicKrakenYoutubeIE(YoutubeIE): + def __init__(self, *args, main_instance: YoutubeMusic, **kwargs): + self.main_instance = main_instance + super().__init__(*args, **kwargs) + + + class YoutubeMusic(SuperYouTube): # CHANGE SOURCE_TYPE = SourcePages.YOUTUBE_MUSIC LOGGER = logging_settings["youtube_music_logger"] - def __init__(self, *args, **kwargs): - self.connection: YoutubeMusicConnection = YoutubeMusicConnection(logger=self.LOGGER, accept_language="en-US,en;q=0.5") + def __init__(self, *args, ydl_opts: dict = None, **kwargs): + self.yt_music_connection: YoutubeMusicConnection = YoutubeMusicConnection( + logger=self.LOGGER, + accept_language="en-US,en;q=0.5" + ) self.credentials: YouTubeMusicCredentials = YouTubeMusicCredentials( api_key=youtube_settings["youtube_music_api_key"], ctoken="", - context=youtube_settings["youtube_music_innertube_context"] + context=youtube_settings["youtube_music_innertube_context"], + player_url=youtube_settings["player_url"], ) self.start_millis = get_current_millis() if self.credentials.api_key == "" or DEBUG_YOUTUBE_INITIALIZING: self._fetch_from_main_page() - - super().__init__(*args, **kwargs) + + SuperYouTube.__init__(self, *args, **kwargs) + + self.download_connection: Connection = Connection( + host="https://rr2---sn-cxaf0x-nugl.googlevideo.com/", + logger=self.LOGGER, + sleep_after_404=youtube_settings["sleep_after_youtube_403"], + header_values={ + "Referer": "https://music.youtube.com/", + 'Origin': 'https://music.youtube.com', + } + ) + + # https://github.com/ytdl-org/youtube-dl/blob/master/README.md#embedding-youtube-dl + self.ydl = MusicKrakenYoutubeDL(self, ydl_opts) + self.yt_ie = MusicKrakenYoutubeIE(downloader=self.ydl, main_instance=self) + + self.download_values_by_url: dict = {} def _fetch_from_main_page(self): """ @@ -121,13 +210,13 @@ class YoutubeMusic(SuperYouTube): search for: "innertubeApiKey" """ - r = self.connection.get("https://music.youtube.com/") + r = self.yt_music_connection.get("https://music.youtube.com/") if r is None: return - + if urlparse(r.url).netloc == "consent.youtube.com": self.LOGGER.info(f"Making cookie consent request for {type(self).__name__}.") - r = self.connection.post("https://consent.youtube.com/save", data={ + r = self.yt_music_connection.post("https://consent.youtube.com/save", data={ 'gl': 'DE', 'm': '0', 'app': '0', @@ -144,19 +233,21 @@ class YoutubeMusic(SuperYouTube): }) if r is None: return - + # load cookie dict from settings cookie_dict = youtube_settings["youtube_music_consent_cookies"] - + for cookie in r.cookies: cookie_dict[cookie.name] = cookie.value - for cookie in self.connection.session.cookies: + for cookie in self.yt_music_connection.session.cookies: cookie_dict[cookie.name] = cookie.value - + # save cookies in settings youtube_settings["youtube_music_consent_cookies"] = cookie_dict + else: + self.yt_music_connection.save(r, "index.html") - r = self.connection.get("https://music.youtube.com/") + r = self.yt_music_connection.get("https://music.youtube.com/", name="index.html") if r is None: return @@ -170,16 +261,16 @@ class YoutubeMusic(SuperYouTube): r"(?<=\"innertubeApiKey\":\")(.*?)(?=\")", r"(?<=\"INNERTUBE_API_KEY\":\")(.*?)(?=\")", ) - + api_keys = [] for api_key_patter in api_key_pattern: api_keys.extend(re.findall(api_key_patter, content)) - + found_a_good_api_key = False for api_key in api_keys: # save the first api key api_key = api_keys[0] - + try: youtube_settings["youtube_music_api_key"] = api_key except SettingValueError: @@ -209,9 +300,43 @@ class YoutubeMusic(SuperYouTube): if not found_context: self.LOGGER.warning(f"Couldn't find a context for {type(self).__name__}.") + # player url + """ + Thanks to youtube-dl <33 + """ + player_pattern = [ + r'(?<="jsUrl":")(.*?)(?=")', + r'(?<="PLAYER_JS_URL":")(.*?)(?=")' + ] + found_player_url = False + + for pattern in player_pattern: + for player_string in re.findall(pattern, content, re.M): + try: + youtube_settings["player_url"] = "https://music.youtube.com" + player_string + found_player_url = True + except json.decoder.JSONDecodeError: + continue + + self.credentials.player_url = youtube_settings["player_url"] + break + + if found_player_url: + break + + if not found_player_url: + self.LOGGER.warning(f"Couldn't find an url for the video player.") + + # ytcfg + youtube_settings["ytcfg"] = json.loads(self._search_regex( + r'ytcfg\.set\s*\(\s*({.+?})\s*\)\s*;', + content, + default='{}' + )) or {} + def get_source_type(self, source: Source) -> Optional[Type[DatabaseObject]]: return super().get_source_type(source) - + def general_search(self, search_query: str) -> List[DatabaseObject]: search_query = search_query.strip() @@ -220,25 +345,27 @@ class YoutubeMusic(SuperYouTube): # approximate the ammount of time it would take to type the search, because google for some reason tracks that LAST_EDITED_TIME = get_current_millis() - random.randint(0, 20) _estimated_time = sum(len(search_query) * random.randint(50, 100) for _ in search_query.strip()) - FIRST_EDITED_TIME = LAST_EDITED_TIME - _estimated_time if LAST_EDITED_TIME - self.start_millis > _estimated_time else random.randint(50, 100) + FIRST_EDITED_TIME = LAST_EDITED_TIME - _estimated_time if LAST_EDITED_TIME - self.start_millis > _estimated_time else random.randint( + 50, 100) query_continue = "" if self.credentials.ctoken == "" else f"&ctoken={self.credentials.ctoken}&continuation={self.credentials.ctoken}" # construct the request - r = self.connection.post( - url=get_youtube_url(path="/youtubei/v1/search", query=f"key={self.credentials.api_key}&prettyPrint=false"+query_continue), + r = self.yt_music_connection.post( + url=get_youtube_url(path="/youtubei/v1/search", + query=f"key={self.credentials.api_key}&prettyPrint=false" + query_continue), json={ - "context": {**self.credentials.context, "adSignalsInfo":{"params":[]}}, + "context": {**self.credentials.context, "adSignalsInfo": {"params": []}}, "query": search_query, "suggestStats": { "clientName": "youtube-music", "firstEditTimeMsec": FIRST_EDITED_TIME, "inputMethod": "KEYBOARD", - "lastEditTimeMsec": LAST_EDITED_TIME, + "lastEditTimeMsec": LAST_EDITED_TIME, "originalQuery": search_query, "parameterValidationStatus": "VALID_PARAMETERS", "searchMethod": "ENTER_KEY", - "validationStatus": "VALID", + "validationStatus": "VALID", "zeroPrefixEnabled": True, "availableSuggestions": [] } @@ -248,8 +375,12 @@ class YoutubeMusic(SuperYouTube): } ) - renderer_list = r.json().get("contents", {}).get("tabbedSearchResultsRenderer", {}).get("tabs", [{}])[0].get("tabRenderer").get("content", {}).get("sectionListRenderer", {}).get("contents", []) - + if r is None: + return [] + + renderer_list = r.json().get("contents", {}).get("tabbedSearchResultsRenderer", {}).get("tabs", [{}])[0].get( + "tabRenderer").get("content", {}).get("sectionListRenderer", {}).get("contents", []) + if DEBUG: for i, content in enumerate(renderer_list): dump_to_file(f"{i}-renderer.json", json.dumps(content), is_json=True, exit_after_dump=False) @@ -257,7 +388,7 @@ class YoutubeMusic(SuperYouTube): results = [] """ - cant use fixed indices, because if something has no entries, the list dissappears + cant use fixed indices, because if something has no entries, the list disappears instead I have to try parse everything, and just reject community playlists and profiles. """ @@ -273,11 +404,11 @@ class YoutubeMusic(SuperYouTube): url = urlparse(source.url) browse_id = url.path.replace("/channel/", "") - r = self.connection.post( + r = self.yt_music_connection.post( url=get_youtube_url(path="/youtubei/v1/browse", query=f"key={self.credentials.api_key}&prettyPrint=false"), json={ "browseId": browse_id, - "context": {**self.credentials.context, "adSignalsInfo":{"params":[]}} + "context": {**self.credentials.context, "adSignalsInfo": {"params": []}} } ) if r is None: @@ -286,7 +417,8 @@ class YoutubeMusic(SuperYouTube): if DEBUG: dump_to_file(f"{browse_id}.json", r.text, is_json=True, exit_after_dump=False) - renderer_list = r.json().get("contents", {}).get("singleColumnBrowseResultsRenderer", {}).get("tabs", [{}])[0].get("tabRenderer", {}).get("content", {}).get("sectionListRenderer", {}).get("contents", []) + renderer_list = r.json().get("contents", {}).get("singleColumnBrowseResultsRenderer", {}).get("tabs", [{}])[ + 0].get("tabRenderer", {}).get("content", {}).get("sectionListRenderer", {}).get("contents", []) if DEBUG: for i, content in enumerate(renderer_list): @@ -302,10 +434,10 @@ class YoutubeMusic(SuperYouTube): for renderer in renderer_list: results.extend(parse_renderer(renderer)) - artist.add_list_of_other_objects(results) + artist.add_list_of_other_objects(results) return artist - + def fetch_album(self, source: Source, stop_at_level: int = 1) -> Album: album = Album() @@ -315,11 +447,11 @@ class YoutubeMusic(SuperYouTube): return album browse_id = list_id_list[0] - r = self.connection.post( + r = self.yt_music_connection.post( url=get_youtube_url(path="/youtubei/v1/browse", query=f"key={self.credentials.api_key}&prettyPrint=false"), json={ "browseId": browse_id, - "context": {**self.credentials.context, "adSignalsInfo":{"params":[]}} + "context": {**self.credentials.context, "adSignalsInfo": {"params": []}} } ) if r is None: @@ -328,7 +460,8 @@ class YoutubeMusic(SuperYouTube): if DEBUG: dump_to_file(f"{browse_id}.json", r.text, is_json=True, exit_after_dump=False) - renderer_list = r.json().get("contents", {}).get("singleColumnBrowseResultsRenderer", {}).get("tabs", [{}])[0].get("tabRenderer", {}).get("content", {}).get("sectionListRenderer", {}).get("contents", []) + renderer_list = r.json().get("contents", {}).get("singleColumnBrowseResultsRenderer", {}).get("tabs", [{}])[ + 0].get("tabRenderer", {}).get("content", {}).get("sectionListRenderer", {}).get("contents", []) if DEBUG: for i, content in enumerate(renderer_list): @@ -344,10 +477,99 @@ class YoutubeMusic(SuperYouTube): for renderer in renderer_list: results.extend(parse_renderer(renderer)) - album.add_list_of_other_objects(results) + album.add_list_of_other_objects(results) return album + def fetch_song(self, source: Source, stop_at_level: int = 1) -> Song: - print(source) - return Song() + ydl_res: dict = self.ydl.extract_info(url=source.url, download=False) + + self.fetch_media_url(source=source, ydl_res=ydl_res) + + artist_name = ydl_res.get("artist", ydl_res.get("uploader", "")).rstrip(" - Topic") + + album_list = [] + if "album" in ydl_res: + album_list.append(Album( + title=ydl_res.get("album"), + date=ID3Timestamp.strptime(ydl_res.get("upload_date"), "%Y%m%d"), + )) + + return Song( + title=ydl_res.get("track", clean_song_title(ydl_res.get("title"), artist_name=artist_name)), + note=ydl_res.get("descriptions"), + album_list=album_list, + length=int(ydl_res.get("duration", 0)) * 1000, + main_artist_list=[Artist( + name=artist_name, + source_list=[Source( + SourcePages.YOUTUBE_MUSIC, + f"https://music.youtube.com/channel/{ydl_res.get('channel_id', ydl_res.get('uploader_id', ''))}" + )] + )], + source_list=[Source( + SourcePages.YOUTUBE_MUSIC, + f"https://music.youtube.com/watch?v={ydl_res.get('id')}" + ), source], + ) + + + def fetch_media_url(self, source: Source, ydl_res: dict = None) -> dict: + def _get_best_format(format_list: List[Dict]) -> dict: + def _calc_score(_f: dict): + s = 0 + + _url = _f.get("url", "") + if "mime=audio" in _url: + s += 100 + + return s + + highest_score = 0 + best_format = {} + for _format in format_list: + _s = _calc_score(_format) + if _s >= highest_score: + highest_score = _s + best_format = _format + + return best_format + + if source.url in self.download_values_by_url: + return self.download_values_by_url[source.url] + + if ydl_res is None: + ydl_res = self.ydl.extract_info(url=source.url, download=False) + _best_format = _get_best_format(ydl_res.get("formats", [{}])) + + self.download_values_by_url[source.url] = { + "url": _best_format.get("url"), + "chunk_size": _best_format.get("downloader_options", {}).get("http_chunk_size", main_settings["chunk_size"]), + "headers": _best_format.get("http_headers", {}), + } + + return self.download_values_by_url[source.url] + + def download_song_to_target(self, source: Source, target: Target, desc: str = None) -> DownloadResult: + media = self.fetch_media_url(source) + + result = self.download_connection.stream_into( + media["url"], + target, + name=desc, + raw_url=True, + raw_headers=True, + disable_cache=True, + headers=media.get("headers", {}), + # chunk_size=media.get("chunk_size", main_settings["chunk_size"]), + method="GET", + ) + + if result.is_fatal_error: + result.merge(super().download_song_to_target(source=source, target=target, desc=desc)) + + return result + + def __del__(self): + self.ydl.__exit__() diff --git a/music_kraken/utils/__init__.py b/music_kraken/utils/__init__.py new file mode 100644 index 0000000..6b4754e --- /dev/null +++ b/music_kraken/utils/__init__.py @@ -0,0 +1,66 @@ +from datetime import datetime +from pathlib import Path +import json +import logging + +from .shared import DEBUG, DEBUG_LOGGING, DEBUG_DUMP, DEBUG_TRACE +from .config import config, read_config, write_config +from .enums.colors import BColors +from .path_manager import LOCATIONS + +""" +IO functions +""" + +def _apply_color(msg: str, color: BColors) -> str: + if color is BColors.ENDC: + return msg + return color.value + msg + BColors.ENDC.value + + +def output(msg: str, color: BColors = BColors.ENDC): + print(_apply_color(msg, color)) + + +def user_input(msg: str, color: BColors = BColors.ENDC): + return input(_apply_color(msg, color)).strip() + + +def dump_to_file(file_name: str, payload: str, is_json: bool = False, exit_after_dump: bool = False): + if not DEBUG_DUMP: + return + + path = Path(LOCATIONS.TEMP_DIRECTORY, file_name) + logging.warning(f"dumping {file_name} to: \"{path}\"") + + if is_json and isinstance(payload, str): + payload = json.loads(payload) + + if isinstance(payload, dict): + payload = json.dumps(payload, indent=4) + + with path.open("w") as f: + f.write(payload) + + if exit_after_dump: + exit() + + +def trace(msg: str): + if not DEBUG_TRACE: + return + + output("trace: " + msg, BColors.OKBLUE) + + +""" +misc functions +""" + +def get_current_millis() -> int: + dt = datetime.now() + return int(dt.microsecond / 1_000) + + +def get_unix_time() -> int: + return int(datetime.now().timestamp()) \ No newline at end of file diff --git a/src/music_kraken/utils/config/__init__.py b/music_kraken/utils/config/__init__.py similarity index 100% rename from src/music_kraken/utils/config/__init__.py rename to music_kraken/utils/config/__init__.py diff --git a/src/music_kraken/download/__init__.py b/music_kraken/utils/config/attributes/__init__.py similarity index 100% rename from src/music_kraken/download/__init__.py rename to music_kraken/utils/config/attributes/__init__.py diff --git a/src/music_kraken/utils/config/attributes/attribute.py b/music_kraken/utils/config/attributes/attribute.py similarity index 100% rename from src/music_kraken/utils/config/attributes/attribute.py rename to music_kraken/utils/config/attributes/attribute.py diff --git a/src/music_kraken/utils/config/attributes/special_attributes.py b/music_kraken/utils/config/attributes/special_attributes.py similarity index 100% rename from src/music_kraken/utils/config/attributes/special_attributes.py rename to music_kraken/utils/config/attributes/special_attributes.py diff --git a/src/music_kraken/utils/config/config.py b/music_kraken/utils/config/config.py similarity index 73% rename from src/music_kraken/utils/config/config.py rename to music_kraken/utils/config/config.py index 6a6d99f..f7cf96f 100644 --- a/src/music_kraken/utils/config/config.py +++ b/music_kraken/utils/config/config.py @@ -1,6 +1,7 @@ -from typing import Any, Tuple, Union +from typing import Any, Tuple, Union, List from pathlib import Path import logging +from datetime import datetime import toml @@ -31,10 +32,26 @@ class ConfigDict(dict): class Config: - def __init__(self, componet_list: Tuple[Union[Attribute, Description, EmptyLine]], config_file: Path) -> None: + def __init__(self, component_list: Tuple[Union[Attribute, Description, EmptyLine], ...], config_file: Path) -> None: self.config_file: Path = config_file - self.component_list: Tuple[Union[Attribute, Description, EmptyLine]] = componet_list + self.component_list: List[Union[Attribute, Description, EmptyLine]] = [ + Description(f"""IMPORTANT: If you modify this file, the changes for the actual setting, will be kept as is. +The changes you make to the comments, will be discarded, next time you run music-kraken. Have fun! + +Latest reset: {datetime.now()} + + _____ + / ____| + | | __ __ _ _ _ + | | |_ | / _` || | | | + | |__| || (_| || |_| | + \_____| \__,_| \__, | + __/ | + |___/ +""")] + + self.component_list.extend(component_list) self.loaded_settings: ConfigDict = ConfigDict(self) self.attribute_map = {} diff --git a/src/music_kraken/utils/config/config_files/__init__.py b/music_kraken/utils/config/config_files/__init__.py similarity index 100% rename from src/music_kraken/utils/config/config_files/__init__.py rename to music_kraken/utils/config/config_files/__init__.py diff --git a/src/music_kraken/utils/config/config_files/logging_config.py b/music_kraken/utils/config/config_files/logging_config.py similarity index 93% rename from src/music_kraken/utils/config/config_files/logging_config.py rename to music_kraken/utils/config/config_files/logging_config.py index 5ff0cd6..c068fe3 100644 --- a/src/music_kraken/utils/config/config_files/logging_config.py +++ b/music_kraken/utils/config/config_files/logging_config.py @@ -79,6 +79,11 @@ Reference for the logging formats: https://docs.python.org/3/library/logging.htm description="The logger for the genius scraper", default_value="genius" ), + LoggerAttribute( + name="bandcamp_logger", + description="The logger for the bandcamp scraper", + default_value="bandcamp" + ) ], LOCATIONS.get_config_file("logging")) @@ -96,4 +101,5 @@ class SettingsStructure(TypedDict): youtube_logger: Logger youtube_music_logger: Logger metal_archives_logger: Logger - genius_logger: Logger \ No newline at end of file + genius_logger: Logger + bandcamp_logger: Logger \ No newline at end of file diff --git a/src/music_kraken/utils/config/config_files/main_config.py b/music_kraken/utils/config/config_files/main_config.py similarity index 81% rename from src/music_kraken/utils/config/config_files/main_config.py rename to music_kraken/utils/config/config_files/main_config.py index 164a08a..8de212b 100644 --- a/src/music_kraken/utils/config/config_files/main_config.py +++ b/music_kraken/utils/config/config_files/main_config.py @@ -1,5 +1,4 @@ from typing import TypedDict, List -from datetime import datetime from urllib.parse import ParseResult from logging import Logger from pathlib import Path @@ -10,25 +9,10 @@ from ..attributes.attribute import Attribute, EmptyLine, Description from ..attributes.special_attributes import ( SelectAttribute, PathAttribute, - AudioFormatAttribute, + AudioFormatAttribute ) -config = Config([ - Description(f"""IMPORTANT: If you modify this file, the changes for the actual setting, will be kept as is. -The changes you make to the comments, will be discarded, next time you run music-kraken. Have fun! - -Latest reset: {datetime.now()} - - _____ - / ____| - | | __ __ _ _ _ - | | |_ | / _` || | | | - | |__| || (_| || |_| | - \_____| \__,_| \__, | - __/ | - |___/ -"""), - +config = Config(( Attribute(name="hasnt_yet_started", default_value=False, description="This will be set automatically, to look if it needs to run the scripts that run on start."), Attribute(name="bitrate", default_value=125, description="Streams the audio with given bitrate [kB/s]. Can't stream with a higher Bitrate, than the audio source provides."), AudioFormatAttribute(name="audio_format", default_value="mp3", description="""Music Kraken will stream the audio into this format. @@ -62,6 +46,7 @@ The folder music kraken should put the songs into."""), "Mixtape" ], options=("Studio Album", "EP (Extended Play)", "Single", "Live Album", "Compilation Album", "Mixtape", "Demo", "Other"), description="""Music Kraken ignores all albums of those types. Following album types exist in the programm:"""), + Attribute(name="refresh_after", default_value=161, description="The time in seconds, after which a song/album/artist/label is newly fetched."), EmptyLine(), @@ -74,12 +59,33 @@ If you use Tor, make sure the Tor browser is installed, and running.I can't guar Attribute(name="show_download_errors_threshold", default_value=0.3, description="""If the percentage of failed downloads goes over this threshold, all the error messages are shown."""), + Attribute( + name="language", + default_value="en-US,en;q=0.6", + description="The language of the program. This will be used to translate the program in the future.\n" + "Currently it just sets the Accept-Language header.\n" + "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Language" + ), + Attribute( + name="user_agent", + default_value="Mozilla/5.0 (X11; Linux x86_64; rv:90.0) Gecko/20100101 Firefox/90.0", + description="The user agent of the program. This will be used to translate the program in the future.\n" + "Currently it just sets the User-Agent header.\n" + "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent" + ), + Attribute( + name="tries_per_proxy", + default_value=2, + description="The retries it should do. These can be overridden by the program, at certain places, and they have to be.", + ), + EmptyLine(), PathAttribute(name="music_directory", default_value=LOCATIONS.MUSIC_DIRECTORY.resolve(), description="The directory, all the music will be downloaded to."), PathAttribute(name="temp_directory", default_value=LOCATIONS.TEMP_DIRECTORY.resolve(), description="All temporary stuff is gonna be dumped in this directory."), PathAttribute(name="log_file", default_value=LOCATIONS.get_log_file("download_logs.log").resolve()), PathAttribute(name="ffmpeg_binary", default_value=LOCATIONS.FFMPEG_BIN.resolve(), description="Set the path to the ffmpeg binary."), + PathAttribute(name="cache_directory", default_value=LOCATIONS.CACHE_DIRECTORY.resolve(), description="Set the path of the cache directory."), Attribute( name="not_a_genre_regex", description="These regular expressions tell music-kraken, which sub-folders of the music-directory\n" @@ -109,7 +115,7 @@ But anyways... Freedom of thought, so go ahead and change the messages."""), Attribute(name="id_bits", default_value=64, description="I really dunno why I even made this a setting.. Modifying this is a REALLY dumb idea."), Description("🏳️‍⚧️🏳️‍⚧️ Protect trans youth. 🏳️‍⚧️🏳️‍⚧️\n"), -], LOCATIONS.get_config_file("main")) +), LOCATIONS.get_config_file("main")) class SettingsStructure(TypedDict): @@ -119,6 +125,7 @@ class SettingsStructure(TypedDict): happy_messages: List[str] modify_gc: bool id_bits: int + refresh_after: int # audio bitrate: int @@ -131,10 +138,13 @@ class SettingsStructure(TypedDict): # connection proxies: List[dict[str, str]] + tries_per_proxy: int tor: bool tor_port: int chunk_size: int show_download_errors_threshold: float + language: str + user_agent: str # paths music_directory: Path @@ -142,4 +152,4 @@ class SettingsStructure(TypedDict): log_file: Path not_a_genre_regex: List[str] ffmpeg_binary: Path - + cache_directory: Path diff --git a/src/music_kraken/utils/config/config_files/youtube_config.py b/music_kraken/utils/config/config_files/youtube_config.py similarity index 92% rename from src/music_kraken/utils/config/config_files/youtube_config.py rename to music_kraken/utils/config/config_files/youtube_config.py index 8870a54..4610f18 100644 --- a/src/music_kraken/utils/config/config_files/youtube_config.py +++ b/music_kraken/utils/config/config_files/youtube_config.py @@ -9,7 +9,7 @@ from ..attributes.attribute import Attribute from ..attributes.special_attributes import SelectAttribute, PathAttribute, UrlAttribute -config = Config([ +config = Config(( Attribute(name="use_youtube_alongside_youtube_music", default_value=False, description="""If set to true, it will search youtube through invidious and piped, despite a direct wrapper for the youtube music INNERTUBE api being implemented. I my INNERTUBE api wrapper doesn't work, set this to true."""), @@ -30,11 +30,15 @@ Dw. if it is empty, Rachel will fetch it automatically for you <333 Attribute(name="youtube_music_clean_data", default_value=True, description="If set to true, it exclusively fetches artists/albums/songs, not things like user channels etc."), UrlAttribute(name="youtube_url", default_value=[ "https://www.youtube.com/", - "https://www.youtu.be/" + "https://www.youtu.be/", + "https://music.youtube.com/", ], description="""This is used to detect, if an url is from youtube, or any alternativ frontend. If any instance seems to be missing, run music kraken with the -f flag."""), Attribute(name="use_sponsor_block", default_value=True, description="Use sponsor block to remove adds or simmilar from the youtube videos."), + Attribute(name="player_url", default_value="https://music.youtube.com/s/player/80b90bfd/player_ias.vflset/en_US/base.js", description=""" + This is needed to fetch videos without invidious + """), Attribute(name="youtube_music_consent_cookies", default_value={ "CONSENT": "PENDING+258" }, description="The cookie with the key CONSENT says to what stuff you agree. Per default you decline all cookies, but it honestly doesn't matter."), @@ -89,8 +93,9 @@ If any instance seems to be missing, run music kraken with the -f flag."""), "adSignalsInfo": { "params": [] } - }, description="Don't bother about this. It is something technical, but if you wanna change the innertube requests... go on.") -], LOCATIONS.get_config_file("youtube")) + }, description="Don't bother about this. It is something technical, but if you wanna change the innertube requests... go on."), + Attribute(name="ytcfg", description="Please... ignore it.", default_value={}) +), LOCATIONS.get_config_file("youtube")) class SettingsStructure(TypedDict): @@ -102,5 +107,7 @@ class SettingsStructure(TypedDict): youtube_music_clean_data: bool youtube_url: List[ParseResult] use_sponsor_block: bool + player_url: str youtube_music_innertube_context: dict youtube_music_consent_cookies: dict + ytcfg: dict diff --git a/src/music_kraken/utils/config/utils.py b/music_kraken/utils/config/utils.py similarity index 100% rename from src/music_kraken/utils/config/utils.py rename to music_kraken/utils/config/utils.py diff --git a/music_kraken/utils/enums/__init__.py b/music_kraken/utils/enums/__init__.py new file mode 100644 index 0000000..b90f9aa --- /dev/null +++ b/music_kraken/utils/enums/__init__.py @@ -0,0 +1 @@ +from .source import SourcePages \ No newline at end of file diff --git a/src/music_kraken/utils/enums/album.py b/music_kraken/utils/enums/album.py similarity index 100% rename from src/music_kraken/utils/enums/album.py rename to music_kraken/utils/enums/album.py diff --git a/music_kraken/utils/enums/colors.py b/music_kraken/utils/enums/colors.py new file mode 100644 index 0000000..a9fac51 --- /dev/null +++ b/music_kraken/utils/enums/colors.py @@ -0,0 +1,19 @@ +from enum import Enum + + +class BColors(Enum): + # https://stackoverflow.com/a/287944 + HEADER = "\033[95m" + OKBLUE = "\033[94m" + OKCYAN = "\033[96m" + OKGREEN = "\033[92m" + WARNING = "\033[93m" + FAIL = "\033[91m" + ENDC = "\033[0m" + BOLD = "\033[1m" + UNDERLINE = "\033[4m" + + GREY = "\x1b[38;20m" + YELLOW = "\x1b[33;20m" + RED = "\x1b[31;20m" + BOLD_RED = "\x1b[31;1m" diff --git a/music_kraken/utils/enums/contact.py b/music_kraken/utils/enums/contact.py new file mode 100644 index 0000000..938c801 --- /dev/null +++ b/music_kraken/utils/enums/contact.py @@ -0,0 +1,7 @@ +from enum import Enum + + +class ContactMethod(Enum): + EMAIL = "email" + PHONE = "phone" + FAX = "fax" diff --git a/src/music_kraken/utils/enums/source.py b/music_kraken/utils/enums/source.py similarity index 100% rename from src/music_kraken/utils/enums/source.py rename to music_kraken/utils/enums/source.py diff --git a/src/music_kraken/utils/exception/__init__.py b/music_kraken/utils/exception/__init__.py similarity index 100% rename from src/music_kraken/utils/exception/__init__.py rename to music_kraken/utils/exception/__init__.py diff --git a/src/music_kraken/utils/exception/config.py b/music_kraken/utils/exception/config.py similarity index 100% rename from src/music_kraken/utils/exception/config.py rename to music_kraken/utils/exception/config.py diff --git a/src/music_kraken/utils/exception/download.py b/music_kraken/utils/exception/download.py similarity index 100% rename from src/music_kraken/utils/exception/download.py rename to music_kraken/utils/exception/download.py diff --git a/music_kraken/utils/exception/objects.py b/music_kraken/utils/exception/objects.py new file mode 100644 index 0000000..ff3026d --- /dev/null +++ b/music_kraken/utils/exception/objects.py @@ -0,0 +1,10 @@ +class ObjectException(Exception): + pass + + +class IsDynamicException(Exception): + """ + Gets raised, if a dynamic data object tries to perform an action, + which does not make sense for a dynamic object. + """ + pass diff --git a/music_kraken/utils/hacking.py b/music_kraken/utils/hacking.py new file mode 100644 index 0000000..e68356e --- /dev/null +++ b/music_kraken/utils/hacking.py @@ -0,0 +1,303 @@ +# -*- encoding: utf-8 -*- +# merge_args v0.1.5 +# Merge signatures of two functions with Advanced Hackery. +# Copyright © 2018-2023, Chris Warrick. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions, and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions, and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the author of this software nor the names of +# contributors to this software may be used to endorse or promote +# products derived from this software without specific prior written +# consent. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +""" +Merge signatures of two functions with Advanced Hackery. Useful for wrappers. + +Usage: @merge_args(old_function) +""" + +import weakref +from types import FunctionType +from functools import wraps +from typing import Dict, Set + +import inspect +import itertools +import types +import functools +import sys +import typing + +__version__ = '0.1.5' +__all__ = ('merge_args',) + + +PY38 = sys.version_info >= (3, 8) +PY310 = sys.version_info >= (3, 10) +PY311 = sys.version_info >= (3, 11) + + +def _blank(): # pragma: no cover + pass + + +def _merge( + source, + dest, + drop_args: typing.Optional[typing.List[str]] = None, + drop_kwonlyargs: typing.Optional[typing.List[str]] = None, +): + """Merge the signatures of ``source`` and ``dest``. + + ``dest`` args go before ``source`` args in all three categories + (positional, keyword-maybe, keyword-only). + """ + if drop_args is None: + drop_args = [] + if drop_kwonlyargs is None: + drop_kwonlyargs = [] + source_spec = inspect.getfullargspec(source) + dest_spec = inspect.getfullargspec(dest) + + if source_spec.varargs or source_spec.varkw: + return dest + + source_all = source_spec.args + dest_all = dest_spec.args + + if source_spec.defaults: + source_pos = source_all[:-len(source_spec.defaults)] + source_kw = source_all[-len(source_spec.defaults):] + else: + source_pos = source_all + source_kw = [] + + if dest_spec.defaults: + dest_pos = dest_all[:-len(dest_spec.defaults)] + dest_kw = dest_all[-len(dest_spec.defaults):] + else: + dest_pos = dest_all + dest_kw = [] + + args_merged = dest_pos + for a in source_pos: + if a not in args_merged and a not in drop_args: + args_merged.append(a) + + defaults_merged = [] + for a, default in itertools.chain( + zip(dest_kw, dest_spec.defaults or []), + zip(source_kw, source_spec.defaults or []) + ): + if a not in args_merged and a not in drop_args: + args_merged.append(a) + defaults_merged.append(default) + + kwonlyargs_merged = dest_spec.kwonlyargs + for a in source_spec.kwonlyargs: + if a not in kwonlyargs_merged and a not in drop_kwonlyargs: + kwonlyargs_merged.append(a) + + args_all = tuple(args_merged + kwonlyargs_merged) + + if PY38: + replace_kwargs = { + 'co_argcount': len(args_merged), + 'co_kwonlyargcount': len(kwonlyargs_merged), + 'co_posonlyargcount': dest.__code__.co_posonlyargcount, + 'co_nlocals': len(args_all), + 'co_flags': source.__code__.co_flags, + 'co_varnames': args_all, + 'co_filename': dest.__code__.co_filename, + 'co_name': dest.__code__.co_name, + 'co_firstlineno': dest.__code__.co_firstlineno, + } + + if PY310: + replace_kwargs['co_linetable'] = dest.__code__.co_linetable + else: + replace_kwargs['co_lnotab'] = dest.__code__.co_lnotab + + if PY311: + replace_kwargs['co_exceptiontable'] = dest.__code__.co_exceptiontable + replace_kwargs['co_qualname'] = dest.__code__.co_qualname + + passer_code = _blank.__code__.replace(**replace_kwargs) + else: + passer_args = [ + len(args_merged), + len(kwonlyargs_merged), + _blank.__code__.co_nlocals, + _blank.__code__.co_stacksize, + source.__code__.co_flags, + _blank.__code__.co_code, (), (), + args_all, dest.__code__.co_filename, + dest.__code__.co_name, + dest.__code__.co_firstlineno, + dest.__code__.co_lnotab, + ] + passer_code = types.CodeType(*passer_args) + + passer = types.FunctionType(passer_code, globals()) + dest.__wrapped__ = passer + + # annotations + + # ensure we take destination’s return annotation + has_dest_ret = 'return' in dest.__annotations__ + if has_dest_ret: + dest_ret = dest.__annotations__['return'] + + for v in ('__kwdefaults__', '__annotations__'): + out = getattr(source, v) + if out is None: + out = {} + if getattr(dest, v) is not None: + out = out.copy() + out.update(getattr(dest, v)) + setattr(passer, v, out) + + if has_dest_ret: + passer.__annotations__['return'] = dest_ret + dest.__annotations__ = passer.__annotations__ + + passer.__defaults__ = tuple(defaults_merged) + if not dest.__doc__: + dest.__doc__ = source.__doc__ + return dest + + +def merge_args( + source, + drop_args: typing.Optional[typing.List[str]] = None, + drop_kwonlyargs: typing.Optional[typing.List[str]] = None, +): + """Merge the signatures of two functions.""" + try: + return functools.partial( + lambda x, y: _merge(x, y, drop_args, drop_kwonlyargs), source + ) + except TypeError: + pass + + +class Lake: + def __init__(self): + self.redirects: Dict[int, int] = {} + self.id_to_object: Dict[int, object] = {} + + def get_real_object(self, db_object: object) -> object: + _id = id(db_object) + while _id in self.redirects: + _id = self.redirects[_id] + + try: + return self.id_to_object[_id] + except KeyError: + self.add(db_object) + return db_object + + def add(self, db_object: object): + self.id_to_object[id(db_object)] = db_object + + def override(self, to_override: object, new_db_object: object): + _id = id(to_override) + while _id in self.redirects: + _id = self.redirects[_id] + + if id(new_db_object) in self.id_to_object: + print("!!!!!") + + self.add(new_db_object) + self.redirects[_id] = id(new_db_object) + # if _id in self.id_to_object: + # del self.id_to_object[_id] + + def is_same(self, __object: object, other: object) -> bool: + _self_id = id(__object) + while _self_id in self.redirects: + _self_id = self.redirects[_self_id] + + _other_id = id(other) + while _other_id in self.redirects: + _other_id = self.redirects[_other_id] + + return _self_id == _other_id + + +lake = Lake() + + +def wrapper(method): + @wraps(method) + def wrapped(*args, **kwargs): + return method(*(lake.get_real_object(args[0]), *args[1:]), **kwargs) + + return wrapped + + +class BaseClass: + def __new__(cls, *args, **kwargs): + instance = cls(*args, **kwargs) + print("new") + lake.add(instance) + return instance + + def __eq__(self, other): + return lake.is_same(self, other) + + def _risky_merge(self, to_replace): + lake.override(to_replace, self) + + +class MetaClass(type): + def __new__(meta, classname, bases, classDict): + bases = (*bases, BaseClass) + newClassDict = {} + + ignore_functions: Set[str] = {"__new__", "__init__"} + + for attributeName, attribute in classDict.items(): + if isinstance(attribute, FunctionType) and (attributeName not in ignore_functions): + """ + The funktion new and init shouldn't be accounted for because we can assume the class is + independent on initialization. + """ + attribute = wrapper(attribute) + + newClassDict[attributeName] = attribute + + print() + + for key, value in object.__dict__.items(): + # hasattr( value, '__call__' ) and + if hasattr(value, '__call__') and value not in newClassDict and key not in ("__new__", "__init__"): + newClassDict[key] = wrapper(value) + + new_instance = type.__new__(meta, classname, bases, newClassDict) + + lake.add(new_instance) + + return new_instance diff --git a/src/music_kraken/utils/path_manager/__init__.py b/music_kraken/utils/path_manager/__init__.py similarity index 100% rename from src/music_kraken/utils/path_manager/__init__.py rename to music_kraken/utils/path_manager/__init__.py diff --git a/src/music_kraken/utils/path_manager/config_directory.py b/music_kraken/utils/path_manager/config_directory.py similarity index 100% rename from src/music_kraken/utils/path_manager/config_directory.py rename to music_kraken/utils/path_manager/config_directory.py diff --git a/music_kraken/utils/path_manager/locations.py b/music_kraken/utils/path_manager/locations.py new file mode 100644 index 0000000..a3917bf --- /dev/null +++ b/music_kraken/utils/path_manager/locations.py @@ -0,0 +1,88 @@ +import configparser +from pathlib import Path +import os +from os.path import expandvars +import logging +from sys import platform + +import tempfile +from typing import Optional + +from pyffmpeg import FFmpeg + + +from .music_directory import get_music_directory +from .config_directory import get_config_directory + + +class Locations: + @staticmethod + def _get_env(key: str, default: Path, default_for_windows: bool = True) -> Optional[Path]: + res = os.environ.get(key.upper()) + if res is not None: + return res + + xdg_user_dirs_file = os.environ.get("XDG_CONFIG_HOME") or Path(Path.home(), ".config", "user-dirs.dirs") + xdg_user_dirs_default_file = Path("/etc/xdg/user-dirs.defaults") + + def get_dir_from_xdg_file(xdg_file_path: os.PathLike) -> Optional[Path]: + nonlocal key + + try: + with open(xdg_file_path, 'r') as f: + data = "[XDG_USER_DIRS]\n" + f.read() + config = configparser.ConfigParser(allow_no_value=True) + config.read_string(data) + xdg_config = config['XDG_USER_DIRS'] + + return Path(expandvars(xdg_config[key.lower()].strip('"'))) + + except (FileNotFoundError, KeyError) as e: + logging.warning( + f"Missing file or No entry found for \"{key}\" in: \"{xdg_file_path}\".\n" + ) + logging.debug(str(e)) + + res = get_dir_from_xdg_file(xdg_user_dirs_file) + if res is not None: + return res + + res = get_dir_from_xdg_file(xdg_user_dirs_default_file) + if res is not None: + return res + + logging.warning(f"couldn't find a {key}, falling back to: {default}") + + if not default_for_windows and platform == "linux": + return + + return default + + def __init__(self, application_name: os.PathLike = "music-kraken"): + self.FILE_ENCODING: str = "utf-8" + + self.TEMP_DIRECTORY = Path(tempfile.gettempdir(), application_name) + self.TEMP_DIRECTORY.mkdir(exist_ok=True, parents=True) + + self.MUSIC_DIRECTORY = get_music_directory() + + self.CONFIG_DIRECTORY = get_config_directory(str(application_name)) + self.CONFIG_DIRECTORY.mkdir(exist_ok=True, parents=True) + self.CONFIG_FILE = Path(self.CONFIG_DIRECTORY, f"{application_name}.conf") + self.LEGACY_CONFIG_FILE = Path(self.CONFIG_DIRECTORY, f"{application_name}.conf") + + self.CACHE_DIRECTORY = self._get_env("XDG_CACHE_HOME", Path(Path.home(), ".cache")) + if self.CACHE_DIRECTORY is None: + logging.warning(f"Could not find a cache dir. Falling back to the temp dir: {self.TEMP_DIRECTORY}") + self.CACHE_DIRECTORY = self.TEMP_DIRECTORY + else: + self.CACHE_DIRECTORY = Path(self.CACHE_DIRECTORY, application_name) + self.CACHE_DIRECTORY.mkdir(parents=True, exist_ok=True) + + self.FFMPEG_BIN = Path(FFmpeg(enable_log=False).get_ffmpeg_bin()) + + def get_config_file(self, config_name: str) -> Path: + return Path(self.CONFIG_DIRECTORY, f"{config_name}.toml") + + def get_log_file(self, file_name: os.PathLike) -> Path: + return Path(self.TEMP_DIRECTORY, file_name) diff --git a/src/music_kraken/utils/path_manager/music_directory.py b/music_kraken/utils/path_manager/music_directory.py similarity index 94% rename from src/music_kraken/utils/path_manager/music_directory.py rename to music_kraken/utils/path_manager/music_directory.py index c0e0167..aa7ef09 100644 --- a/src/music_kraken/utils/path_manager/music_directory.py +++ b/music_kraken/utils/path_manager/music_directory.py @@ -22,7 +22,7 @@ def get_xdg_music_directory() -> Path: https://web.archive.org/web/20230322012953/https://freedesktop.org/wiki/Software/xdg-user-dirs/ """ - xdg_user_dirs_file = os.environ.get("XDG_CONFIG_HOME") or Path(Path.home(), ".config", "user-dirs.dirs") + xdg_user_dirs_file = Path(os.environ.get("XDG_CONFIG_HOME") or Path(Path.home(), ".config"), "user-dirs.dirs") xdg_user_dirs_default_file = Path("/etc/xdg/user-dirs.defaults") def get_music_dir_from_xdg_file(xdg_file_path: os.PathLike) -> Optional[Path]: diff --git a/music_kraken/utils/shared.py b/music_kraken/utils/shared.py new file mode 100644 index 0000000..cf3cda7 --- /dev/null +++ b/music_kraken/utils/shared.py @@ -0,0 +1,50 @@ +import random +from dotenv import load_dotenv +from pathlib import Path +import os + + +from .path_manager import LOCATIONS +from .config import main_settings + +if not load_dotenv(Path(__file__).parent.parent.parent / ".env"): + load_dotenv(Path(__file__).parent.parent.parent / ".env.example") + +__stage__ = os.getenv("STAGE", "prod") + +DEBUG = (__stage__ == "dev") and True +DEBUG_LOGGING = DEBUG and False +DEBUG_TRACE = DEBUG and True +DEBUG_YOUTUBE_INITIALIZING = DEBUG and False +DEBUG_PAGES = DEBUG and False +DEBUG_DUMP = DEBUG and True + +if DEBUG: + print("DEBUG ACTIVE") + + +def get_random_message() -> str: + return random.choice(main_settings['happy_messages']) + + +CONFIG_DIRECTORY = LOCATIONS.CONFIG_DIRECTORY + +HIGHEST_ID = 2 ** main_settings['id_bits'] + +HELP_MESSAGE = """to search: +> s: {query or url} +> s: https://musify.club/release/some-random-release-183028492 +> s: #a {artist} #r {release} #t {track} + +to download: +> d: {option ids or direct url} +> d: 0, 3, 4 +> d: 1 +> d: https://musify.club/release/some-random-release-183028492 + +have fun :3""".strip() + +# regex pattern +URL_PATTERN = r"https?://(?:[-\w.]|(?:%[\da-fA-F]{2}))+" +INT_PATTERN = r"^\d*$" +FLOAT_PATTERN = r"^[\d|\,|\.]*$" diff --git a/src/music_kraken/utils/string_processing.py b/music_kraken/utils/string_processing.py similarity index 58% rename from src/music_kraken/utils/string_processing.py rename to music_kraken/utils/string_processing.py index 3c1cedc..39963e9 100644 --- a/src/music_kraken/utils/string_processing.py +++ b/music_kraken/utils/string_processing.py @@ -1,5 +1,6 @@ from typing import Tuple, Union from pathlib import Path +import string from transliterate.exceptions import LanguageDetectionError from transliterate import translit @@ -82,3 +83,60 @@ def comment(uncommented_string: str) -> str: _fragments = ["# " + frag for frag in _fragments] return "\n".join(_fragments) + +# comparisons +TITLE_THRESHOLD_LEVENSHTEIN = 1 +UNIFY_TO = " " + +ALLOWED_LENGTH_DISTANCE = 20 + + +def unify_punctuation(to_unify: str) -> str: + for char in string.punctuation: + to_unify = to_unify.replace(char, UNIFY_TO) + return to_unify + + +def remove_feature_part_from_track(title: str) -> str: + if ")" != title[-1]: + return title + if "(" not in title: + return title + + return title[:title.index("(")] + + +def modify_title(to_modify: str) -> str: + to_modify = to_modify.strip() + to_modify = to_modify.lower() + to_modify = remove_feature_part_from_track(to_modify) + to_modify = unify_punctuation(to_modify) + return to_modify + + +def match_titles(title_1: str, title_2: str): + title_1, title_2 = modify_title(title_1), modify_title(title_2) + distance = jellyfish.levenshtein_distance(title_1, title_2) + return distance > TITLE_THRESHOLD_LEVENSHTEIN, distance + + +def match_artists(artist_1, artist_2: str): + if type(artist_1) == list: + distances = [] + + for artist_1_ in artist_1: + match, distance = match_titles(artist_1_, artist_2) + if not match: + return match, distance + + distances.append(distance) + return True, min(distances) + return match_titles(artist_1, artist_2) + +def match_length(length_1: int | None, length_2: int | None) -> bool: + # returning true if either one is Null, because if one value is not known, + # then it shouldn't be an attribute which could reject an audio source + if length_1 is None or length_2 is None: + return True + return abs(length_1 - length_2) <= ALLOWED_LENGTH_DISTANCE + diff --git a/src/music_kraken/utils/config/sections/__init__.py b/music_kraken/utils/support_classes/__init__.py similarity index 100% rename from src/music_kraken/utils/config/sections/__init__.py rename to music_kraken/utils/support_classes/__init__.py diff --git a/src/music_kraken/utils/support_classes/download_result.py b/music_kraken/utils/support_classes/download_result.py similarity index 83% rename from src/music_kraken/utils/support_classes/download_result.py rename to music_kraken/utils/support_classes/download_result.py index 11f3417..5458a34 100644 --- a/src/music_kraken/utils/support_classes/download_result.py +++ b/music_kraken/utils/support_classes/download_result.py @@ -2,12 +2,12 @@ from dataclasses import dataclass, field from typing import List, Tuple from ...utils.config import main_settings, logging_settings +from ...utils.enums.colors import BColors from ...objects import Target UNIT_PREFIXES: List[str] = ["", "k", "m", "g", "t"] UNIT_DIVISOR = 1024 - LOGGER = logging_settings["download_logger"] @@ -83,16 +83,16 @@ class DownloadResult: def __str__(self): if self.is_fatal_error: return self.error_message - head = f"{self.fail} from {self.total} downloads failed:\n" \ - f"successrate:\t{int(self.success_percentage * 100)}%\n" \ - f"failrate:\t{int(self.failure_percentage * 100)}%\n" \ - f"total size:\t{self.formated_size}\n" \ - f"skipped segments:\t{self.sponsor_segments}\n" \ - f"found on disc:\t{self.found_on_disk}" + head = f"{self.fail} from {self.total} downloads failed:\n" \ + f"success-rate:\t{int(self.success_percentage * 100)}%\n" \ + f"fail-rate:\t{int(self.failure_percentage * 100)}%\n" \ + f"total size:\t{self.formated_size}\n" \ + f"skipped segments:\t{self.sponsor_segments}\n" \ + f"found on disc:\t{self.found_on_disk}" if not self.is_mild_failure: return head _lines = [head] - _lines.extend(self._error_message_list) + _lines.extend(BColors.FAIL.value + s + BColors.ENDC.value for s in self._error_message_list) return "\n".join(_lines) diff --git a/src/music_kraken/utils/support_classes/query.py b/music_kraken/utils/support_classes/query.py similarity index 94% rename from src/music_kraken/utils/support_classes/query.py rename to music_kraken/utils/support_classes/query.py index 239096b..d6d70c9 100644 --- a/src/music_kraken/utils/support_classes/query.py +++ b/music_kraken/utils/support_classes/query.py @@ -1,6 +1,6 @@ from typing import Optional, List -from ...objects import DatabaseObject, Artist, Album, Song +from ...objects import Artist, Album, Song, DatabaseObject class Query: def __init__( diff --git a/pyproject.toml b/pyproject.toml index e107dcc..0dd521b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,12 +1,26 @@ [build-system] -requires = ["setuptools>=58.0.4", "wheel>=0.37.0"] -build-backend = "setuptools.build_meta" +requires = ["hatchling", "hatch-requirements-txt" ] +build-backend = "hatchling.build" + +[tool.hatch.build] +directory = "dist" + +[tool.hatch.build.targets.sdist] +include = ["music_kraken/*.py", "music_kraken/**/*.py" ] + +[tool.hatch.build.targets.wheel] +packages = ["music_kraken"] + +[project.scripts] +music-kraken = "music_kraken.__main__:cli" + +[tool.hatch.version] +path = "music_kraken/__init__.py" [project] name = "music-kraken" -version = "1.2.1" description = "An extensive music downloader crawling the internet. It gets its metadata from a couple of metadata providers, and it scrapes the audiofiles." -authors = [{ name = "Hellow2", email = "Hellow2@outlook.de" }] +authors = [{ name = "Hellow2", email = "hazel_is_cute@proton.me" }] license = "AGPL-3.0-or-later" readme = "README.md" repository = "https://github.com/HeIIow2/music-downloader" @@ -22,26 +36,32 @@ classifiers = [ "Topic :: Multimedia :: Sound/Audio", "Topic :: Utilities", ] -[project.dependencies] -requests = "~=2.28.1" -mutagen = "~=1.46.0" -musicbrainzngs = "~=0.7.1" -jellyfish = "~=0.9.0" -pydub = "~=0.25.1" -youtube_dl = "*" -beautifulsoup4 = "~=4.11.1" -pycountry = "~=22.3.5" +dependencies = [ + "requests~=2.31.0", + "responses~=0.24.1", + "beautifulsoup4~=4.11.1", + "pyffmpeg~=2.4.2.18.1", + "ffmpeg-progress-yield~=0.7.8", + "mutagen~=1.46.0", -[tool.setuptools.entry_points] -music-kraken = "music_kraken:cli" + "rich~=13.7.1", + "mistune~=3.0.2", + "html2markdown~=0.1.7", + "jellyfish~=0.9.0", + "transliterate~=1.10.2", + "pycountry~=23.12.11", + + "python-dotenv~=1.0.1", + "tqdm~=4.65.0", + "platformdirs~=4.2.0", + "pathvalidate~=2.5.2", + "toml~=0.10.2", + "typing_extensions~=4.7.1", -[tool.setuptools] -packages = ['music_kraken', 'music_kraken.lyrics', 'music_kraken.not_used_anymore', 'music_kraken.target', 'music_kraken.tagging', 'music_kraken.utils', 'music_kraken.not_used_anymore.sources', 'music_kraken.database', 'music_kraken.static_files'] -include_package_data = true -package_dir = {''= 'src', 'music_kraken'= 'src/music_kraken'} -package_data = {'music_kraken'= ['*.sql']} -data_files = ["", ["requirements.txt", "README.md", "version"]] - -[tool.setuptools.command.test] -# ... + "sponsorblock~=0.1.3", + "youtube_dl", +] +dynamic = [ + "version" +] diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 0000000..7ce1d72 --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,3 @@ +build +twine +hatch diff --git a/requirements.txt b/requirements.txt index 48443cf..7ba18a1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,19 +1,25 @@ -requests~=2.28.1 +requests~=2.31.0 mutagen~=1.46.0 musicbrainzngs~=0.7.1 jellyfish~=0.9.0 beautifulsoup4~=4.11.1 -pycountry~=22.3.5 +pycountry~=24.0.1 python-dateutil~=2.8.2 pandoc~=2.3 SQLAlchemy~=2.0.7 -setuptools~=60.2.0 +setuptools~=68.2.0 tqdm~=4.65.0 ffmpeg-python~=0.2.0 -platformdirs~=3.2.0 +platformdirs~=4.2.0 transliterate~=1.10.2 sponsorblock~=0.1.3 regex~=2022.9.13 pyffmpeg~=2.4.2.18 ffmpeg-progress-yield~=0.7.8 pathvalidate~=2.5.2 +guppy3~=3.1.3 +toml~=0.10.2 +typing_extensions~=4.7.1 +responses~=0.24.1 +youtube_dl +merge_args~=0.1.5 \ No newline at end of file diff --git a/setup.py b/setup.py deleted file mode 100644 index 1eebc18..0000000 --- a/setup.py +++ /dev/null @@ -1,65 +0,0 @@ -try: - from setuptools import setup, Command, find_packages - setuptools_available = True -except ImportError: - from distutils.core import setup, Command, find_packages - setuptools_available = False - -# packages=['music_kraken'], - -#packages = find_packages(where="src") -packages = ['music_kraken', 'music_kraken.lyrics', 'music_kraken.not_used_anymore', 'music_kraken.target', 'music_kraken.metadata', 'music_kraken.tagging', 'music_kraken.utils', 'music_kraken.not_used_anymore.sources', 'music_kraken.database', 'music_kraken.static_files'] - -print("packages") -print(packages) -# packages.extend(["music_kraken.database"]) - -with open("README.md", "r") as readme_file: - long_description = readme_file.read() - -install_requires = [ - "requests~=2.28.1", - "mutagen~=1.46.0", - "musicbrainzngs~=0.7.1", - "jellyfish~=0.9.0", - "pydub~=0.25.1", - "youtube_dl", - "beautifulsoup4~=4.11.1", - "pycountry~=22.3.5" -] - -with open("requirements.txt", "r") as requirements_txt: - install_requires = [] - for requirement in requirements_txt: - requirement = requirement.strip() - install_requires.append(requirement) - print(requirement) - -version = '1.2.1' -with open('version', 'r') as version_file: - version = version_file.read() - print(f"version: {version}") - -setup( - name='music-kraken', - version=version, - description='An extensive music downloader crawling the internet. It gets its metadata from a couple of metadata ' - 'providers, and it scrapes the audiofiles.', - long_description=long_description, - long_description_content_type='text/markdown', - author='Hellow2', - author_email='Hellow2@outlook.de', - url='https://github.com/HeIIow2/music-downloader', - packages=packages, - package_dir={'': 'src', 'music_kraken': 'src/music_kraken'}, - install_requires=install_requires, - entry_points={'console_scripts': ['music-kraken = music_kraken:cli']}, - include_package_data=True, - package_data={'music_kraken': ['*.sql']}, - data_files=[ - ('', ['requirements.txt', 'README.md', 'version']) - ] -) - -# ('music_kraken', ['static_files/database_structure.sql']), - diff --git a/src/actual_donwload.py b/src/actual_donwload.py deleted file mode 100644 index 78ecefd..0000000 --- a/src/actual_donwload.py +++ /dev/null @@ -1,36 +0,0 @@ -import music_kraken - - -if __name__ == "__main__": - normally_download = [ - "s: #a Favorite #r Anarcho", - "1", - "d: 1, 5" - ] - - direct_download = [ - "d: https://musify.club/release/crystal-f-x-2012-795181" - ] - - fetch_musify_song = [ - "s: https://musify.club/track/blokkmonsta-schwartz-crystal-f-purer-hass-8369115" - ] - - fetch_youtube_playlist = [ - "s: https://yt.artemislena.eu/playlist?list=OLAK5uy_kcUBiDv5ATbl-R20OjNaZ5G28XFanQOmM" - ] - - download_youtube_playlist = ["d: https://www.youtube.com/playlist?list=OLAK5uy_lqI_c6aDF9q4DWJ4TBzt1AFQYx_FXfU4E"] - - youtube_search = [ - "s: #a Zombiez", - "10", - "d: 5" - ] - - youtube_music_test = [ - "s: #a Favorite #r Anarcho", - "0" - ] - - music_kraken.cli.download(genre="test", command_list=youtube_music_test, process_metadata_anyway=True) diff --git a/src/create_custom_objects.py b/src/create_custom_objects.py deleted file mode 100644 index 47017be..0000000 --- a/src/create_custom_objects.py +++ /dev/null @@ -1,63 +0,0 @@ -from music_kraken import objects, recurse - -import pycountry - - -song = objects.Song( - genre="HS Core", - title="Vein Deep in the Solution", - length=666, - isrc="US-S1Z-99-00001", - tracksort=2, - target=[ - objects.Target(file="song.mp3", path="example") - ], - lyrics_list=[ - objects.Lyrics(text="these are some depressive lyrics", language="en"), - objects.Lyrics(text="Dies sind depressive Lyrics", language="de") - ], - source_list=[ - objects.Source(objects.SourcePages.YOUTUBE, "https://youtu.be/dfnsdajlhkjhsd"), - objects.Source(objects.SourcePages.MUSIFY, "https://ln.topdf.de/Music-Kraken/") - ], - album_list=[ - objects.Album( - title="One Final Action", - date=objects.ID3Timestamp(year=1986, month=3, day=1), - language=pycountry.languages.get(alpha_2="en"), - label_list=[ - objects.Label(name="an album label") - ], - source_list=[ - objects.Source(objects.SourcePages.ENCYCLOPAEDIA_METALLUM, "https://www.metal-archives.com/albums/I%27m_in_a_Coffin/One_Final_Action/207614") - ] - ), - ], - main_artist_list=[ - objects.Artist( - name="I'm in a coffin", - source_list=[ - objects.Source( - objects.SourcePages.ENCYCLOPAEDIA_METALLUM, - "https://www.metal-archives.com/bands/I%27m_in_a_Coffin/127727" - ) - ], - label_list=[ - objects.Label(name="Depressive records") - ] - ), - objects.Artist(name="some_split_artist") - ], - feature_artist_list=[ - objects.Artist( - name="Ruffiction", - label_list=[ - objects.Label(name="Ruffiction Productions") - ] - ) - ], -) - -song.compile() - -print(song.options) diff --git a/src/metal_archives.py b/src/metal_archives.py deleted file mode 100644 index e603849..0000000 --- a/src/metal_archives.py +++ /dev/null @@ -1,41 +0,0 @@ -from music_kraken import objects -from music_kraken.pages import EncyclopaediaMetallum - - -def search(): - results = EncyclopaediaMetallum._raw_search("#a Ghost Bath") - print(results) - print(results[0].source_collection) - - -def fetch_artist(): - artist = objects.Artist( - source_list=[ - objects.Source(objects.SourcePages.MUSIFY, "https://musify.club/artist/psychonaut-4-83193"), - objects.Source(objects.SourcePages.ENCYCLOPAEDIA_METALLUM, "https://www.metal-archives.com/bands/Ghost_Bath/3540372489") - ] - ) - - artist: objects.Artist = EncyclopaediaMetallum.fetch_details(artist) - print(artist.options) - - -def fetch_album(): - album = objects.Album( - source_list=[objects.Source(objects.SourcePages.MUSIFY, - "https://musify.club/release/linkin-park-hybrid-theory-2000-188")] - ) - - album: objects.Album = EncyclopaediaMetallum.fetch_details(album) - print(album.options) - - song: objects.Song - for artist in album.artist_collection: - print(artist.id, artist.name) - for song in album.song_collection: - for artist in song.main_artist_collection: - print(artist.id, artist.name) - - -if __name__ == "__main__": - fetch_artist() diff --git a/src/music_kraken.egg-info/PKG-INFO b/src/music_kraken.egg-info/PKG-INFO deleted file mode 100644 index 0db5715..0000000 --- a/src/music_kraken.egg-info/PKG-INFO +++ /dev/null @@ -1,704 +0,0 @@ -Metadata-Version: 2.1 -Name: music-kraken -Version: 1.2.2 -Summary: An extensive music downloader crawling the internet. It gets its metadata from a couple of metadata providers, and it scrapes the audiofiles. -Home-page: https://github.com/HeIIow2/music-downloader -Author: Hellow2 -Author-email: Hellow2@outlook.de -License: UNKNOWN -Platform: UNKNOWN -Description-Content-Type: text/markdown -License-File: LICENSE - -# Music Kraken - - - -1. [Installlation](#installation) -2. [Command Line Usage](#quick-guide) -3. [Contribute](#contribute) -4. [Matrix Space](#matrix-space), if you don't wanna read: **[Invite](https://matrix.to/#/#music-kraken:matrix.org)** - -5. [Library Usage / Python Interface](#programming-interface--use-as-library) -6. [About Metadata](#metadata) -7. [About the Audio](#download) -8. [About the Lyrics](#lyrics) - ---- - -## Installation - -You can find and get this project from either [PyPI](https://pypi.org/project/music-kraken/) as a Python-Package, -or simply the source code from [GitHub](https://github.com/HeIIow2/music-downloader). Note that even though -everything **SHOULD** work cross-platform, I have only tested it on Ubuntu. -If you enjoy this project, feel free to give it a star on GitHub. - -```sh -# Install it with -pip install music-kraken - -# and simply run it like this: -music-kraken -``` - -## Dependencies -- ffmpeg -- pandoc - -### Notes for Python 3.9 - -Unfortunately I use features that newly git introduced in [Python 3.10](https://docs.python.org/3/library/types.html#types.UnionType). -So unfortunately you **CAN'T** run this programm with python 3.9. [#10][i10] - -### Notes for WSL - -If you choose to run it in WSL, make sure ` ~/.local/bin` is added to your `$PATH` [#2][i2] - -## Quick-Guide - -**Genre:** First, the cli asks you to input a genre you want to download to. The options it gives you (if it gives you any) are all the folders you have in the music directory. You can also just input a new one. - -**What to download:** After that it prompts you for a search. Here are a couple examples how you can search: - -``` -> #a -searches for the artist - -> #a #r -searches for the release (album) by the artist - -> #r Me #t -searches for the track from the release -``` - -After searching with this syntax, it prompts you with multiple results. You can either choose one of those by inputing its id `int`, or you can search for a new query. - -After you chose either an artist, a release group, a release, or a track by its id, download it by inputting the string `ok`. My downloader will download it automatically for you. - ---- - -## CONTRIBUTE - -I am happy about every pull request. To contribute look [here](contribute.md). - -## Matrix Space - - - -I decided against creating a discord server, due to piracy communities get often banned from discord. A good and free Alternative are Matrix Spaces. I reccomend the use of the Client [Element](https://element.io/download). It is completely open source. - -**Click [this link](https://matrix.to/#/#music-kraken:matrix.org) _([https://matrix.to/#/#music-kraken:matrix.org](https://matrix.to/#/#music-kraken:matrix.org))_ to join.** - ---- - -# Programming Interface / Use as Library - -This application is $100\%$ centered around Data. Thus the most important thing for working with musik kraken is, to understand how I structured the data. - -## quick Overview - -- explanation of the [Data Model](#data-model) -- how to use the [Data Objects](#data-objects) - -```mermaid ---- -title: Quick Overview ---- -sequenceDiagram - -participant pg as Page (eg. YouTube, MB, Musify, ...) -participant obj as DataObjects (eg. Song, Artist, ...) -participant db as DataBase - -obj ->> db: write -db ->> obj: read - -pg -> obj: find a source for any page, for object. -obj -> pg: add more detailed data from according page. -obj -> pg: if available download audio to target. -``` - -## Data Model - -The Data Structure, that the whole programm is built on looks as follows: - -```mermaid ---- -title: Music Data ---- -erDiagram - - - -Target { - -} - -Lyrics { - -} - -Song { - -} - -Album { - -} - -Artist { - -} - -Label { - -} - -Source { - -} - -Source }o--|| Song : from -Source }o--|| Lyrics : from -Source }o--|| Album : from -Source }o--|| Artist : from -Source }o--|| Label : from - -Song }o--o{ Album : AlbumSong -Album }o--o{ Artist : ArtistAlbum -Song }o--o{ Artist : features - -Label }o--o{ Album : LabelAlbum -Label }o--o{ Artist : LabelSong - -Song ||--o{ Lyrics : contains -Song ||--o{ Target : points -``` - -Ok now this **WILL** look intimidating, thus I break it down quickly. -*That is also the reason I didn't add all Attributes here.* - -The most important Entities are: - -- Song -- Album -- Artist -- Label - -All of them *(and Lyrics)* can have multiple Sources, and every Source can only Point to one of those Element. - -The `Target` Entity represents the location on the hard drive a Song has. One Song can have multiple download Locations. - -The `Lyrics` Entity simply represents the Lyrics of each Song. One Song can have multiple Lyrics, e.g. Translations. - -Here is the simplified Diagramm without only the main Entities. - - -```mermaid ---- -title: simplified Music Data ---- -erDiagram - -Song { - -} - -Album { - -} - -Artist { - -} - -Label { - -} - -Song }o--o{ Album : AlbumSong -Album }o--o{ Artist : ArtistAlbum -Song }o--o{ Artist : features - -Label }o--o{ Album : LabelAlbum -Label }o--o{ Artist : LabelSong - -``` - -Looks way more manageable, doesn't it? - -The reason every relation here is a `n:m` *(many to many)* relation is not, that it makes sense in the aspekt of modeling reality, but to be able to put data from many Sources in the same Data Model. -Every Service models Data a bit different, and projecting a one-to-many relationship to a many to many relationship without data loss is easy. The other way around it is basically impossible - -## Data Objects - -> Not 100% accurate yet and *might* change slightly - -### Creation - -```python -# importing the libraries I build on -from music_kraken import objects - -import pycountry - - -song = objects.Song( - genre="HS Core", - title="Vein Deep in the Solution", - length=666, - isrc="US-S1Z-99-00001", - tracksort=2, - target=[ - objects.Target(file="song.mp3", path="example") - ], - lyrics_list=[ - objects.Lyrics(text="these are some depressive lyrics", language="en"), - objects.Lyrics(text="Dies sind depressive Lyrics", language="de") - ], - source_list=[ - objects.Source(objects.SourcePages.YOUTUBE, "https://youtu.be/dfnsdajlhkjhsd"), - objects.Source(objects.SourcePages.MUSIFY, "https://ln.topdf.de/Music-Kraken/") - ], - album_list=[ - objects.Album( - title="One Final Action", - date=objects.ID3Timestamp(year=1986, month=3, day=1), - language=pycountry.languages.get(alpha_2="en"), - label_list=[ - objects.Label(name="an album label") - ], - source_list=[ - objects.Source(objects.SourcePages.ENCYCLOPAEDIA_METALLUM, "https://www.metal-archives.com/albums/I%27m_in_a_Coffin/One_Final_Action/207614") - ] - ), - ], - main_artist_list=[ - objects.Artist( - name="I'm in a coffin", - source_list=[ - objects.Source( - objects.SourcePages.ENCYCLOPAEDIA_METALLUM, - "https://www.metal-archives.com/bands/I%27m_in_a_Coffin/127727" - ) - ] - ), - objects.Artist(name="some_split_artist") - ], - feature_artist_list=[ - objects.Artist( - name="Ruffiction", - label_list=[ - objects.Label(name="Ruffiction Productions") - ] - ) - ], -) - -print(song.option_string) -for album in song.album_collection: - print(album.option_string) -for artist in song.main_artist_collection: - print(artist.option_string) -``` - - - -If you just want to start implementing, then just use the code example, I don't care. -For those who don't want any bugs and use it as intended *(which is recommended, cuz I am only one person so there are defs bugs)* continue reading. - -## Appending and Merging data - -If you want to append for example a Song to an Album, you obviously need to check beforehand if the Song already exists in the Album, and if so, you need to merge their data in one Song object, to not loose any Information. - -Fortunately I implemented all of this functionality in [objects.Collection](#collection).append(music_object). -I made a flow chart showing how it works: - -```mermaid ---- -title: "Collection.append(music_object: MusicObject)" ---- -flowchart TD - exist(""" -Check if music_object already exists. -
-Gets all indexing values with music_object.indexing_values. -If any returned value exists in Collection._attribute_to_object_map, -the music_object exists - """) - - subgraph merge["Merging"] - - _merge("""merges the passed in object in the already - existing whith existing.merge(new)""") - - _map("""In case a new source or something simmilar - has been addet, it maps the existing object again. - """) - - _merge --> _map - - end - - subgraph add["Adding"] - - __map("""map the values from music_object.indexing_values - to Collection._attribute_to_object_map by writing - those values in the map as keys, and the class I wanna add as values. - """) - - _add("""add the new music object to _data""") - - __map --> _add - - end - - exist-->|"if it doesn't exist"|add --> return - exist-->|"if already exists"|merge --> return -``` - -This is Implemented in [music_kraken.objects.Collection.append()](src/music_kraken/objects/collection.py). - -The indexing values are defined in the superclass [DatabaseObject](src/music_kraken/objects/parents.py) and get implemented for each Object seperately. I will just give as example its implementation for the `Song` class: - -```python -@property -def indexing_values(self) -> List[Tuple[str, object]]: - return [ - ('id', self.id), - ('title', self.unified_title), - ('barcode', self.barcode), - *[('url', source.url) for source in self.source_collection] - ] -``` - -## Classes and Objects - -### music_kraken.objects - -#### Collection - -#### Song - -So as you can see, the probably most important Class is the `music_kraken.Song` class. It is used to save the song in *(duh)*. - -It has handful attributes, where half of em are self-explanatory, like `title` or `genre`. The ones like `isrc` are only relevant to you, if you know what it is, so I won't elaborate on it. - -Interesting is the `date`. It uses a custom class. More on that [here](#music_krakenid3timestamp). - -#### ID3Timestamp - -For multiple Reasons I don't use the default `datetime.datetime` class. - -The most important reason is, that you need to pass in at least year, month and day. For every other values there are default values, that are indistinguishable from values that are directly passed in. But I need optional values. The ID3 standart allows default values. Additionally `datetime.datetime` is immutable, thus I can't inherint all the methods. Sorry. - -Anyway you can create those custom objects easily. - -```python -from music_kraken import ID3Timestamp - -# returns an instance of ID3Timestamp with the current time -ID3Timestamp.now() - -# yea -ID3Timestamp(year=1986, month=3, day=1) -``` - -you can pass in the Arguments: - - year - - month - - day - - hour - - minute - - second - -:) - -# Old implementation - -> IF U USE THIS NOW YOU ARE DUMB *no offense thoug*. IT ISN'T FINISHED AND THE STUFF YOU CODE NOW WILL BE BROKEN TOMORROW -> SOON YOU CAN THOUGH - -If you want to use this project, or parts from it in your own projects from it, -make sure to be familiar with [Python Modules](https://docs.python.org/3/tutorial/modules.html). -Further and better documentation including code examples are yet to come, so here is the rough -module structure for now. (Should be up-to-date, but no guarantees) - -If you simply want to run the builtin minimal cli just do this: -```python -from music_kraken import cli - -cli() -``` - -### Search for Metadata - -The whole program takes the data it processes further from the cache, a sqlite database. -So before you can do anything, you will need to fill it with the songs you want to download (*or create song objects manually, but more on that later*). - -For now the base of everything is [musicbrainz][mb], so you need to get the musicbrainz `id` and `type`. The `id` corresponds to either - - an artist - - a release group - - a release - - a recording/track). - -To get this info, you first have to initialize a search object (`music_kraken.MetadataSearch`). - -```python -search_object = music_kraken.MetadataSearch() -``` - -Then you need an initial "text search" to get some options you can choose from. For -this you can either specify artists releases and whatever directly with one of the following functions: - -```python -# you can directly specify artist, release group, release or recording/track -multiple_options = search_object.search_from_text(artist=input("input the name of the artist: ")) -# you can specify a query see the simple integrated cli on how to use the query -multiple_options = search_object.search_from_query(query=input("input the query: ")) -``` - -Both methods return an instance of `MultipleOptions`, which can be directly converted to a string. - -```python -print(multiple_options) -``` - -After the first "*text search*" you can either again search the same way as before, -or you can further explore one of the options from the previous search. -To explore and select one options from `MultipleOptions`, simply call `MetadataSearch.choose(self, index: int)`. -The index represents the number in the previously returned instance of MultipleOptions. -The selected Option will be selected and can be downloaded in the next step. - -*Thus, this has to be done **after either search_from_text or search_from_query*** - -```python -# choosing the best matching band -multiple_options = search_object.choose(0) -# choosing the first ever release group of this band -multiple_options = search_object.choose(1) -# printing out the current options -print(multiple_options) -``` - -This process can be repeated indefinitely (until you run out of memory). -A search history is kept in the Search instance. You could go back to -the previous search (without any loading time) like this: - -```python -multiple_options = search_object.get_previous_options() -``` - -### Downloading Metadata / Filling up the Cache - -You can download following metadata: - - an artist (the whole discography) - - a release group - - a release - - a track/recording - -If you got an instance of `MetadataSearch`, like I elaborated [previously](#search-for-metadata), downloading every piece of metadata from the currently selected Option is really quite easy. - -```python -from music_kraken import fetch_metadata_from_search - -# this is it :) -music_kraken.fetch_metadata_from_search(search_object) -``` - -If you already know what you want to download you can skip the search instance and simply do the following. - -```python -from music_kraken import fetch_metadata - -# might change and break after I add multiple metadata sources which I will - -fetch_metadata(id_=musicbrainz_id, type=metadata_type) -``` -The option type is a string (*I'm sorry for not making it an enum I know its a bad pratice*), which can -have following values: - - 'artist' - - 'release_group' - - 'release' - - 'recording' - -**PAY ATTENTION TO TYPOS, IT'S CASE SENSITIVE** - -The musicbrainz id is just the id of the object from musicbrainz. - -After following those steps, it might take a couple seconds/minutes to execute, but then the Cache will be filled. - - -### Cache / Temporary Database - -All the data, the functions that download stuff use, can be gotten from the temporary database / cache. -The cache can be simply used like this: - -```python -music_kraken.test_db -``` - -When fetching any song data from the cache, you will get it as Song -object (music_kraken.Song). There are multiple methods -to get different sets of Songs. The names explain the methods pretty -well: - -```python -from music_kraken import cache - -# gets a single track specified by the id -cache.get_track_metadata(id: str) - -# gets a list of tracks. -cache.get_tracks_to_download() -cache.get_tracks_without_src() -cache.get_tracks_without_isrc() -cache.get_tracks_without_filepath() -``` - -The id always is a musicbrainz id and distinct for every track. - -### Setting the Target - -By default the music downloader doesn't know where to save the music file, if downloaded. To set those variables (the directory to save the file in and the filepath), it is enough to run one single command: - -```python -from music_kraken import set_target - -# adds file path, file directory and the genre to the database -set_target(genre="some test genre") -``` - -The concept of genres is too loose, to definitely say, this band exclusively plays this genre, or this song is this genre. This doesn't work manually, this will never work automatically. Thus, I've decided to just use the genre as category, to sort the artists and songs by. Most Music players support that. - -As a result of this decision you will have to pass the genre in this function. - -### Get Audio - -This is most likely the most useful and unique feature of this Project. If the cache is filled, you can get audio sources for the songs you only have the metadata, and download them. This works for most songs. I'd guess for about 97% (?) - -First of you will need a List of song objects `music_kraken.Song`. As [mentioned above](#cache--temporary-database), you could get a list like that from the cache. - -```python -# Here is an Example -from music_kraken import ( - cache, - fetch_sources, - fetch_audios -) - -# scanning pages, searching for a download and storing results -fetch_sources(cache.get_tracks_without_src()) - -# downloading all previously fetched sources to previously defined targets -fetch_audios(cache.get_tracks_to_download()) - -``` - -*Note:* -To download audio two cases have to be met: - 1. [The target](#setting-the-target) has to be set beforehand - 2. The sources have to be fetched beforehand - ---- - -## Metadata - -First the metadata has to be downloaded. The best api to do so is undeniably [Musicbrainz][mb]. This is a result of them being a website with a large Database spanning over all Genres. - -### Musicbrainz - -![Musicbrainz Data Scheme](https://wiki.musicbrainz.org/-/images/9/9e/pymb3-model-core.png) - -To fetch from [Musicbrainz][mb] we first have to know what to fetch. A good start is to get an input query, which can be just put into the MB-Api. It then returns a list of possible artists, releases and recordings. - -If the following chosen element is an artist, its discography + a couple tracks are printed, if a release is chosen, the artists + tracklist + release is outputted, If a track is chosen its artists and releases are shown. - -For now, it doesn't if the discography or tracklist is chosen. - -### Metadata to fetch - -I orient on which metadata to download on the keys in `mutagen.EasyID3`. The following I fetch and tag the MP3 with: -- title -- artist -- albumartist -- tracknumber -- albumsort can sort albums cronological -- titlesort is just set to the tracknumber to sort by track order to sort correctly -- isrc -- musicbrainz_artistid -- musicbrainz_albumid -- musicbrainz_albumartistid -- musicbrainz_albumstatus -- language -- musicbrainz_albumtype -- releasecountry -- barcode - -#### albumsort/titlesort - -Those Tags are for the musicplayer to not sort for Example the albums of a band alphabetically, but in another way. I set it just to chronological order - -#### isrc - -This is the **international standart release code**. With this a track can be identified 99% of the time, if it is known and the website has a search api for that. Obviously this will get important later. - -## Download - -Now that the metadata is downloaded and cached, download sources need to be sound, because one can't listen to metadata. Granted it would be amazing if that would be possible. - -### Musify - -The quickest source to get download links from is to my knowledge [musify](https://musify.club/). It's a Russian music downloading page, where many many songs are available to stream and to download. Due to me not wanting to stress the server to much, I abuse a handy feature nearly every page where you can search suff has. The autocomplete api for the search input. Those always are quite limited in the number of results it returns, but it is optimized to be quick. Thus with the http header `Connection` set to `keep-alive` the bottleneck definitely is not at the speed of those requests. - -For musify the endpoint is following: [https://musify.club/search/suggestions?term={title}](https://musify.club/search/suggestions?term=LornaShore) If the http headers are set correctly, then searching for example for "Lorna Shore" yields following result: - -```json -[ - { - "id":"Lorna Shore", - "label":"Lorna Shore", - "value":"Lorna Shore", - "category":"Исполнители", - "image":"https://39s.musify.club/img/68/9561484/25159224.jpg", - "url":"/artist/lorna-shore-59611" - }, - {"id":"Immortal","label":"Lorna Shore - Immortal (2020)","value":"Immortal","category":"Релизы","image":"https://39s-a.musify.club/img/70/20335517/52174338.jpg","url":"/release/lorna-shore-immortal-2020-1241300"}, - {"id":"Immortal","label":"Lorna Shore - Immortal","value":"Immortal","category":"Треки","image":"","url":"/track/lorna-shore-immortal-12475071"} -] -``` - -This is a shortened example for the response the api gives. The results are very Limited, but it is also very efficient to parse. The steps I take are: - -- call the api with the query being the track name -- parse the json response to an object -- look at how different the title and artist are on every element from the category `Треки`, translated roughly to track or release. -- If they match get the download links and cache them. - -### Youtube - -Herte the **isrc** plays a huge role. You probably know it, when you search on youtube for a song, and the music videos has a long intro or the first result is a live version. I don't want those in my music collection, only if the tracks are like this in the official release. Well how can you get around that? - -Turns out if you search for the **isrc** on youtube the results contain the music, like it is on the official release and some japanese meme videos. The tracks I wan't just have the title of the released track, so one can just compare those two. - -For searching, as well as for downloading I use the programm `youtube-dl`, which also has a programming interface for python. - -There are two bottlenecks with this approach though: -1. `youtube-dl` is just slow. Actually it has to be, to not get blocked by youtube. -2. Ofthen musicbrainz just doesn't give the isrc for some songs. - - -## Lyrics - -To get the Lyrics, I scrape them, and put those in the USLT ID3 Tags of for example mp3 files. Unfortunately some players, like the one I use, Rhythmbox don't support USLT Lyrics. So I created an Plugin for Rhythmbox. You can find it here: [https://github.com/HeIIow2/rythmbox-id3-lyrics-support](https://github.com/HeIIow2/rythmbox-id3-lyrics-support). - -### Genius - -For the lyrics source the page [https://genius.com/](https://genius.com/) is easily sufficient. It has most songs. Some songs are not present though, but that is fine, because the lyrics are optional anyways. - - -[i10]: https://github.com/HeIIow2/music-downloader/issues/10 -[i2]: https://github.com/HeIIow2/music-downloader/issues/2 -[mb]: https://musicbrainz.org/ - - diff --git a/src/music_kraken.egg-info/SOURCES.txt b/src/music_kraken.egg-info/SOURCES.txt deleted file mode 100644 index e0f3960..0000000 --- a/src/music_kraken.egg-info/SOURCES.txt +++ /dev/null @@ -1,46 +0,0 @@ -LICENSE -README.md -pyproject.toml -requirements.txt -setup.py -version -assets/database_structure.sql -assets/temp_database_structure.sql -src/music_kraken/__init__.py -src/music_kraken/__main__.py -src/music_kraken.egg-info/PKG-INFO -src/music_kraken.egg-info/SOURCES.txt -src/music_kraken.egg-info/dependency_links.txt -src/music_kraken.egg-info/entry_points.txt -src/music_kraken.egg-info/requires.txt -src/music_kraken.egg-info/top_level.txt -src/music_kraken/database/__init__.py -src/music_kraken/database/data_models.py -src/music_kraken/database/database.py -src/music_kraken/database/object_cache.py -src/music_kraken/database/old_database.py -src/music_kraken/database/read.py -src/music_kraken/database/temp_database.py -src/music_kraken/database/write.py -src/music_kraken/lyrics/__init__.py -src/music_kraken/metadata/__init__.py -src/music_kraken/not_used_anymore/__init__.py -src/music_kraken/not_used_anymore/fetch_audio.py -src/music_kraken/not_used_anymore/fetch_source.py -src/music_kraken/not_used_anymore/sources/__init__.py -src/music_kraken/not_used_anymore/sources/genius.py -src/music_kraken/not_used_anymore/sources/local_files.py -src/music_kraken/not_used_anymore/sources/musify.py -src/music_kraken/not_used_anymore/sources/source.py -src/music_kraken/not_used_anymore/sources/youtube.py -src/music_kraken/static_files/temp_database_structure.sql -src/music_kraken/tagging/__init__.py -src/music_kraken/tagging/id3.py -src/music_kraken/target/__init__.py -src/music_kraken/target/set_target.py -src/music_kraken/utils/__init__.py -src/music_kraken/utils/functions.py -src/music_kraken/utils/object_handeling.py -src/music_kraken/utils/phonetic_compares.py -src/music_kraken/utils/shared.py -src/music_kraken/utils/string_processing.py \ No newline at end of file diff --git a/src/music_kraken.egg-info/dependency_links.txt b/src/music_kraken.egg-info/dependency_links.txt deleted file mode 100644 index 8b13789..0000000 --- a/src/music_kraken.egg-info/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/music_kraken.egg-info/entry_points.txt b/src/music_kraken.egg-info/entry_points.txt deleted file mode 100644 index 6d8a32d..0000000 --- a/src/music_kraken.egg-info/entry_points.txt +++ /dev/null @@ -1,3 +0,0 @@ -[console_scripts] -music-kraken = music_kraken:cli - diff --git a/src/music_kraken.egg-info/requires.txt b/src/music_kraken.egg-info/requires.txt deleted file mode 100644 index ffbd9a1..0000000 --- a/src/music_kraken.egg-info/requires.txt +++ /dev/null @@ -1,12 +0,0 @@ -requests~=2.28.1 -mutagen~=1.46.0 -musicbrainzngs~=0.7.1 -jellyfish~=0.9.0 -pydub~=0.25.1 -youtube_dl -beautifulsoup4~=4.11.1 -pycountry~=22.3.5 -python-dateutil~=2.8.2 -pandoc~=2.3 -SQLAlchemy -setuptools~=60.2.0 diff --git a/src/music_kraken.egg-info/top_level.txt b/src/music_kraken.egg-info/top_level.txt deleted file mode 100644 index 3f22a76..0000000 --- a/src/music_kraken.egg-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -music_kraken diff --git a/src/music_kraken/__init__.py b/src/music_kraken/__init__.py deleted file mode 100644 index acf551f..0000000 --- a/src/music_kraken/__init__.py +++ /dev/null @@ -1,35 +0,0 @@ -import logging - -import gc -import musicbrainzngs - -from .utils.shared import DEBUG -from .utils.config import logging_settings, main_settings, read_config -read_config() -from . import cli - - -# configure logger default -logging.basicConfig( - level=logging_settings['log_level'] if not DEBUG else logging.DEBUG, - format=logging_settings['logging_format'], - handlers=[ - logging.FileHandler(main_settings['log_file']), - logging.StreamHandler() - ] -) - -if main_settings['modify_gc']: - """ - At the start I modify the garbage collector to run a bit fewer times. - This should increase speed: - https://mkennedy.codes/posts/python-gc-settings-change-this-and-make-your-app-go-20pc-faster/ - """ - # Clean up what might be garbage so far. - gc.collect(2) - - allocs, gen1, gen2 = gc.get_threshold() - allocs = 50_000 # Start the GC sequence every 50K not 700 allocations. - gen1 = gen1 * 2 - gen2 = gen2 * 2 - gc.set_threshold(allocs, gen1, gen2) diff --git a/src/music_kraken/database/data_models.py b/src/music_kraken/database/data_models.py deleted file mode 100644 index 7deff35..0000000 --- a/src/music_kraken/database/data_models.py +++ /dev/null @@ -1,197 +0,0 @@ -from typing import List, Union, Type, Optional -from peewee import ( - SqliteDatabase, - PostgresqlDatabase, - MySQLDatabase, - Model, - CharField, - IntegerField, - BooleanField, - ForeignKeyField, - TextField -) - -""" -**IMPORTANT**: - -never delete, modify the datatype or add constrains to ANY existing collumns, -between the versions, that gets pushed out to the users. -Else my function can't update legacy databases, to new databases, -while keeping the data of the old ones. - -EVEN if that means to for example keep decimal values stored in strings. -(not in my codebase though.) -""" - - -class BaseModel(Model): - notes: str = CharField(null=True) - - class Meta: - database = None - - @classmethod - def Use(cls, database: Union[SqliteDatabase, PostgresqlDatabase, MySQLDatabase]) -> Model: - cls._meta.database = database - return cls - - def use(self, database: Union[SqliteDatabase, PostgresqlDatabase, MySQLDatabase]) -> Model: - self._meta.database = database - return self - -class ObjectModel(BaseModel): - id: str = CharField(primary_key=True) - -class MainModel(BaseModel): - additional_arguments: str = CharField(null=True) - notes: str = CharField(null=True) - - -class Song(MainModel): - """A class representing a song in the music database.""" - - title: str = CharField(null=True) - isrc: str = CharField(null=True) - length: int = IntegerField(null=True) - tracksort: int = IntegerField(null=True) - genre: str = CharField(null=True) - - -class Album(MainModel): - """A class representing an album in the music database.""" - - title: str = CharField(null=True) - album_status: str = CharField(null=True) - album_type: str = CharField(null=True) - language: str = CharField(null=True) - date_string: str = CharField(null=True) - date_format: str = CharField(null=True) - barcode: str = CharField(null=True) - albumsort: int = IntegerField(null=True) - - -class Artist(MainModel): - """A class representing an artist in the music database.""" - - name: str = CharField(null=True) - country: str = CharField(null=True) - formed_in_date: str = CharField(null=True) - formed_in_format: str = CharField(null=True) - general_genre: str = CharField(null=True) - - -class Label(MainModel): - name: str = CharField(null=True) - - -class Target(ObjectModel): - """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(ObjectModel): - """A class representing lyrics of a song in the music database.""" - - text: str = TextField() - language: str = CharField() - song = ForeignKeyField(Song, backref='lyrics') - - -class Source(BaseModel): - """A class representing a source of a song in the music database.""" - ContentTypes = Union[Song, Album, Artist, Lyrics] - - page: str = CharField() - url: str = CharField() - - content_type: str = CharField() - content_id: int = CharField() - # content: ForeignKeyField = ForeignKeyField('self', backref='content_items', null=True) - - @property - def content_object(self) -> Union[Song, Album, Artist]: - """Get the content associated with the source as an object.""" - if self.content_type == 'Song': - return Song.get(Song.id == self.content_id) - if self.content_type == 'Album': - return Album.get(Album.id == self.content_id) - if self.content_type == 'Artist': - return Artist.get(Artist.id == self.content_id) - if self.content_type == 'Label': - return Label.get(Label.id == self.content_id) - if self.content_type == 'Lyrics': - return Lyrics.get(Lyrics.id == self.content_id) - - - @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 SongArtist(BaseModel): - """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 ArtistAlbum(BaseModel): - """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 AlbumSong(BaseModel): - """A class representing the relationship between an album and an song.""" - album: ForeignKeyField = ForeignKeyField(Album, backref='album_artists') - song: ForeignKeyField = ForeignKeyField(Song, backref='album_artists') - - -class LabelAlbum(BaseModel): - label: ForeignKeyField = ForeignKeyField(Label, backref='label_album') - album: ForeignKeyField = ForeignKeyField(Album, backref='label_album') - - -class LabelArtist(BaseModel): - label: ForeignKeyField = ForeignKeyField(Label, backref='label_artist') - artist: ForeignKeyField = ForeignKeyField(Artist, backref='label_artists') - - -ALL_MODELS = [ - Song, - Album, - Artist, - Source, - Lyrics, - ArtistAlbum, - Target, - SongArtist -] - -if __name__ == "__main__": - database_1 = SqliteDatabase(":memory:") - database_1.create_tables([Song.Use(database_1)]) - database_2 = SqliteDatabase(":memory:") - database_2.create_tables([Song.Use(database_2)]) - - # creating songs, adding it to db_2 if i is even, else to db_1 - for i in range(100): - song = Song(name=str(i) + "hs") - - db_to_use = database_2 if i % 2 == 0 else database_1 - song.use(db_to_use).save() - - print("database 1") - for song in Song.Use(database_1).select(): - print(song.name) - - print("database 2") - for song in Song.Use(database_1).select(): - print(song.name) diff --git a/src/music_kraken/database/database.py b/src/music_kraken/database/database.py deleted file mode 100644 index 0120a89..0000000 --- a/src/music_kraken/database/database.py +++ /dev/null @@ -1,188 +0,0 @@ -# Standard library -from typing import Optional, Union, List -from enum import Enum -from playhouse.migrate import * - -# third party modules -from peewee import ( - SqliteDatabase, - MySQLDatabase, - PostgresqlDatabase, -) - -# own modules -from . import ( - data_models, - write -) -from .. import objects - - -class DatabaseType(Enum): - SQLITE = "sqlite" - POSTGRESQL = "postgresql" - MYSQL = "mysql" - -class Database: - database: Union[SqliteDatabase, PostgresqlDatabase, MySQLDatabase] - - 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 - - self.initialize_database() - - def create_database(self) -> Union[SqliteDatabase, PostgresqlDatabase, MySQLDatabase]: - """Create a database instance based on the configured database type and parameters. - - Returns: - The created database instance, or None if an invalid database type was specified. - """ - - # 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, - ) - - # 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, - ) - - raise ValueError("Invalid database type specified.") - - - @property - def migrator(self) -> SchemaMigrator: - if self.db_type == DatabaseType.SQLITE: - return SqliteMigrator(self.database) - - if self.db_type == DatabaseType.MYSQL: - return MySQLMigrator(self.database) - - if self.db_type == DatabaseType.POSTGRESQL: - return PostgresqlMigrator(self.database) - - def initialize_database(self): - """ - Connect to the database - initialize the previously defined databases - create tables if they don't exist. - """ - self.database = self.create_database() - self.database.connect() - - migrator = self.migrator - - for model in data_models.ALL_MODELS: - model = model.Use(self.database) - - if self.database.table_exists(model): - migration_operations = [ - migrator.add_column( - "some field", field[0], field[1] - ) - for field in model._meta.fields.items() - ] - - migrate(*migration_operations) - else: - self.database.create_tables([model], safe=True) - - #self.database.create_tables([model.Use(self.database) for model in data_models.ALL_MODELS], safe=True) - - """ - upgrade old databases. - If a collumn has been added in a new version this adds it to old Tables, - without deleting the data in legacy databases - """ - - for model in data_models.ALL_MODELS: - model = model.Use(self.database) - - - - print(model._meta.fields) - - def push(self, database_object: objects.DatabaseObject): - """ - Adds a new music object to the database using the corresponding method from the `write` session. - When possible, rather use the `push_many` function. - This gets even more important, when using a remote database server. - - Args: - database_object (objects.MusicObject): The music object to add to the database. - - Returns: - The newly added music object. - """ - - with write.WritingSession(self.database) as writing_session: - if isinstance(database_object, objects.Song): - return writing_session.add_song(database_object) - - if isinstance(database_object, objects.Album): - return writing_session.add_album(database_object) - - if isinstance(database_object, objects.Artist): - return writing_session.add_artist(database_object) - - def push_many(self, database_objects: List[objects.DatabaseObject]) -> None: - """ - Adds a list of MusicObject instances to the database. - This function sends only needs one querry for each type of table added. - Beware that if you have for example an object like this: - - Album - - Song - - Song - you already have 3 different Tables. - - Unlike the function `push`, this function doesn't return the added database objects. - - Args: - database_objects: List of MusicObject instances to be added to the database. - """ - - with write.WritingSession(self.database) as writing_session: - for obj in database_objects: - if isinstance(obj, objects.Song): - writing_session.add_song(obj) - continue - - if isinstance(obj, objects.Album): - writing_session.add_album(obj) - continue - - if isinstance(obj, objects.Artist): - writing_session.add_artist(obj) - continue - - - def __del__(self): - self.database.close() diff --git a/src/music_kraken/objects/collection.py b/src/music_kraken/objects/collection.py deleted file mode 100644 index 2b9ce72..0000000 --- a/src/music_kraken/objects/collection.py +++ /dev/null @@ -1,166 +0,0 @@ -from typing import List, Iterable, Dict, TypeVar, Generic, Iterator -from collections import defaultdict -from dataclasses import dataclass - -from .parents import DatabaseObject - - -T = TypeVar('T', bound=DatabaseObject) - - -@dataclass -class AppendResult: - was_in_collection: bool - current_element: DatabaseObject - was_the_same: bool - - -class Collection(Generic[T]): - """ - This a class for the iterables - like tracklist or discography - """ - _data: List[T] - - _by_url: dict - _by_attribute: dict - - def __init__(self, data: List[T] = None, element_type=None, *args, **kwargs) -> None: - # Attribute needs to point to - self.element_type = element_type - - self._data: List[T] = list() - - """ - example of attribute_to_object_map - the song objects are references pointing to objects - in _data - - ```python - { - 'id': {323: song_1, 563: song_2, 666: song_3}, - 'url': {'www.song_2.com': song_2} - } - ``` - """ - self._attribute_to_object_map: Dict[str, Dict[object, T]] = defaultdict(dict) - self._used_ids: set = set() - - if data is not None: - self.extend(data, merge_on_conflict=True) - - def sort(self, reverse: bool = False, **kwargs): - self._data.sort(reverse=reverse, **kwargs) - - def map_element(self, element: T): - for name, value in element.indexing_values: - if value is None: - continue - - self._attribute_to_object_map[name][value] = element - - self._used_ids.add(element.id) - - def unmap_element(self, element: T): - for name, value in element.indexing_values: - if value is None: - continue - - if value in self._attribute_to_object_map[name]: - if element is self._attribute_to_object_map[name][value]: - try: - self._attribute_to_object_map[name].pop(value) - except KeyError: - pass - - def append(self, element: T, merge_on_conflict: bool = True, - merge_into_existing: bool = True) -> AppendResult: - """ - :param element: - :param merge_on_conflict: - :param merge_into_existing: - :return did_not_exist: - """ - - # if the element type has been defined in the initializer it checks if the type matches - if self.element_type is not None and not isinstance(element, self.element_type): - raise TypeError(f"{type(element)} is not the set type {self.element_type}") - - # return if the same instance of the object is in the list - for existing in self._data: - if element is existing: - return AppendResult(True, element, True) - - for name, value in element.indexing_values: - if value in self._attribute_to_object_map[name]: - existing_object = self._attribute_to_object_map[name][value] - - if not merge_on_conflict: - return AppendResult(True, existing_object, False) - - # if the object does already exist - # thus merging and don't add it afterward - if merge_into_existing: - existing_object.merge(element) - # in case any relevant data has been added (e.g. it remaps the old object) - self.map_element(existing_object) - return AppendResult(True, existing_object, False) - - element.merge(existing_object) - - exists_at = self._data.index(existing_object) - self._data[exists_at] = element - - self.unmap_element(existing_object) - self.map_element(element) - return AppendResult(True, existing_object, False) - - self._data.append(element) - self.map_element(element) - - return AppendResult(False, element, False) - - def extend(self, element_list: Iterable[T], merge_on_conflict: bool = True, - merge_into_existing: bool = True): - for element in element_list: - self.append(element, merge_on_conflict=merge_on_conflict, merge_into_existing=merge_into_existing) - - def __iter__(self) -> Iterator[T]: - for element in self.shallow_list: - yield element - - def __str__(self) -> str: - return "\n".join([f"{str(j).zfill(2)}: {i.__repr__()}" for j, i in enumerate(self._data)]) - - def __len__(self) -> int: - return len(self._data) - - def __getitem__(self, key) -> T: - if type(key) != int: - return ValueError("key needs to be an integer") - - return self._data[key] - - def __setitem__(self, key, value: T): - if type(key) != int: - return ValueError("key needs to be an integer") - - old_item = self._data[key] - self.unmap_element(old_item) - self.map_element(value) - - self._data[key] = value - - @property - def shallow_list(self) -> List[T]: - """ - returns a shallow copy of the data list - """ - return self._data.copy() - - @property - def empty(self) -> bool: - return len(self._data) == 0 - - def clear(self): - self.__init__(element_type=self.element_type) diff --git a/src/music_kraken/objects/formatted_text.py b/src/music_kraken/objects/formatted_text.py deleted file mode 100644 index 20927a3..0000000 --- a/src/music_kraken/objects/formatted_text.py +++ /dev/null @@ -1,78 +0,0 @@ -import pandoc - -""" -TODO -implement in setup.py a skript to install pandocs -https://pandoc.org/installing.html - -!!!!!!!!!!!!!!!!!!IMPORTANT!!!!!!!!!!!!!!!!!! -""" - - -class FormattedText: - """ - the self.html value should be saved to the database - """ - - doc = None - - def __init__( - self, - plaintext: str = None, - markdown: str = None, - html: str = None - ) -> None: - self.set_plaintext(plaintext) - self.set_markdown(markdown) - self.set_html(html) - - def set_plaintext(self, plaintext: str): - if plaintext is None: - return - self.doc = pandoc.read(plaintext) - - def set_markdown(self, markdown: str): - if markdown is None: - return - self.doc = pandoc.read(markdown, format="markdown") - - def set_html(self, html: str): - if html is None: - return - self.doc = pandoc.read(html, format="html") - - def get_markdown(self) -> str: - if self.doc is None: - return "" - return pandoc.write(self.doc, format="markdown").strip() - - def get_html(self) -> str: - if self.doc is None: - return "" - return pandoc.write(self.doc, format="html").strip() - - def get_plaintext(self) -> str: - if self.doc is None: - return "" - return pandoc.write(self.doc, format="plain").strip() - - @property - def is_empty(self) -> bool: - return self.doc is None - - def __eq__(self, other) -> False: - if type(other) != type(self): - return False - if self.is_empty and other.is_empty: - return True - - return self.doc == other.doc - - def __str__(self) -> str: - return self.plaintext - - - - plaintext = property(fget=get_plaintext, fset=set_plaintext) - markdown = property(fget=get_markdown, fset=set_markdown) - html = property(fget=get_html, fset=set_html) diff --git a/src/music_kraken/objects/lyrics.py b/src/music_kraken/objects/lyrics.py deleted file mode 100644 index 465b96d..0000000 --- a/src/music_kraken/objects/lyrics.py +++ /dev/null @@ -1,31 +0,0 @@ -from typing import List -from collections import defaultdict -import pycountry - -from .parents import DatabaseObject -from .source import Source, SourceCollection -from .formatted_text import FormattedText - - -class Lyrics(DatabaseObject): - COLLECTION_ATTRIBUTES = ("source_collection",) - SIMPLE_ATTRIBUTES = { - "text": FormattedText(), - "language": None - } - - def __init__( - self, - text: FormattedText, - language: pycountry.Languages = pycountry.languages.get(alpha_2="en"), - _id: str = None, - dynamic: bool = False, - source_list: List[Source] = None, - **kwargs - ) -> None: - DatabaseObject.__init__(self, _id=_id, dynamic=dynamic, **kwargs) - - self.text: FormattedText = text or FormattedText() - self.language: pycountry.Languages = language - - self.source_collection: SourceCollection = SourceCollection(source_list) diff --git a/src/music_kraken/objects/parents.py b/src/music_kraken/objects/parents.py deleted file mode 100644 index a553700..0000000 --- a/src/music_kraken/objects/parents.py +++ /dev/null @@ -1,160 +0,0 @@ -import random -from collections import defaultdict -from typing import Optional, Dict, Tuple, List, Type - -from .metadata import Metadata -from .option import Options -from ..utils.shared import HIGHEST_ID -from ..utils.config import main_settings, logging_settings - - -LOGGER = logging_settings["object_logger"] - - -class DatabaseObject: - COLLECTION_ATTRIBUTES: tuple = tuple() - SIMPLE_ATTRIBUTES: dict = dict() - - # contains all collection attributes, which describe something "smaller" - # e.g. album has songs, but not artist. - DOWNWARDS_COLLECTION_ATTRIBUTES: tuple = tuple() - UPWARDS_COLLECTION_ATTRIBUTES: tuple = tuple() - - def __init__(self, _id: int = None, dynamic: bool = False, **kwargs) -> None: - self.automatic_id: bool = False - - if _id is None and not dynamic: - """ - generates a random integer id - 64 bit integer, but this is defined in shared.py in ID_BITS - the range is defined in the Tuple ID_RANGE - """ - _id = random.randint(0, HIGHEST_ID) - self.automatic_id = True - LOGGER.debug(f"Id for {type(self).__name__} isn't set. Setting to {_id}") - - # The id can only be None, if the object is dynamic (self.dynamic = True) - self.id: Optional[int] = _id - - self.dynamic = dynamic - - self.build_version = -1 - - def __hash__(self): - if self.dynamic: - raise TypeError("Dynamic DatabaseObjects are unhashable.") - return self.id - - def __eq__(self, other) -> bool: - if not isinstance(other, type(self)): - return False - - # add the checks for dynamic, to not throw an exception - if not self.dynamic and not other.dynamic and self.id == other.id: - return True - - temp_attribute_map: Dict[str, set] = defaultdict(set) - - # building map with sets - for name, value in self.indexing_values: - temp_attribute_map[name].add(value) - - # check against the attributes of the other object - for name, other_value in other.indexing_values: - if other_value in temp_attribute_map[name]: - return True - - return False - - @property - def indexing_values(self) -> List[Tuple[str, object]]: - """ - returns a map of the name and values of the attributes. - This helps in comparing classes for equal data (eg. being the same song but different attributes) - - Returns: - List[Tuple[str, object]]: the first element in the tuple is the name of the attribute, the second the value. - """ - - return list() - - def merge(self, other, override: bool = False): - if other is None: - return - - if self is other: - return - - if not isinstance(other, type(self)): - LOGGER.warning(f"can't merge \"{type(other)}\" into \"{type(self)}\"") - return - - for collection in type(self).COLLECTION_ATTRIBUTES: - getattr(self, collection).extend(getattr(other, collection)) - - for simple_attribute, default_value in type(self).SIMPLE_ATTRIBUTES.items(): - if getattr(other, simple_attribute) == default_value: - continue - - if override or getattr(self, simple_attribute) == default_value: - setattr(self, simple_attribute, getattr(other, simple_attribute)) - - def strip_details(self): - for collection in type(self).DOWNWARDS_COLLECTION_ATTRIBUTES: - getattr(self, collection).clear() - - @property - def metadata(self) -> Metadata: - return Metadata() - - @property - def options(self) -> List["DatabaseObject"]: - return [self] - - @property - def option_string(self) -> str: - return self.__repr__() - - def _build_recursive_structures(self, build_version: int, merge: False): - pass - - def compile(self, merge_into: bool = False): - """ - compiles the recursive structures, - and does depending on the object some other stuff. - - no need to override if only the recursive structure should be build. - override self.build_recursive_structures() instead - """ - - self._build_recursive_structures(build_version=random.randint(0, 99999), merge=merge_into) - - def _add_other_db_objects(self, object_type: Type["DatabaseObject"], object_list: List["DatabaseObject"]): - pass - - def add_list_of_other_objects(self, object_list: List["DatabaseObject"]): - d: Dict[Type[DatabaseObject], List[DatabaseObject]] = defaultdict(list) - - for db_object in object_list: - d[type(db_object)].append(db_object) - - for key, value in d.items(): - self._add_other_db_objects(key, value) - - -class MainObject(DatabaseObject): - """ - This is the parent class for all "main" data objects: - - Song - - Album - - Artist - - Label - - It has all the functionality of the "DatabaseObject" (it inherits from said class) - but also some added functions as well. - """ - - def __init__(self, _id: int = None, dynamic: bool = False, **kwargs): - DatabaseObject.__init__(self, _id=_id, dynamic=dynamic, **kwargs) - - self.additional_arguments: dict = kwargs diff --git a/src/music_kraken/objects/song.py b/src/music_kraken/objects/song.py deleted file mode 100644 index 3f46d4b..0000000 --- a/src/music_kraken/objects/song.py +++ /dev/null @@ -1,780 +0,0 @@ -import random -from collections import defaultdict -from typing import List, Optional, Dict, Tuple, Type - -import pycountry - -from ..utils.enums.album import AlbumType, AlbumStatus -from .collection import Collection -from .formatted_text import FormattedText -from .lyrics import Lyrics -from .metadata import ( - Mapping as id3Mapping, - ID3Timestamp, - Metadata -) -from .option import Options -from .parents import MainObject, DatabaseObject -from .source import Source, SourceCollection -from .target import Target -from ..utils.string_processing import unify - -from ..utils.config import main_settings - -""" -All Objects dependent -""" - -CountryTyping = type(list(pycountry.countries)[0]) -OPTION_STRING_DELIMITER = " | " - - -class Song(MainObject): - """ - Class representing a song object, with attributes id, mb_id, title, album_name, isrc, length, - tracksort, genre, source_list, target, lyrics_list, album, main_artist_list, and feature_artist_list. - """ - - COLLECTION_ATTRIBUTES = ( - "lyrics_collection", "album_collection", "main_artist_collection", "feature_artist_collection", - "source_collection") - SIMPLE_ATTRIBUTES = { - "title": None, - "unified_title": None, - "isrc": None, - "length": None, - "tracksort": 0, - "genre": None, - "notes": FormattedText() - } - - UPWARDS_COLLECTION_ATTRIBUTES = ("album_collection", "main_artist_collection", "feature_artist_collection") - - def __init__( - self, - _id: int = None, - dynamic: bool = False, - title: str = None, - unified_title: str = None, - isrc: str = None, - length: int = None, - tracksort: int = None, - genre: str = None, - source_list: List[Source] = None, - target_list: List[Target] = None, - lyrics_list: List[Lyrics] = None, - album_list: List['Album'] = None, - main_artist_list: List['Artist'] = None, - feature_artist_list: List['Artist'] = None, - notes: FormattedText = None, - **kwargs - ) -> None: - MainObject.__init__(self, _id=_id, dynamic=dynamic, **kwargs) - # attributes - self.title: str = title - self.unified_title: str = unified_title - if unified_title is None and title is not None: - self.unified_title = unify(title) - - self.isrc: str = isrc - self.length: int = length - self.tracksort: int = tracksort or 0 - self.genre: str = genre - self.notes: FormattedText = notes or FormattedText() - - self.source_collection: SourceCollection = SourceCollection(source_list) - self.target_collection: Collection[Target] = Collection(data=target_list, element_type=Target) - self.lyrics_collection: Collection[Lyrics] = Collection(data=lyrics_list, element_type=Lyrics) - self.album_collection: Collection[Album] = Collection(data=album_list, element_type=Album) - self.main_artist_collection: Collection[Artist] = Collection(data=main_artist_list, element_type=Artist) - self.feature_artist_collection: Collection[Artist] = Collection(data=feature_artist_list, element_type=Artist) - - def _build_recursive_structures(self, build_version: int, merge: bool): - if build_version == self.build_version: - return - self.build_version = build_version - - album: Album - for album in self.album_collection: - album.song_collection.append(self, merge_on_conflict=merge, merge_into_existing=False) - album._build_recursive_structures(build_version=build_version, merge=merge) - - artist: Artist - for artist in self.feature_artist_collection: - artist.feature_song_collection.append(self, merge_on_conflict=merge, merge_into_existing=False) - artist._build_recursive_structures(build_version=build_version, merge=merge) - - for artist in self.main_artist_collection: - for album in self.album_collection: - artist.main_album_collection.append(album, merge_on_conflict=merge, merge_into_existing=False) - artist._build_recursive_structures(build_version=build_version, merge=merge) - - def _add_other_db_objects(self, object_type: Type["DatabaseObject"], object_list: List["DatabaseObject"]): - if object_type is Song: - return - - if object_type is Lyrics: - self.lyrics_collection.extend(object_list) - return - - if object_type is Artist: - self.main_artist_collection.extend(object_list) - return - - if object_type is Album: - self.album_collection.extend(object_list) - return - - - @property - def indexing_values(self) -> List[Tuple[str, object]]: - return [ - ('id', self.id), - ('title', self.unified_title), - ('isrc', self.isrc), - *[('url', source.url) for source in self.source_collection] - ] - - @property - def metadata(self) -> Metadata: - metadata = Metadata({ - id3Mapping.TITLE: [self.title], - id3Mapping.ISRC: [self.isrc], - id3Mapping.LENGTH: [self.length], - id3Mapping.GENRE: [self.genre], - id3Mapping.TRACKNUMBER: [self.tracksort_str] - }) - - # metadata.merge_many([s.get_song_metadata() for s in self.source_collection]) album sources have no relevant metadata for id3 - metadata.merge_many([a.metadata for a in self.album_collection]) - metadata.merge_many([a.metadata for a in self.main_artist_collection]) - metadata.merge_many([a.metadata for a in self.feature_artist_collection]) - metadata.merge_many([lyrics.metadata for lyrics in self.lyrics_collection]) - - return metadata - - def get_artist_credits(self) -> str: - main_artists = ", ".join([artist.name for artist in self.main_artist_collection]) - feature_artists = ", ".join([artist.name for artist in self.feature_artist_collection]) - - if len(feature_artists) == 0: - return main_artists - return f"{main_artists} feat. {feature_artists}" - - def __str__(self) -> str: - artist_credit_str = "" - artist_credits = self.get_artist_credits() - if artist_credits != "": - artist_credit_str = f" by {artist_credits}" - - return f"\"{self.title}\"{artist_credit_str}" - - def __repr__(self) -> str: - return f"Song(\"{self.title}\")" - - @property - def option_string(self) -> str: - return f"{self.__repr__()} " \ - f"from Album({OPTION_STRING_DELIMITER.join(album.title for album in self.album_collection)}) " \ - f"by Artist({OPTION_STRING_DELIMITER.join(artist.name for artist in self.main_artist_collection)}) " \ - f"feat. Artist({OPTION_STRING_DELIMITER.join(artist.name for artist in self.feature_artist_collection)})" - - @property - def options(self) -> List[DatabaseObject]: - """ - Return a list of related objects including the song object, album object, main artist objects, and - feature artist objects. - - :return: a list of objects that are related to the Song object - """ - options = self.main_artist_collection.shallow_list - options.extend(self.feature_artist_collection) - options.extend(self.album_collection) - options.append(self) - return options - - @property - def tracksort_str(self) -> str: - """ - if the album tracklist is empty, it sets it length to 1, this song has to be on the Album - :returns id3_tracksort: {song_position}/{album.length_of_tracklist} - """ - if len(self.album_collection) == 0: - return f"{self.tracksort}" - - return f"{self.tracksort}/{len(self.album_collection[0].song_collection) or 1}" - - -""" -All objects dependent on Album -""" - - -class Album(MainObject): - COLLECTION_ATTRIBUTES = ("label_collection", "artist_collection", "song_collection") - SIMPLE_ATTRIBUTES = { - "title": None, - "unified_title": None, - "album_status": None, - "album_type": AlbumType.OTHER, - "language": None, - "date": ID3Timestamp(), - "barcode": None, - "albumsort": None, - "notes": FormattedText() - } - - DOWNWARDS_COLLECTION_ATTRIBUTES = ("song_collection", ) - UPWARDS_COLLECTION_ATTRIBUTES = ("artist_collection", "label_collection") - - def __init__( - self, - _id: int = None, - title: str = None, - unified_title: str = None, - language: pycountry.Languages = None, - date: ID3Timestamp = None, - barcode: str = None, - albumsort: int = None, - dynamic: bool = False, - source_list: List[Source] = None, - artist_list: List['Artist'] = None, - song_list: List[Song] = None, - album_status: AlbumStatus = None, - album_type: AlbumType = None, - label_list: List['Label'] = None, - notes: FormattedText = None, - **kwargs - ) -> None: - MainObject.__init__(self, _id=_id, dynamic=dynamic, **kwargs) - - self.title: str = title - self.unified_title: str = unified_title - if unified_title is None and title is not None: - self.unified_title = unify(title) - - self.album_status: AlbumStatus = album_status - self.album_type: AlbumType = album_type or AlbumType.OTHER - self.language: pycountry.Languages = language - self.date: ID3Timestamp = date or ID3Timestamp() - - """ - TODO - find out the id3 tag for barcode and implement it - maybe look at how mutagen does it with easy_id3 - """ - self.barcode: str = barcode - """ - TODO - implement a function in the Artist class, - to set albumsort with help of the release year - """ - self.albumsort: Optional[int] = albumsort - self.notes = notes or FormattedText() - - self.source_collection: SourceCollection = SourceCollection(source_list) - self.song_collection: Collection[Song] = Collection(data=song_list, element_type=Song) - self.artist_collection: Collection[Artist] = Collection(data=artist_list, element_type=Artist) - self.label_collection: Collection[Label] = Collection(data=label_list, element_type=Label) - - def _build_recursive_structures(self, build_version: int, merge: bool): - if build_version == self.build_version: - return - self.build_version = build_version - - song: Song - for song in self.song_collection: - song.album_collection.append(self, merge_on_conflict=merge, merge_into_existing=False) - song._build_recursive_structures(build_version=build_version, merge=merge) - - artist: Artist - for artist in self.artist_collection: - artist.main_album_collection.append(self, merge_on_conflict=merge, merge_into_existing=False) - artist._build_recursive_structures(build_version=build_version, merge=merge) - - label: Label - for label in self.label_collection: - label.album_collection.append(self, merge_on_conflict=merge, merge_into_existing=False) - label._build_recursive_structures(build_version=build_version, merge=merge) - - def _add_other_db_objects(self, object_type: Type["DatabaseObject"], object_list: List["DatabaseObject"]): - if object_type is Song: - self.song_collection.extend(object_list) - return - - if object_type is Artist: - self.artist_collection.extend(object_list) - return - - if object_type is Album: - return - - if object_type is Label: - self.label_collection.extend(object_list) - return - - @property - def indexing_values(self) -> List[Tuple[str, object]]: - return [ - ('id', self.id), - ('title', self.unified_title), - ('barcode', self.barcode), - *[('url', source.url) for source in self.source_collection] - ] - - @property - def metadata(self) -> Metadata: - return Metadata({ - id3Mapping.ALBUM: [self.title], - id3Mapping.COPYRIGHT: [self.copyright], - id3Mapping.LANGUAGE: [self.iso_639_2_lang], - id3Mapping.ALBUM_ARTIST: [a.name for a in self.artist_collection], - id3Mapping.DATE: [self.date.strftime("%d%m")] if self.date.has_year and self.date.has_month else [], - id3Mapping.TIME: [self.date.strftime(("%H%M"))] if self.date.has_hour and self.date.has_minute else [], - id3Mapping.YEAR: [str(self.date.year).zfill(4)] if self.date.has_year else [], - id3Mapping.RELEASE_DATE: [self.date.timestamp], - id3Mapping.ORIGINAL_RELEASE_DATE: [self.date.timestamp], - id3Mapping.ALBUMSORTORDER: [str(self.albumsort)] if self.albumsort is not None else [] - }) - - def __repr__(self): - return f"Album(\"{self.title}\")" - - @property - def option_string(self) -> str: - return f"{self.__repr__()} " \ - f"by Artist({OPTION_STRING_DELIMITER.join([artist.name for artist in self.artist_collection])}) " \ - f"under Label({OPTION_STRING_DELIMITER.join([label.name for label in self.label_collection])})" - - @property - def options(self) -> List[DatabaseObject]: - options = self.artist_collection.shallow_list - options.append(self) - options.extend(self.song_collection) - - return options - - def update_tracksort(self): - """ - This updates the tracksort attributes, of the songs in - `self.song_collection`, and sorts the songs, if possible. - - It is advised to only call this function, once all the tracks are - added to the songs. - - :return: - """ - - if self.song_collection.empty: - return - - tracksort_map: Dict[int, Song] = { - song.tracksort: song for song in self.song_collection if song.tracksort is not None - } - - # place the songs, with set tracksort attribute according to it - for tracksort, song in tracksort_map.items(): - index = tracksort - 1 - - """ - I ONLY modify the `Collection._data` attribute directly, - to bypass the mapping of the attributes, because I will add the item in the next step - """ - - """ - but for some reason, neither - `self.song_collection._data.index(song)` - `self.song_collection._data.remove(song)` - get the right object. - - I have NO FUCKING CLUE why xD - But I just implemented it myself. - """ - for old_index, temp_song in enumerate(self.song_collection._data): - if song is temp_song: - break - - # the list can't be empty - del self.song_collection._data[old_index] - self.song_collection._data.insert(index, song) - - # fill in the empty tracksort attributes - for i, song in enumerate(self.song_collection): - if song.tracksort is not None: - continue - song.tracksort = i + 1 - - def compile(self, merge_into: bool = False): - """ - compiles the recursive structures, - and does depending on the object some other stuff. - - no need to override if only the recursive structure should be built. - override self.build_recursive_structures() instead - """ - - self.update_tracksort() - self._build_recursive_structures(build_version=random.randint(0, 99999), merge=merge_into) - - @property - def copyright(self) -> str: - if self.date is None: - return "" - if self.date.has_year or len(self.label_collection) == 0: - return "" - - return f"{self.date.year} {self.label_collection[0].name}" - - @property - def iso_639_2_lang(self) -> Optional[str]: - if self.language is None: - return None - - return self.language.alpha_3 - - @property - def is_split(self) -> bool: - """ - A split Album is an Album from more than one Artists - usually half the songs are made by one Artist, the other half by the other one. - In this case split means either that or one artist featured by all songs. - :return: - """ - return len(self.artist_collection) > 1 - - @property - def album_type_string(self) -> str: - return self.album_type.value - - -""" -All objects dependent on Artist -""" - - -class Artist(MainObject): - COLLECTION_ATTRIBUTES = ( - "feature_song_collection", - "main_album_collection", - "label_collection", - "source_collection" - ) - SIMPLE_ATTRIBUTES = { - "name": None, - "unified_name": None, - "country": None, - "formed_in": ID3Timestamp(), - "notes": FormattedText(), - "lyrical_themes": [], - "general_genre": "" - } - - DOWNWARDS_COLLECTION_ATTRIBUTES = ("feature_song_collection", "main_album_collection") - UPWARDS_COLLECTION_ATTRIBUTES = ("label_collection", ) - - def __init__( - self, - _id: int = None, - dynamic: bool = False, - name: str = None, - unified_name: str = None, - source_list: List[Source] = None, - feature_song_list: List[Song] = None, - main_album_list: List[Album] = None, - notes: FormattedText = None, - lyrical_themes: List[str] = None, - general_genre: str = "", - country: CountryTyping = None, - formed_in: ID3Timestamp = None, - label_list: List['Label'] = None, - **kwargs - ): - MainObject.__init__(self, _id=_id, dynamic=dynamic, **kwargs) - - self.name: str = name - self.unified_name: str = unified_name - if unified_name is None and name is not None: - self.unified_name = unify(name) - - """ - TODO implement album type and notes - """ - self.country: CountryTyping = country - self.formed_in: ID3Timestamp = formed_in - """ - notes, general genre, lyrics themes are attributes - which are meant to only use in outputs to describe the object - i mean do as you want but there is no strict rule about em so good luck - """ - self.notes: FormattedText = notes or FormattedText() - - self.lyrical_themes: List[str] = lyrical_themes or [] - self.general_genre = general_genre - - self.source_collection: SourceCollection = SourceCollection(source_list) - self.feature_song_collection: Collection[Song] = Collection(data=feature_song_list, element_type=Song) - self.main_album_collection: Collection[Album] = Collection(data=main_album_list, element_type=Album) - self.label_collection: Collection[Label] = Collection(data=label_list, element_type=Label) - - def _add_other_db_objects(self, object_type: Type["DatabaseObject"], object_list: List["DatabaseObject"]): - if object_type is Song: - # this doesn't really make sense - # self.feature_song_collection.extend(object_list) - return - - if object_type is Artist: - return - - if object_type is Album: - self.main_album_collection.extend(object_list) - return - - if object_type is Label: - self.label_collection.extend(object_list) - return - - def compile(self, merge_into: bool = False): - """ - compiles the recursive structures, - and does depending on the object some other stuff. - - no need to override if only the recursive structure should be built. - override self.build_recursive_structures() instead - """ - - self.update_albumsort() - self._build_recursive_structures(build_version=random.randint(0, 99999), merge=merge_into) - - def update_albumsort(self): - """ - This updates the albumsort attributes, of the albums in - `self.main_album_collection`, and sorts the albums, if possible. - - It is advised to only call this function, once all the albums are - added to the artist. - - :return: - """ - if len(self.main_album_collection) <= 0: - return - - type_section: Dict[AlbumType] = defaultdict(lambda: 2, { - AlbumType.OTHER: 0, # if I don't know it, I add it to the first section - AlbumType.STUDIO_ALBUM: 0, - AlbumType.EP: 0, - AlbumType.SINGLE: 1 - }) if main_settings["sort_album_by_type"] else defaultdict(lambda: 0) - - sections = defaultdict(list) - - # order albums in the previously defined section - album: Album - for album in self.main_album_collection: - sections[type_section[album.album_type]].append(album) - - def sort_section(_section: List[Album], last_albumsort: int) -> int: - # album is just a value used in loops - nonlocal album - - if main_settings["sort_by_date"]: - _section.sort(key=lambda _album: _album.date, reverse=True) - - new_last_albumsort = last_albumsort - - for album_index, album in enumerate(_section): - if album.albumsort is None: - album.albumsort = new_last_albumsort = album_index + 1 + last_albumsort - - _section.sort(key=lambda _album: _album.albumsort) - - return new_last_albumsort - - # sort the sections individually - _last_albumsort = 1 - for section_index in sorted(sections): - _last_albumsort = sort_section(sections[section_index], _last_albumsort) - - # merge all sections again - album_list = [] - for section_index in sorted(sections): - album_list.extend(sections[section_index]) - - # replace the old collection with the new one - self.main_album_collection: Collection = Collection(data=album_list, element_type=Album) - - - def _build_recursive_structures(self, build_version: int, merge: False): - if build_version == self.build_version: - return - self.build_version = build_version - - song: Song - for song in self.feature_song_collection: - song.feature_artist_collection.append(self, merge_on_conflict=merge, merge_into_existing=False) - song._build_recursive_structures(build_version=build_version, merge=merge) - - album: Album - for album in self.main_album_collection: - album.artist_collection.append(self, merge_on_conflict=merge, merge_into_existing=False) - album._build_recursive_structures(build_version=build_version, merge=merge) - - label: Label - for label in self.label_collection: - label.current_artist_collection.append(self, merge_on_conflict=merge, merge_into_existing=False) - label._build_recursive_structures(build_version=build_version, merge=merge) - - @property - def indexing_values(self) -> List[Tuple[str, object]]: - return [ - ('id', self.id), - ('name', self.unified_name), - *[('url', source.url) for source in self.source_collection] - ] - - @property - def metadata(self) -> Metadata: - metadata = Metadata({ - id3Mapping.ARTIST: [self.name] - }) - metadata.merge_many([s.get_artist_metadata() for s in self.source_collection]) - - return metadata - - def __str__(self): - string = self.name or "" - plaintext_notes = self.notes.get_plaintext() - if plaintext_notes is not None: - string += "\n" + plaintext_notes - return string - - def __repr__(self): - return f"Artist(\"{self.name}\")" - - @property - def option_string(self) -> str: - return f"{self.__repr__()} " \ - f"under Label({OPTION_STRING_DELIMITER.join([label.name for label in self.label_collection])})" - - @property - def options(self) -> List[DatabaseObject]: - options = [self] - options.extend(self.main_album_collection) - options.extend(self.feature_song_collection) - return options - - @property - def country_string(self): - return self.country.alpha_3 - - @property - def feature_album(self) -> Album: - return Album( - title="features", - album_status=AlbumStatus.UNRELEASED, - album_type=AlbumType.COMPILATION_ALBUM, - is_split=True, - albumsort=666, - dynamic=True, - song_list=self.feature_song_collection.shallow_list - ) - - def get_all_songs(self) -> List[Song]: - """ - returns a list of all Songs. - probably not that useful, because it is unsorted - """ - collection = self.feature_song_collection.copy() - for album in self.discography: - collection.extend(album.song_collection) - - return collection - - @property - def discography(self) -> List[Album]: - flat_copy_discography = self.main_album_collection.copy() - flat_copy_discography.append(self.feature_album) - - return flat_copy_discography - - -""" -Label -""" - - -class Label(MainObject): - COLLECTION_ATTRIBUTES = ("album_collection", "current_artist_collection") - SIMPLE_ATTRIBUTES = { - "name": None, - "unified_name": None, - "notes": FormattedText() - } - - DOWNWARDS_COLLECTION_ATTRIBUTES = COLLECTION_ATTRIBUTES - - def __init__( - self, - _id: int = None, - dynamic: bool = False, - name: str = None, - unified_name: str = None, - notes: FormattedText = None, - album_list: List[Album] = None, - current_artist_list: List[Artist] = None, - source_list: List[Source] = None, - **kwargs - ): - MainObject.__init__(self, _id=_id, dynamic=dynamic, **kwargs) - - self.name: str = name - self.unified_name: str = unified_name - if unified_name is None and name is not None: - self.unified_name = unify(name) - self.notes = notes or FormattedText() - - self.source_collection: SourceCollection = SourceCollection(source_list) - self.album_collection: Collection[Album] = Collection(data=album_list, element_type=Album) - self.current_artist_collection: Collection[Artist] = Collection(data=current_artist_list, element_type=Artist) - - def _add_other_db_objects(self, object_type: Type["DatabaseObject"], object_list: List["DatabaseObject"]): - if object_type is Song: - return - - if object_type is Artist: - self.current_artist_collection.extend(object_list) - return - - if object_type is Album: - self.album_collection.extend(object_list) - return - - def _build_recursive_structures(self, build_version: int, merge: False): - if build_version == self.build_version: - return - self.build_version = build_version - - album: Album - for album in self.album_collection: - album.label_collection.append(self, merge_on_conflict=merge, merge_into_existing=False) - album._build_recursive_structures(build_version=build_version, merge=merge) - - artist: Artist - for artist in self.current_artist_collection: - artist.label_collection.append(self, merge_on_conflict=merge, merge_into_existing=False) - artist._build_recursive_structures(build_version=build_version, merge=merge) - - @property - def indexing_values(self) -> List[Tuple[str, object]]: - return [ - ('id', self.id), - ('name', self.unified_name), - *[('url', source.url) for source in self.source_collection] - ] - - @property - def options(self) -> List[DatabaseObject]: - options = [self] - options.extend(self.current_artist_collection.shallow_list) - options.extend(self.album_collection.shallow_list) - - return options diff --git a/src/music_kraken/utils/__init__.py b/src/music_kraken/utils/__init__.py deleted file mode 100644 index 89186a6..0000000 --- a/src/music_kraken/utils/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .config import config, read_config, write_config diff --git a/src/music_kraken/utils/config/attributes/__init__,py b/src/music_kraken/utils/config/attributes/__init__,py deleted file mode 100644 index e69de29..0000000 diff --git a/src/music_kraken/utils/config/sections/audio.py b/src/music_kraken/utils/config/sections/audio.py deleted file mode 100644 index def1348..0000000 --- a/src/music_kraken/utils/config/sections/audio.py +++ /dev/null @@ -1,154 +0,0 @@ -import logging - -from ..base_classes import ( - SingleAttribute, - FloatAttribute, - StringAttribute, - Section, - Description, - EmptyLine, - BoolAttribute, - ListAttribute -) -from ...enums.album import AlbumType -from ...exception.config import SettingValueError - -# Only the formats with id3 metadata can be used -# https://www.audioranger.com/audio-formats.php -# https://web.archive.org/web/20230322234434/https://www.audioranger.com/audio-formats.php -ID3_2_FILE_FORMATS = frozenset(( - "mp3", "mp2", "mp1", # MPEG-1 ID3.2 - "wav", "wave", "rmi", # RIFF (including WAV) ID3.2 - "aiff", "aif", "aifc", # AIFF ID3.2 - "aac", "aacp", # Raw AAC ID3.2 - "tta", # True Audio ID3.2 -)) -_sorted_id3_2_formats = sorted(ID3_2_FILE_FORMATS) - -ID3_1_FILE_FORMATS = frozenset(( - "ape", # Monkey's Audio ID3.1 - "mpc", "mpp", "mp+", # MusePack ID3.1 - "wv", # WavPack ID3.1 - "ofr", "ofs" # OptimFrog ID3.1 -)) -_sorted_id3_1_formats = sorted(ID3_1_FILE_FORMATS) - - -class AudioFormatAttribute(SingleAttribute): - def validate(self, value: str): - v = self.value.strip().lower() - if v not in ID3_1_FILE_FORMATS and v not in ID3_2_FILE_FORMATS: - raise SettingValueError( - setting_name=self.name, - setting_value=value, - rule="has to be a valid audio format, supporting id3 metadata" - ) - - @property - def object_from_value(self) -> str: - v = self.value.strip().lower() - if v in ID3_2_FILE_FORMATS: - return v - if v in ID3_1_FILE_FORMATS: - logging.debug(f"setting audio format to a format that only supports ID3.1: {v}") - return v - - raise ValueError(f"Invalid Audio Format: {v}") - - -class AlbumTypeListAttribute(ListAttribute): - def validate(self, value: str): - try: - AlbumType(value.strip()) - except ValueError: - raise SettingValueError( - setting_name=self.name, - setting_value=value, - rule="has to be an existing album type" - ) - - def single_object_from_element(self, value: str) -> AlbumType: - return AlbumType(value) - - -class AudioSection(Section): - def __init__(self): - self.BITRATE = FloatAttribute( - name="bitrate", - description="Streams the audio with given bitrate [kB/s]. " - "Can't stream with a higher Bitrate, than the audio source provides.", - value="125" - ) - - self.AUDIO_FORMAT = AudioFormatAttribute(name="audio_format", value="mp3", description=f""" -Music Kraken will stream the audio into this format. -You can use Audio formats which support ID3.2 and ID3.1, -but you will have cleaner Metadata using ID3.2. -ID3.2: {', '.join(_sorted_id3_2_formats)} -ID3.1: {', '.join(_sorted_id3_1_formats)} - """.strip()) - - self.SORT_BY_DATE = BoolAttribute( - name="sort_by_date", - description="If this is set to true, it will set the albumsort attribute such that,\n" - "the albums are sorted by date.", - value="true" - ) - - self.SORT_BY_ALBUM_TYPE = BoolAttribute( - name="sort_album_by_type", - description="If this is set to true, it will set the albumsort attribute such that,\n" - "the albums are put into categories before being sorted.\n" - "This means for example, the Studio Albums and EP's are always in front of Singles, " - "and Compilations are in the back.", - value="true" - ) - - self.DOWNLOAD_PATH = StringAttribute( - name="download_path", - value="{genre}/{artist}/{album}", - description="The folder music kraken should put the songs into." - ) - - self.DOWNLOAD_FILE = StringAttribute( - name="download_file", - value="{song}.{audio_format}", - description="The filename of the audio file." - ) - - - self.ALBUM_TYPE_BLACKLIST = AlbumTypeListAttribute( - name="album_type_blacklist", - description="Music Kraken ignores all albums of those types.\n" - "Following album types exist in the programm:\n" - f"{', '.join(album.value for album in AlbumType)}", - value=[ - AlbumType.COMPILATION_ALBUM.value, - AlbumType.LIVE_ALBUM.value, - AlbumType.MIXTAPE.value - ] - ) - - self.attribute_list = [ - self.BITRATE, - self.AUDIO_FORMAT, - EmptyLine(), - self.SORT_BY_DATE, - self.SORT_BY_ALBUM_TYPE, - Description(""" -There are multiple fields, you can use for the path and file name: -- genre -- label -- artist -- album -- song -- album_type - """.strip()), - self.DOWNLOAD_PATH, - self.DOWNLOAD_FILE, - self.ALBUM_TYPE_BLACKLIST, - ] - super().__init__() - - -AUDIO_SECTION = AudioSection() diff --git a/src/music_kraken/utils/config/sections/connection.py b/src/music_kraken/utils/config/sections/connection.py deleted file mode 100644 index b29ac05..0000000 --- a/src/music_kraken/utils/config/sections/connection.py +++ /dev/null @@ -1,157 +0,0 @@ -from urllib.parse import urlparse, ParseResult -import re - -from ..base_classes import Section, FloatAttribute, IntAttribute, BoolAttribute, ListAttribute, StringAttribute -from ...regex import URL_PATTERN -from ...exception.config import SettingValueError - - -class ProxAttribute(ListAttribute): - def single_object_from_element(self, value) -> dict: - return { - 'http': value, - 'https': value, - 'ftp': value - } - - -class UrlStringAttribute(StringAttribute): - def validate(self, value: str): - v = value.strip() - url = re.match(URL_PATTERN, v) - if url is None: - raise SettingValueError( - setting_name=self.name, - setting_value=v, - rule="has to be a valid url" - ) - - @property - def object_from_value(self) -> ParseResult: - return urlparse(self.value) - - -class UrlListAttribute(ListAttribute): - def validate(self, value: str): - v = value.strip() - url = re.match(URL_PATTERN, v) - if url is None: - raise SettingValueError( - setting_name=self.name, - setting_value=v, - rule="has to be a valid url" - ) - - def single_object_from_element(self, value: str): - return urlparse(value) - - - -class ConnectionSection(Section): - def __init__(self): - self.PROXIES = ProxAttribute( - name="proxies", - description="Set your proxies.\n" - "Must be valid for http, as well as https.", - value=[] - ) - - self.USE_TOR = BoolAttribute( - name="tor", - description="Route ALL traffic through Tor.\n" - "If you use Tor, make sure the Tor browser is installed, and running." - "I can't guarantee maximum security though!", - value="false" - ) - self.TOR_PORT = IntAttribute( - name="tor_port", - description="The port, tor is listening. If tor is already working, don't change it.", - value="9150" - ) - self.CHUNK_SIZE = IntAttribute( - name="chunk_size", - description="Size of the chunks that are streamed.", - value="1024" - ) - self.SHOW_DOWNLOAD_ERRORS_THRESHOLD = FloatAttribute( - name="show_download_errors_threshold", - description="If the percentage of failed downloads goes over this threshold,\n" - "all the error messages are shown.", - value="0.3" - ) - - # INVIDIOUS INSTANCES LIST - self.INVIDIOUS_INSTANCE = UrlStringAttribute( - name="invidious_instance", - description="This is an attribute, where you can define the invidious instances,\n" - "the youtube downloader should use.\n" - "Here is a list of active ones: https://docs.invidious.io/instances/\n" - "Instances that use cloudflare or have source code changes could cause issues.\n" - "Hidden instances (.onion) will only work, when setting 'tor=true'.", - value="https://yt.artemislena.eu/" - ) - - self.PIPED_INSTANCE = UrlStringAttribute( - name="piped_instance", - description="This is an attribute, where you can define the pioed instances,\n" - "the youtube downloader should use.\n" - "Here is a list of active ones: https://github.com/TeamPiped/Piped/wiki/Instances\n" - "Instances that use cloudflare or have source code changes could cause issues.\n" - "Hidden instances (.onion) will only work, when setting 'tor=true'.", - value="https://pipedapi.kavin.rocks" - ) - - self.SLEEP_AFTER_YOUTUBE_403 = FloatAttribute( - name="sleep_after_youtube_403", - description="The time to wait, after youtube returned 403 (in seconds)", - value="20" - ) - - self.YOUTUBE_MUSIC_API_KEY = StringAttribute( - name="youtube_music_api_key", - description="This is the API key used by YouTube-Music internally.\nDw. if it is empty, Rachel will fetch it automatically for you <333\n(she will also update outdated api keys/those that don't work)", - value="AIzaSyC9XL3ZjWddXya6X74dJoCTL-WEYFDNX30" - ) - - self.YOUTUBE_MUSIC_CLEAN_DATA = BoolAttribute( - name="youtube_music_clean_data", - description="If set to true, it exclusively fetches artists/albums/songs, not things like user channels etc.", - value="true" - ) - - self.ALL_YOUTUBE_URLS = UrlListAttribute( - name="youtube_url", - description="This is used to detect, if an url is from youtube, or any alternativ frontend.\n" - "If any instance seems to be missing, run music kraken with the -f flag.", - value=[ - "https://www.youtube.com/", - "https://www.youtu.be/", - "https://redirect.invidious.io/", - "https://piped.kavin.rocks/" - ] - ) - - self.SPONSOR_BLOCK = BoolAttribute( - name="use_sponsor_block", - value="true", - description="Use sponsor block to remove adds or simmilar from the youtube videos." - ) - - self.attribute_list = [ - self.USE_TOR, - self.TOR_PORT, - self.CHUNK_SIZE, - self.SHOW_DOWNLOAD_ERRORS_THRESHOLD, - self.INVIDIOUS_INSTANCE, - self.PIPED_INSTANCE, - self.SLEEP_AFTER_YOUTUBE_403, - self.YOUTUBE_MUSIC_API_KEY, - self.YOUTUBE_MUSIC_CLEAN_DATA, - self.ALL_YOUTUBE_URLS, - self.SPONSOR_BLOCK - ] - - super().__init__() - - -CONNECTION_SECTION = ConnectionSection() diff --git a/src/music_kraken/utils/config/sections/logging.py b/src/music_kraken/utils/config/sections/logging.py deleted file mode 100644 index 17c0969..0000000 --- a/src/music_kraken/utils/config/sections/logging.py +++ /dev/null @@ -1,130 +0,0 @@ -import logging -from typing import Callable - -from ..base_classes import SingleAttribute, StringAttribute, Section, Description, EmptyLine - -LOG_LEVELS = { - "CRITICAL": 50, - "ERROR": 40, - "WARNING": 30, - "INFO": 20, - "DEBUG": 10, - "NOTSET": 0 -} - - -class LoggerAttribute(SingleAttribute): - @property - def object_from_value(self) -> logging.Logger: - return logging.getLogger(self.value) - - -class LogLevelAttribute(SingleAttribute): - @property - def object_from_value(self) -> int: - """ - gets the numeric value of a log level - :return: - """ - if self.value.isnumeric(): - return int(self.value) - - v = self.value.strip().upper() - - if v not in LOG_LEVELS: - raise ValueError( - f"{self.name} can only been either one of the following levels, or an integer:\n" - f"{';'.join(key for key in LOG_LEVELS)}" - ) - - return LOG_LEVELS[v] - - -class LoggingSection(Section): - def __init__(self): - self.FORMAT = StringAttribute( - name="logging_format", - description="Reference for the logging formats: " - "https://docs.python.org/3/library/logging.html#logrecord-attributes", - value=logging.BASIC_FORMAT - ) - self.LOG_LEVEL = LogLevelAttribute( - name="log_level", - description=f"can only been either one of the following levels, or an integer:\n" - f"{';'.join(key for key in LOG_LEVELS)}", - value=str(logging.INFO) - ) - - self.DOWNLOAD_LOGGER = LoggerAttribute( - name="download_logger", - description="The logger for downloading.", - value="download" - ) - self.TAGGING_LOGGER = LoggerAttribute( - name="tagging_logger", - description="The logger for tagging id3 containers.", - value="tagging" - ) - self.CODEX_LOGGER = LoggerAttribute( - name="codex_logger", - description="The logger for streaming the audio into an uniform codex.", - value="codex" - ) - self.OBJECT_LOGGER = LoggerAttribute( - name="object_logger", - description="The logger for creating Data-Objects.", - value="object" - ) - self.DATABASE_LOGGER = LoggerAttribute( - name="database_logger", - description="The logger for Database operations.", - value="database" - ) - self.MUSIFY_LOGGER = LoggerAttribute( - name="musify_logger", - description="The logger for the musify scraper.", - value="musify" - ) - self.YOUTUBE_LOGGER = LoggerAttribute( - name="youtube_logger", - description="The logger for the youtube scraper.", - value="youtube" - ) - self.YOUTUBE_MUSIC_LOGGER = LoggerAttribute( - name="youtube_music_logger", - description="The logger for the youtube music scraper.\n(The scraper is seperate to the youtube scraper)", - value="youtube_music" - ) - self.ENCYCLOPAEDIA_METALLUM_LOGGER = LoggerAttribute( - name="metal_archives_logger", - description="The logger for the metal archives scraper.", - value="metal_archives" - ) - self.GENIUS_LOGGER = LoggerAttribute( - name="genius_logger", - description="The logger for the genius scraper", - value="genius" - ) - - self.attribute_list = [ - Description("Logging settings for the actual logging:"), - self.FORMAT, - self.LOG_LEVEL, - EmptyLine(), - Description("Just the names for different logger, for different parts of the programm:"), - self.DOWNLOAD_LOGGER, - self.TAGGING_LOGGER, - self.CODEX_LOGGER, - self.OBJECT_LOGGER, - self.DATABASE_LOGGER, - self.MUSIFY_LOGGER, - self.YOUTUBE_LOGGER, - self.YOUTUBE_MUSIC_LOGGER, - self.ENCYCLOPAEDIA_METALLUM_LOGGER, - self.GENIUS_LOGGER - ] - - super().__init__() - - -LOGGING_SECTION = LoggingSection() diff --git a/src/music_kraken/utils/config/sections/misc.py b/src/music_kraken/utils/config/sections/misc.py deleted file mode 100644 index ad8641a..0000000 --- a/src/music_kraken/utils/config/sections/misc.py +++ /dev/null @@ -1,72 +0,0 @@ -from ..base_classes import Section, IntAttribute, ListAttribute, BoolAttribute - - -class MiscSection(Section): - def __init__(self): - self.HASNT_YET_STARTED = BoolAttribute( - name="hasnt_yet_started", - description="If you did already run, and configured everything, this is false.", - value="true" - ) - - self.ENABLE_RESULT_HISTORY = BoolAttribute( - name="result_history", - description="If enabled, you can go back to the previous results.\n" - "The consequence is a higher meory consumption, because every result is saved.", - value="false" - ) - - self.HISTORY_LENGTH = IntAttribute( - name="history_length", - description="You can choose how far back you can go in the result history.\n" - "The further you choose to be able to go back, the higher the memory usage.\n" - "'-1' removes the Limit entirely.", - value="8" - ) - - self.HAPPY_MESSAGES = ListAttribute( - name="happy_messages", - description="Just some nice and wholesome messages.\n" - "If your mindset has traits of a [file corruption], you might not agree.\n" - "But anyways... Freedom of thought, so go ahead and change the messages.", - value=[ - "Support the artist.", - "Star Me: https://github.com/HeIIow2/music-downloader", - "🏳️‍⚧️🏳️‍⚧️ Trans rights are human rights. 🏳️‍⚧️🏳️‍⚧️", - "🏳️‍⚧️🏳️‍⚧️ Trans women are women, trans men are men, and enbies are enbies. 🏳️‍⚧️🏳️‍⚧️", - "🏴‍☠️🏴‍☠️ Unite under one flag, fck borders. 🏴‍☠️🏴‍☠️", - "Join my Matrix Space: https://matrix.to/#/#music-kraken:matrix.org", - "Gotta love the BPJM ;-;", - "🏳️‍⚧️🏳️‍⚧️ Protect trans youth. 🏳️‍⚧️🏳️‍⚧️", - ] - ) - - self.MODIFY_GC = BoolAttribute( - name="modify_gc", - description="If set to true, it will modify the gc for the sake of performance.\n" - "This should not drive up ram usage, but if it is, then turn it of.\n" - "Here a blog post about that matter:\n" - "https://mkennedy.codes/posts/python-gc-settings-change-this-and-make-your-app-go-20pc-faster/\n" - "https://web.archive.org/web/20221124122222/https://mkennedy.codes/posts/python-gc-settings-change-this-and-make-your-app-go-20pc-faster/", - value="true" - ) - - self.ID_BITS = IntAttribute( - name="id_bits", - description="I really dunno why I even made this a setting.. Modifying this is a REALLY dumb idea.", - value="64" - ) - - self.attribute_list = [ - self.HASNT_YET_STARTED, - self.ENABLE_RESULT_HISTORY, - self.HISTORY_LENGTH, - self.HAPPY_MESSAGES, - self.MODIFY_GC, - self.ID_BITS - ] - - super().__init__() - - -MISC_SECTION = MiscSection() diff --git a/src/music_kraken/utils/config/sections/paths.py b/src/music_kraken/utils/config/sections/paths.py deleted file mode 100644 index cd35a4b..0000000 --- a/src/music_kraken/utils/config/sections/paths.py +++ /dev/null @@ -1,59 +0,0 @@ -from pathlib import Path - -from ...path_manager import LOCATIONS -from ..base_classes import Section, StringAttribute, ListAttribute - - -class PathAttribute(StringAttribute): - @property - def object_from_value(self) -> Path: - return Path(self.value.strip()) - - -class PathsSection(Section): - def __init__(self): - self.MUSIC_DIRECTORY = PathAttribute( - name="music_directory", - description="The directory, all the music will be downloaded to.", - value=str(LOCATIONS.MUSIC_DIRECTORY) - ) - - self.TEMP_DIRECTORY = PathAttribute( - name="temp_directory", - description="All temporary stuff is gonna be dumped in this directory.", - value=str(LOCATIONS.TEMP_DIRECTORY) - ) - - self.LOG_PATH = PathAttribute( - name="log_file", - description="The path to the logging file", - value=str(LOCATIONS.get_log_file("download_logs.log")) - ) - - self.NOT_A_GENRE_REGEX = ListAttribute( - name="not_a_genre_regex", - description="These regular expressions tell music-kraken, which sub-folders of the music-directory\n" - "it should ignore, and not count to genres", - value=[ - r'^\.' # is hidden/starts with a "." - ] - ) - - self.FFMPEG_BINARY = PathAttribute( - name="ffmpeg_binary", - description="Set the path to the ffmpeg binary.", - value=str(LOCATIONS.FFMPEG_BIN) - ) - - self.attribute_list = [ - self.MUSIC_DIRECTORY, - self.TEMP_DIRECTORY, - self.LOG_PATH, - self.NOT_A_GENRE_REGEX, - self.FFMPEG_BINARY - ] - - super().__init__() - - -PATHS_SECTION = PathsSection() diff --git a/src/music_kraken/utils/debug_utils.py b/src/music_kraken/utils/debug_utils.py deleted file mode 100644 index a13ecb7..0000000 --- a/src/music_kraken/utils/debug_utils.py +++ /dev/null @@ -1,18 +0,0 @@ -from pathlib import Path -import json - -from .path_manager import LOCATIONS - - -def dump_to_file(file_name: str, payload: str, is_json: bool = False, exit_after_dump: bool = True): - path = Path(LOCATIONS.TEMP_DIRECTORY, file_name) - print(f"Dumping payload to: \"{path}\"") - - if is_json: - payload = json.dumps(json.loads(payload), indent=4) - - with path.open("w", encoding="utf-8") as f: - f.write(payload) - - if exit_after_dump: - exit() diff --git a/src/music_kraken/utils/enums/__init__.py b/src/music_kraken/utils/enums/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/music_kraken/utils/functions.py b/src/music_kraken/utils/functions.py deleted file mode 100644 index f773213..0000000 --- a/src/music_kraken/utils/functions.py +++ /dev/null @@ -1,11 +0,0 @@ -import os -from datetime import datetime - - -def clear_console(): - os.system('cls' if os.name in ('nt', 'dos') else 'clear') - - -def get_current_millis() -> int: - dt = datetime.now() - return int(dt.microsecond / 1_000) diff --git a/src/music_kraken/utils/object_handeling.py b/src/music_kraken/utils/object_handeling.py deleted file mode 100644 index 7922603..0000000 --- a/src/music_kraken/utils/object_handeling.py +++ /dev/null @@ -1,24 +0,0 @@ -from datetime import date - - -def get_elem_from_obj(current_object, keys: list, after_process=lambda x: x, return_if_none=None): - current_object = current_object - for key in keys: - if key in current_object or (type(key) == int and key < len(current_object)): - current_object = current_object[key] - else: - return return_if_none - return after_process(current_object) - - -def parse_music_brainz_date(mb_date: str) -> date: - year = 1 - month = 1 - day = 1 - - first_release_date = mb_date - if first_release_date.count("-") == 2: - year, month, day = [int(i) for i in first_release_date.split("-")] - elif first_release_date.count("-") == 0 and first_release_date.isdigit(): - year = int(first_release_date) - return date(year, month, day) diff --git a/src/music_kraken/utils/path_manager/locations.py b/src/music_kraken/utils/path_manager/locations.py deleted file mode 100644 index 66953d1..0000000 --- a/src/music_kraken/utils/path_manager/locations.py +++ /dev/null @@ -1,31 +0,0 @@ -from pathlib import Path -import os - -import tempfile -from pyffmpeg import FFmpeg - -from .music_directory import get_music_directory -from .config_directory import get_config_directory - - -class Locations: - def __init__(self, application_name: os.PathLike = "music-kraken"): - self.FILE_ENCODING: str = "utf-8" - - self.TEMP_DIRECTORY = Path(tempfile.gettempdir(), application_name) - self.TEMP_DIRECTORY.mkdir(exist_ok=True, parents=True) - - self.MUSIC_DIRECTORY = get_music_directory() - - self.CONFIG_DIRECTORY = get_config_directory(str(application_name)) - self.CONFIG_DIRECTORY.mkdir(exist_ok=True, parents=True) - self.CONFIG_FILE = Path(self.CONFIG_DIRECTORY, f"{application_name}.conf") - self.LEGACY_CONFIG_FILE = Path(self.CONFIG_DIRECTORY, f"{application_name}.conf") - - self.FFMPEG_BIN = Path(FFmpeg(enable_log=False).get_ffmpeg_bin()) - - def get_config_file(self, config_name: str) -> Path: - return Path(self.CONFIG_DIRECTORY, f"{config_name}.toml") - - def get_log_file(self, file_name: os.PathLike) -> Path: - return Path(self.TEMP_DIRECTORY, file_name) diff --git a/src/music_kraken/utils/phonetic_compares.py b/src/music_kraken/utils/phonetic_compares.py deleted file mode 100644 index 65f5deb..0000000 --- a/src/music_kraken/utils/phonetic_compares.py +++ /dev/null @@ -1,57 +0,0 @@ -import jellyfish -import string - -TITLE_THRESHOLD_LEVENSHTEIN = 1 -UNIFY_TO = " " - -ALLOWED_LENGTH_DISTANCE = 20 - - -def unify_punctuation(to_unify: str) -> str: - for char in string.punctuation: - to_unify = to_unify.replace(char, UNIFY_TO) - return to_unify - - -def remove_feature_part_from_track(title: str) -> str: - if ")" != title[-1]: - return title - if "(" not in title: - return title - - return title[:title.index("(")] - - -def modify_title(to_modify: str) -> str: - to_modify = to_modify.strip() - to_modify = to_modify.lower() - to_modify = remove_feature_part_from_track(to_modify) - to_modify = unify_punctuation(to_modify) - return to_modify - - -def match_titles(title_1: str, title_2: str): - title_1, title_2 = modify_title(title_1), modify_title(title_2) - distance = jellyfish.levenshtein_distance(title_1, title_2) - return distance > TITLE_THRESHOLD_LEVENSHTEIN, distance - - -def match_artists(artist_1, artist_2: str): - if type(artist_1) == list: - distances = [] - - for artist_1_ in artist_1: - match, distance = match_titles(artist_1_, artist_2) - if not match: - return match, distance - - distances.append(distance) - return True, min(distances) - return match_titles(artist_1, artist_2) - -def match_length(length_1: int | None, length_2: int | None) -> bool: - # returning true if either one is Null, because if one value is not known, - # then it shouldn't be an attribute which could reject an audio source - if length_1 is None or length_2 is None: - return True - return abs(length_1 - length_2) <= ALLOWED_LENGTH_DISTANCE diff --git a/src/music_kraken/utils/regex.py b/src/music_kraken/utils/regex.py deleted file mode 100644 index d8f58f5..0000000 --- a/src/music_kraken/utils/regex.py +++ /dev/null @@ -1,3 +0,0 @@ -URL_PATTERN = r"https?://(?:[-\w.]|(?:%[\da-fA-F]{2}))+" -INT_PATTERN = r"^\d*$" -FLOAT_PATTERN = r"^[\d|\,|\.]*$" diff --git a/src/music_kraken/utils/shared.py b/src/music_kraken/utils/shared.py deleted file mode 100644 index 09e2737..0000000 --- a/src/music_kraken/utils/shared.py +++ /dev/null @@ -1,34 +0,0 @@ -import random - -from .config import main_settings - -DEBUG = False -DEBUG_YOUTUBE_INITIALIZING = DEBUG and False -DEBUG_PAGES = DEBUG and False - -if DEBUG: - print("DEBUG ACTIVE") - -def get_random_message() -> str: - return random.choice(main_settings['happy_messages']) - - -ENCODING = "utf-8" - -HIGHEST_ID = 2**main_settings['id_bits'] - - -HELP_MESSAGE = """ -to search: -> s: {query or url} -> s: https://musify.club/release/some-random-release-183028492 -> s: #a {artist} #r {release} #t {track} - -to download: -> d: {option ids or direct url} -> d: 0, 3, 4 -> d: 1 -> d: https://musify.club/release/some-random-release-183028492 - -have fun :3 -""".strip() diff --git a/src/music_kraken/utils/support_classes/__init__.py b/src/music_kraken/utils/support_classes/__init__.py deleted file mode 100644 index 4a04f30..0000000 --- a/src/music_kraken/utils/support_classes/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .download_result import DownloadResult -from .query import Query -from .thread_classes import EndThread, FinishedSearch diff --git a/src/music_kraken/utils/support_classes/thread_classes.py b/src/music_kraken/utils/support_classes/thread_classes.py deleted file mode 100644 index 1a17e57..0000000 --- a/src/music_kraken/utils/support_classes/thread_classes.py +++ /dev/null @@ -1,12 +0,0 @@ -class EndThread: - _has_ended: bool = False - - def __bool__(self): - return self._has_ended - - def exit(self): - self._has_ended - -class FinishedSearch: - pass - \ No newline at end of file diff --git a/src/music_kraken_cli.py b/src/music_kraken_cli.py deleted file mode 100644 index 9c939cf..0000000 --- a/src/music_kraken_cli.py +++ /dev/null @@ -1,5 +0,0 @@ -import music_kraken - - -if __name__ == "__main__": - music_kraken.cli() diff --git a/src/music_kraken_gtk.py b/src/music_kraken_gtk.py deleted file mode 100644 index 8626e7e..0000000 --- a/src/music_kraken_gtk.py +++ /dev/null @@ -1,4 +0,0 @@ -from music_kraken import gtk_gui - -if __name__ == "__main__": - gtk_gui() diff --git a/src/musify_search.py b/src/musify_search.py deleted file mode 100644 index 09fdf5f..0000000 --- a/src/musify_search.py +++ /dev/null @@ -1,51 +0,0 @@ -from music_kraken import objects -from music_kraken.pages import Musify - - -def search(): - results = Musify._raw_search("#a Ghost Bath") - print(results) - - -def fetch_artist(): - artist = objects.Artist( - source_list=[objects.Source(objects.SourcePages.MUSIFY, "https://musify.club/artist/psychonaut-4-83193")] - ) - - artist = objects.Artist( - source_list=[objects.Source(objects.SourcePages.MUSIFY, "https://musify.club/artist/ghost-bath-280348/")] - ) - - artist = objects.Artist( - source_list=[objects.Source(objects.SourcePages.MUSIFY, "https://musify.club/artist/lana-del-rey-124788/releases")] - ) - - artist: objects.Artist = Musify.fetch_details(artist) - print(artist.options) - print(artist.main_album_collection[0].source_collection) - - -def fetch_album(): - album = objects.Album( - source_list=[objects.Source(objects.SourcePages.MUSIFY, - "https://musify.club/release/linkin-park-hybrid-theory-2000-188")] - ) - - album = objects.Album( - source_list=[ - objects.Source(objects.SourcePages.MUSIFY, "https://musify.club/release/ghost-bath-self-loather-2021-1554266") - ] - ) - - album: objects.Album = Musify.fetch_details(album) - print(album.options) - - song: objects.Song - for artist in album.artist_collection: - print(artist.id, artist.name) - for song in album.song_collection: - for artist in song.main_artist_collection: - print(artist.id, artist.name) - -if __name__ == "__main__": - fetch_artist() diff --git a/src/tests/__init__.py b/src/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/tests/conftest.py b/src/tests/conftest.py deleted file mode 100644 index 3bf1e84..0000000 --- a/src/tests/conftest.py +++ /dev/null @@ -1,5 +0,0 @@ -import os -import sys - -# Add the parent directory of the current file (i.e., the "tests" directory) to sys.path -sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) diff --git a/src/tests/test_building_objects.py b/src/tests/test_building_objects.py deleted file mode 100644 index 8cae30c..0000000 --- a/src/tests/test_building_objects.py +++ /dev/null @@ -1,94 +0,0 @@ -from mutagen import id3 -import pycountry -import unittest -import sys -import os -from pathlib import Path - -# Add the parent directory of the src package to the Python module search path -sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) -from music_kraken import objects - - -class TestSong(unittest.TestCase): - - def setUp(self): - self.album_list = [ - objects.Album( - title="One Final Action", - date=objects.ID3Timestamp(year=1986, month=3, day=1), - language=pycountry.languages.get(alpha_2="en"), - label_list=[ - objects.Label(name="an album label") - ], - source_list=[ - objects.Source(objects.SourcePages.ENCYCLOPAEDIA_METALLUM, - "https://www.metal-archives.com/albums/I%27m_in_a_Coffin/One_Final_Action/207614") - ] - ), - ] - - self.artist_list = [] - - self.main_artist_list=[ - objects.Artist( - name="I'm in a coffin", - source_list=[ - objects.Source( - objects.SourcePages.ENCYCLOPAEDIA_METALLUM, - "https://www.metal-archives.com/bands/I%27m_in_a_Coffin/127727" - ) - ] - ), - objects.Artist(name="some_split_artist") - ] - feature_artist_list=[ - objects.Artist( - name="Ruffiction", - label_list=[ - objects.Label(name="Ruffiction Productions") - ] - ) - ] - - self.artist_list.extend(self.main_artist_list) - self.artist_list.extend(feature_artist_list) - - self.song = objects.Song( - genre="HS Core", - title="Vein Deep in the Solution", - length=666, - isrc="US-S1Z-99-00001", - tracksort=2, - target_list=[ - objects.Target(file="song.mp3", path="example") - ], - lyrics_list=[ - objects.Lyrics( - text="these are some depressive lyrics", language="en"), - objects.Lyrics( - text="Dies sind depressive Lyrics", language="de") - ], - source_list=[ - objects.Source(objects.SourcePages.YOUTUBE, - "https://youtu.be/dfnsdajlhkjhsd"), - objects.Source(objects.SourcePages.MUSIFY, - "https://ln.topdf.de/Music-Kraken/") - ], - album_list=self.album_list, - main_artist_list=self.main_artist_list, - feature_artist_list=feature_artist_list, - ) - - self.song.compile() - - def test_album(self): - for artist in self.song.main_artist_collection: - for artist_album in artist.main_album_collection: - self.assertIn(artist_album, self.song.album_collection) - - def test_artist(self): - for album in self.song.album_collection: - for album_artist in album.artist_collection: - self.assertIn(album_artist, self.song.main_artist_collection) - diff --git a/src/tests/test_download.py b/src/tests/test_download.py deleted file mode 100644 index 412b773..0000000 --- a/src/tests/test_download.py +++ /dev/null @@ -1,43 +0,0 @@ -import sys -import os -import unittest - -# Add the parent directory of the src package to the Python module search path -sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) -from music_kraken import pages -from music_kraken.pages import download_center -from music_kraken.pages.download_center import page_attributes - -class TestPageSelection(unittest.TestCase): - def test_no_shady_pages(self): - search = download_center.Download( - exclude_shady=True - ) - - for page in search.pages: - self.assertNotIn(page, page_attributes.SHADY_PAGES) - - def test_excluding(self): - search = download_center.Download( - exclude_pages={pages.EncyclopaediaMetallum} - ) - - for page in search.pages: - self.assertNotEqual(page, pages.EncyclopaediaMetallum) - - - def test_audio_one(self): - search = download_center.Download( - exclude_shady=True - ) - - for audio_page in search.audio_pages: - self.assertIn(audio_page, page_attributes.AUDIO_PAGES) - - def test_audio_two(self): - search = download_center.Download() - - for audio_page in search.audio_pages: - self.assertIn(audio_page, page_attributes.AUDIO_PAGES) - - \ No newline at end of file diff --git a/src/tests/test_objects.py b/src/tests/test_objects.py deleted file mode 100644 index 87b4041..0000000 --- a/src/tests/test_objects.py +++ /dev/null @@ -1,239 +0,0 @@ -from mutagen import id3 -import pycountry -import unittest -import sys -import os -from pathlib import Path - -# Add the parent directory of the src package to the Python module search path -sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) -from music_kraken import objects - -""" -Testing the Formatted text is barely possible cuz one false character and it fails. -Not worth the trouble -""" - - -class TestSong(unittest.TestCase): - - def setUp(self): - self.song = objects.Song( - genre="HS Core", - title="Vein Deep in the Solution", - length=666, - isrc="US-S1Z-99-00001", - tracksort=2, - target_list=[ - objects.Target(file="song.mp3", path="example") - ], - lyrics_list=[ - objects.Lyrics( - text="these are some depressive lyrics", language="en"), - objects.Lyrics( - text="Dies sind depressive Lyrics", language="de") - ], - source_list=[ - objects.Source(objects.SourcePages.YOUTUBE, - "https://youtu.be/dfnsdajlhkjhsd"), - objects.Source(objects.SourcePages.MUSIFY, - "https://ln.topdf.de/Music-Kraken/") - ], - album_list=[ - objects.Album( - title="One Final Action", - date=objects.ID3Timestamp(year=1986, month=3, day=1), - language=pycountry.languages.get(alpha_2="en"), - label_list=[ - objects.Label(name="an album label") - ], - source_list=[ - objects.Source(objects.SourcePages.ENCYCLOPAEDIA_METALLUM, - "https://www.metal-archives.com/albums/I%27m_in_a_Coffin/One_Final_Action/207614") - ] - ), - ], - main_artist_list=[ - objects.Artist( - name="I'm in a coffin", - source_list=[ - objects.Source( - objects.SourcePages.ENCYCLOPAEDIA_METALLUM, - "https://www.metal-archives.com/bands/I%27m_in_a_Coffin/127727" - ) - ] - ), - objects.Artist(name="some_split_artist") - ], - feature_artist_list=[ - objects.Artist( - name="Ruffiction", - label_list=[ - objects.Label(name="Ruffiction Productions") - ] - ) - ], - ) - - def test_song_genre(self): - self.assertEqual(self.song.genre, "HS Core") - - def test_song_title(self): - self.assertEqual(self.song.title, "Vein Deep in the Solution") - - def test_song_length(self): - self.assertEqual(self.song.length, 666) - - def test_song_isrc(self): - self.assertEqual(self.song.isrc, "US-S1Z-99-00001") - - def test_song_tracksort(self): - self.assertEqual(self.song.tracksort, 2) - - def test_song_target(self): - self.assertEqual(self.song.target_collection[0].file_path, Path("example", "song.mp3")) - - def test_song_lyrics(self): - self.assertEqual(len(self.song.lyrics_collection), 2) - # the other stuff will be tested in the Lyrics test - - def test_song_source(self): - self.assertEqual(len(self.song.source_collection), 2) - # again the other stuff will be tested in dedicaded stuff - - -class TestAlbum(unittest.TestCase): - - def setUp(self): - self.album = objects.Album( - title="One Final Action", - date=objects.ID3Timestamp(year=1986, month=3, day=1), - language=pycountry.languages.get(alpha_2="en"), - label_list=[ - objects.Label(name="an album label") - ], - source_list=[ - objects.Source(objects.SourcePages.ENCYCLOPAEDIA_METALLUM, - "https://www.metal-archives.com/albums/I%27m_in_a_Coffin/One_Final_Action/207614") - ] - ) - - def test_album_title(self): - self.assertEqual(self.album.title, "One Final Action") - - def test_album_date(self): - self.assertEqual(self.album.date.year, 1986) - self.assertEqual(self.album.date.month, 3) - self.assertEqual(self.album.date.day, 1) - - def test_album_language(self): - self.assertEqual(self.album.language.alpha_2, "en") - - def test_album_label(self): - self.assertEqual(self.album.label_collection[0].name, "an album label") - - def test_album_source(self): - sp = self.album.source_collection.get_sources_from_page(objects.SourcePages.ENCYCLOPAEDIA_METALLUM)[0] - - self.assertEqual( - sp.page_enum, objects.SourcePages.ENCYCLOPAEDIA_METALLUM) - self.assertEqual( - sp.url, "https://www.metal-archives.com/albums/I%27m_in_a_Coffin/One_Final_Action/207614") - - -class TestCollection(unittest.TestCase): - def setUp(self): - self.song_list: objects.song = [ - objects.Song(title="hasskrank"), - objects.Song(title="HaSSkrank"), - objects.Song(title="Suicideseason", isrc="uniqueID"), - objects.Song(title="same isrc different title", isrc="uniqueID") - ] - self.unified_titels = set(song.unified_title for song in self.song_list) - - self.collection = objects.Collection( - element_type=objects.Song, - data=self.song_list - ) - - def test_length(self): - # hasskrank gets merged into HaSSkrank - self.assertEqual(len(self.collection), 2) - - def test_data(self): - """ - tests if the every unified name existed - """ - song: objects.Song - for song in self.collection: - self.assertIn(song.unified_title, self.unified_titels) - - def test_appending(self): - collection = objects.Collection( - element_type=objects.Song - ) - - res = collection.append(self.song_list[0]) - self.assertEqual(res.was_in_collection, False) - self.assertEqual(res.current_element, self.song_list[0]) - - res = collection.append(self.song_list[1]) - self.assertEqual(res.was_in_collection, True) - self.assertEqual(res.current_element, self.song_list[0]) - - res = collection.append(self.song_list[2]) - self.assertEqual(res.was_in_collection, False) - self.assertEqual(res.current_element, self.song_list[2]) - - res = collection.append(self.song_list[3], merge_into_existing=False) - self.assertEqual(res.was_in_collection, True) - self.assertEqual(res.current_element, self.song_list[3]) - - - - - - - - - -class TestLyrics(unittest.TestCase): - """ - TODO - I NEED TO REWRITE LYRICS TAKING FORMATTED TEXT INSTEAD OF JUST STRINGS - """ - - def setUp(self): - self.lyrics = objects.Lyrics( - text="these are some depressive lyrics", - language=pycountry.languages.get(alpha_2="en"), - source_list=[ - objects.Source(objects.SourcePages.ENCYCLOPAEDIA_METALLUM, - "https://www.metal-archives.com/lyrics/I%27m_in_a_Coffin/One_Final_Action/207614"), - objects.Source(objects.SourcePages.MUSIFY, - "https://www.musify.com/lyrics/I%27m_in_a_Coffin/One_Final_Action/207614") - ] - ) - - def test_lyrics_text(self): - self.assertEqual(self.lyrics.text, "these are some depressive lyrics") - - def test_lyrics_language(self): - self.assertEqual(self.lyrics.language.alpha_2, "en") - - def test_lyrics_source(self): - self.assertEqual(len(self.lyrics.source_collection), 2) - - -class TestMetadata(unittest.TestCase): - - - def setUp(self): - self.title = "some title" - - self.song = objects.Song( - title=self.title - ) - - def test_song_metadata(self): - self.assertEqual(self.song.metadata[objects.ID3Mapping.TITLE], id3.Frames[objects.ID3Mapping.TITLE.value](encoding=3, text=self.title)) \ No newline at end of file diff --git a/test.db b/test.db deleted file mode 100644 index b941c19..0000000 Binary files a/test.db and /dev/null differ diff --git a/version b/version deleted file mode 100644 index 23aa839..0000000 --- a/version +++ /dev/null @@ -1 +0,0 @@ -1.2.2 diff --git a/website/assets/logo_cropped.jpg b/website/assets/logo_cropped.jpg deleted file mode 100644 index a4f0a94..0000000 Binary files a/website/assets/logo_cropped.jpg and /dev/null differ diff --git a/website/index.html b/website/index.html deleted file mode 100644 index eac1a40..0000000 --- a/website/index.html +++ /dev/null @@ -1,373 +0,0 @@ - - - - - - Music Kraken - - - - - - - - -
- Hellow2 - - - -
- - -
-
- - -

Music-Kraken

- -

Installation

- -
-# Install it with
-pip install music-kraken
-
-# and simply run it like this:
-music-kraken
-
- -

Quick-Guide

- -

- Genre: First, the cli asks you to input a genre you want to download to. The options it gives you (if it gives you any) are all the folders you have in the music directory. You can also just input a new one. -

-

- What to download: After that it prompts you for a search. Here are a couple examples how you can search: -

- -
-> #a <any artist>
-searches for the artist <any artist>
-
-> #a <any artist> #r <any releas>
-searches for the release <album> <any release> by the artist <any artist>
-
-> #r <any release> Me #t <any track>
-searches for the track <any track> from the release <any relaese>
-
- -

- After searching with this syntax, it prompts you with multiple results. You can either choose one of those by inputing its id `int`, or you can search for a new query. -

-

- After you chose either an artist, a release group, a release, or a track by its id, download it by inputting the string `ok`. My downloader will download it automatically for you. -

-
-
- -
-
-# Programming Interface / Use as Library - -If you want to use this project, or parts from it in your own projects from it, -make sure to be familiar with [Python Modules](https://docs.python.org/3/tutorial/modules.html). -Further and better documentation including code examples are yet to come, so here is the rough -module structure for now. (Should be up-to-date, but no guarantees) - -If you simply want to run the builtin minimal cli just do this: -
-from music_kraken import cli
-
-cli()
-
- -## Search for Metadata - -The whole program takes the data it processes further from the cache, a sqlite database. -So before you can do anything, you will need to fill it with the songs you want to download (*or create song objects manually, but more on that later*). - -For now the base of everything is [musicbrainz][mb], so you need to get the musicbrainz `id` and `type`. The `id` corresponds to either - - an artist - - a release group - - a release - - a recording/track). - -To get this info, you first have to initialize a search object (`music_kraken.MetadataSearch`). - -
-search_object = music_kraken.MetadataSearch()
-
- -Then you need an initial "text search" to get some options you can choose from. For -this you can either specify artists releases and whatever directly with one of the following functions: - -
-# you can directly specify artist, release group, release or recording/track
-multiple_options = search_object.search_from_text(artist=input("input the name of the artist: "))
-# you can specify a query see the simple integrated cli on how to use the query
-multiple_options = search_object.search_from_query(query=input("input the query: "))
-
- -Both methods return an instance of `MultipleOptions`, which can be directly converted to a string. - -
-print(multiple_options)
-
- -After the first "*text search*" you can either again search the same way as before, -or you can further explore one of the options from the previous search. -To explore and select one options from `MultipleOptions`, simply call `MetadataSearch.choose(self, index: int)`. -The index represents the number in the previously returned instance of MultipleOptions. -The selected Option will be selected and can be downloaded in the next step. - -*Thus, this has to be done **after either search_from_text or search_from_query*** - -
-# choosing the best matching band
-multiple_options = search_object.choose(0)
-# choosing the first ever release group of this band
-multiple_options = search_object.choose(1)
-# printing out the current options
-print(multiple_options)
-
- -This process can be repeated indefinitely (until you run out of memory). -A search history is kept in the Search instance. You could go back to -the previous search (without any loading time) like this: - -
-multiple_options = search_object.get_previous_options()
-
- -## Downloading Metadata / Filling up the Cache - -You can download following metadata: - - an artist (the whole discography) - - a release group - - a release - - a track/recording - -If you got an instance of `MetadataSearch`, like I elaborated [previously](#search-for-metadata), downloading every piece of metadata from the currently selected Option is really quite easy. - -
-from music_kraken import fetch_metadata_from_search
-
-# this is it :)
-music_kraken.fetch_metadata_from_search(search_object)
-
- -If you already know what you want to download you can skip the search instance and simply do the following. - -
-from music_kraken import fetch_metadata
-
-# might change and break after I add multiple metadata sources which I will
-
-fetch_metadata(id_=musicbrainz_id, type=metadata_type)
-
- -The option type is a string (*I'm sorry for not making it an enum I know its a bad pratice*), which can -have following values: - - 'artist' - - 'release_group' - - 'release' - - 'recording' - -**PAY ATTENTION TO TYPOS, IT'S CASE SENSITIVE** - -The musicbrainz id is just the id of the object from musicbrainz. - -After following those steps, it might take a couple seconds/minutes to execute, but then the Cache will be filled. - - -## Cache / Temporary Database - -All the data, the functions that download stuff use, can be gotten from the temporary database / cache. -The cache can be simply used like this: - -
-music_kraken.cache
-
- -When fetching any song data from the cache, you will get it as Song -object (music_kraken.Song). There are multiple methods -to get different sets of Songs. The names explain the methods pretty -well: - -
-from music_kraken import cache
-
-# gets a single track specified by the id
-cache.get_track_metadata(id: str)
-
-# gets a list of tracks.
-cache.get_tracks_to_download()
-cache.get_tracks_without_src()
-cache.get_tracks_without_isrc()
-cache.get_tracks_without_filepath()
-
- -The id always is a musicbrainz id and distinct for every track. - -## Setting the Target - -By default the music downloader doesn't know where to save the music file, if downloaded. To set those variables (the directory to save the file in and the filepath), it is enough to run one single command: - -
-from music_kraken import set_target
-
-# adds file path, file directory and the genre to the database 
-set_target(genre="some test genre")
-
- -The concept of genres is too loose, to definitely say, this band exclusively plays this genre, or this song is this genre. This doesn't work manually, this will never work automatically. Thus, I've decided to just use the genre as category, to sort the artists and songs by. Most Music players support that. - -As a result of this decision you will have to pass the genre in this function. - -## Get Audio - -This is most likely the most useful and unique feature of this Project. If the cache is filled, you can get audio sources for the songs you only have the metadata, and download them. This works for most songs. I'd guess for about 97% (?) - -First of you will need a List of song objects `music_kraken.Song`. As [mentioned above](#cache--temporary-database), you could get a list like that from the cache. - -
-# Here is an Example
-from music_kraken import (
-    cache,
-    fetch_sources,
-    fetch_audios
-)
-
-# scanning pages, searching for a download and storing results
-fetch_sources(cache.get_tracks_without_src())
-
-# downloading all previously fetched sources to previously defined targets
-fetch_audios(cache.get_tracks_to_download())
-
-
- -*Note:* -To download audio two cases have to be met: - 1. [The target](#setting-the-target) has to be set beforehand - 2. The sources have to be fetched beforehand - ---- -
-
- -
-
-# Metadata - -First the metadata has to be downloaded. The best api to do so is undeniably [Musicbrainz][mb]. This is a result of them being a website with a large Database spanning over all Genres. - -## Musicbrainz - -![Musicbrainz Data Scheme](https://wiki.musicbrainz.org/-/images/9/9e/pymb3-model-core.png) - -To fetch from [Musicbrainz][mb] we first have to know what to fetch. A good start is to get an input query, which can be just put into the MB-Api. It then returns a list of possible artists, releases and recordings. - -If the following chosen element is an artist, its discography + a couple tracks are printed, if a release is chosen, the artists + tracklist + release is outputted, If a track is chosen its artists and releases are shown. - -For now, it doesn't if the discography or tracklist is chosen. - -## Metadata to fetch - -I orient on which metadata to download on the keys in `mutagen.EasyID3`. The following I fetch and tag the MP3 with: -- title -- artist -- albumartist -- tracknumber -- albumsort can sort albums cronological -- titlesort is just set to the tracknumber to sort by track order to sort correctly -- isrc -- musicbrainz_artistid -- musicbrainz_albumid -- musicbrainz_albumartistid -- musicbrainz_albumstatus -- language -- musicbrainz_albumtype -- releasecountry -- barcode - -### albumsort/titlesort - -Those Tags are for the musicplayer to not sort for Example the albums of a band alphabetically, but in another way. I set it just to chronological order - -### isrc - -This is the **international standart release code**. With this a track can be identified 99% of the time, if it is known and the website has a search api for that. Obviously this will get important later. - -
-
- -
-
- -# Download - -Now that the metadata is downloaded and cached, download sources need to be sound, because one can't listen to metadata. Granted it would be amazing if that would be possible. - -## Musify - -The quickest source to get download links from is to my knowledge [musify](https://musify.club/). It's a Russian music downloading page, where many many songs are available to stream and to download. Due to me not wanting to stress the server to much, I abuse a handy feature nearly every page where you can search suff has. The autocomplete api for the search input. Those always are quite limited in the number of results it returns, but it is optimized to be quick. Thus with the http header `Connection` set to `keep-alive` the bottleneck definitely is not at the speed of those requests. - -For musify the endpoint is following: [https://musify.club/search/suggestions?term={title}](https://musify.club/search/suggestions?term=LornaShore) If the http headers are set correctly, then searching for example for "Lorna Shore" yields following result: - -
-[
-    {
-        "id":"Lorna Shore",
-        "label":"Lorna Shore",
-        "value":"Lorna Shore",
-        "category":"Исполнители",
-        "image":"https://39s.musify.club/img/68/9561484/25159224.jpg",
-        "url":"/artist/lorna-shore-59611"       
-    },
-    {"id":"Immortal","label":"Lorna Shore - Immortal (2020)","value":"Immortal","category":"Релизы","image":"https://39s-a.musify.club/img/70/20335517/52174338.jpg","url":"/release/lorna-shore-immortal-2020-1241300"},
-    {"id":"Immortal","label":"Lorna Shore - Immortal","value":"Immortal","category":"Треки","image":"","url":"/track/lorna-shore-immortal-12475071"}
-]
-
- -This is a shortened example for the response the api gives. The results are very Limited, but it is also very efficient to parse. The steps I take are: - -- call the api with the query being the track name -- parse the json response to an object -- look at how different the title and artist are on every element from the category `Треки`, translated roughly to track or release. -- If they match get the download links and cache them. - -## Youtube - -Herte the **isrc** plays a huge role. You probably know it, when you search on youtube for a song, and the music videos has a long intro or the first result is a live version. I don't want those in my music collection, only if the tracks are like this in the official release. Well how can you get around that? - -Turns out if you search for the **isrc** on youtube the results contain the music, like it is on the official release and some japanese meme videos. The tracks I wan't just have the title of the released track, so one can just compare those two. - -For searching, as well as for downloading I use the programm `youtube-dl`, which also has a programming interface for python. - -There are two bottlenecks with this approach though: -1. `youtube-dl` is just slow. Actually it has to be, to not get blocked by youtube. -2. Ofthen musicbrainz just doesn't give the isrc for some songs. -
-
- -
-
-# Lyrics - -To get the Lyrics, I scrape them, and put those in the USLT ID3 Tags of for example mp3 files. Unfortunately some players, like the one I use, Rhythmbox don't support USLT Lyrics. So I created an Plugin for Rhythmbox. You can find it here: [https://github.com/HeIIow2/rythmbox-id3-lyrics-support](https://github.com/HeIIow2/rythmbox-id3-lyrics-support). - -## Genius - -For the lyrics source the page [https://genius.com/](https://genius.com/) is easily sufficient. It has most songs. Some songs are not present though, but that is fine, because the lyrics are optional anyways. -
-
- - - - - - diff --git a/website/readme.html b/website/readme.html deleted file mode 100644 index bccdc59..0000000 --- a/website/readme.html +++ /dev/null @@ -1,43 +0,0 @@ - - -

- GitHub - PyPI -

- -

Installation

- -
-# Install it with
-pip install music-kraken
-
-# and simply run it like this:
-music-kraken
-
- -

Quick-Guide

- -

- Genre: First, the cli asks you to input a genre you want to download to. The options it gives you (if it gives you any) are all the folders you have in the music directory. You can also just input a new one. -

-

- What to download: After that it prompts you for a search. Here are a couple examples how you can search: -

- -
-> #a <any artist>
-searches for the artist <any artist>
-
-> #a <any artist> #r <any releas>
-searches for the release <album> <any release> by the artist <any artist>
-
-> #r <any release> Me #t <any track>
-searches for the track <any track> from the release <any relaese>
-
- -

- After searching with this syntax, it prompts you with multiple results. You can either choose one of those by inputing its id `int`, or you can search for a new query. -

-

- After you chose either an artist, a release group, a release, or a track by its id, download it by inputting the string `ok`. My downloader will download it automatically for you. -

\ No newline at end of file