diff --git a/.VSCodeCounter/2022-10-28_12-39-22/details.md b/.VSCodeCounter/2022-10-28_12-39-22/details.md new file mode 100644 index 0000000..e0e8fe7 --- /dev/null +++ b/.VSCodeCounter/2022-10-28_12-39-22/details.md @@ -0,0 +1,37 @@ +# Details + +Date : 2022-10-28 12:39:22 + +Directory /home/lars/Projects/music-downloader + +Total : 22 files, 1132 codes, 109 comments, 340 blanks, all 1581 lines + +[Summary](results.md) / Details / [Diff Summary](diff.md) / [Diff Details](diff-details.md) + +## Files +| filename | language | code | comment | blank | total | +| :--- | :--- | ---: | ---: | ---: | ---: | +| [music-downloader/.idea/dataSources.xml](/music-downloader/.idea/dataSources.xml) | XML | 12 | 0 | 0 | 12 | +| [music-downloader/.idea/inspectionProfiles/profiles_settings.xml](/music-downloader/.idea/inspectionProfiles/profiles_settings.xml) | XML | 6 | 0 | 0 | 6 | +| [music-downloader/.idea/misc.xml](/music-downloader/.idea/misc.xml) | XML | 4 | 0 | 0 | 4 | +| [music-downloader/.idea/modules.xml](/music-downloader/.idea/modules.xml) | XML | 8 | 0 | 0 | 8 | +| [music-downloader/.idea/music-downloader.iml](/music-downloader/.idea/music-downloader.iml) | XML | 10 | 0 | 0 | 10 | +| [music-downloader/.idea/vcs.xml](/music-downloader/.idea/vcs.xml) | XML | 6 | 0 | 0 | 6 | +| [music-downloader/README.md](/music-downloader/README.md) | Markdown | 75 | 0 | 34 | 109 | +| [music-downloader/requirements.txt](/music-downloader/requirements.txt) | pip requirements | 8 | 0 | 0 | 8 | +| [music-downloader/src/download.py](/music-downloader/src/download.py) | Python | 59 | 10 | 21 | 90 | +| [music-downloader/src/download_links.py](/music-downloader/src/download_links.py) | Python | 46 | 3 | 18 | 67 | +| [music-downloader/src/main.py](/music-downloader/src/main.py) | Python | 78 | 0 | 26 | 104 | +| [music-downloader/src/metadata/database.py](/music-downloader/src/metadata/database.py) | Python | 121 | 35 | 35 | 191 | +| [music-downloader/src/metadata/database_structure.sql](/music-downloader/src/metadata/database_structure.sql) | SQLite | 48 | 0 | 6 | 54 | +| [music-downloader/src/metadata/download.py](/music-downloader/src/metadata/download.py) | Python | 211 | 33 | 60 | 304 | +| [music-downloader/src/metadata/metadata.py](/music-downloader/src/metadata/metadata.py) | Python | 106 | 9 | 28 | 143 | +| [music-downloader/src/metadata/object_handeling.py](/music-downloader/src/metadata/object_handeling.py) | Python | 19 | 0 | 4 | 23 | +| [music-downloader/src/metadata/options.py](/music-downloader/src/metadata/options.py) | Python | 87 | 8 | 24 | 119 | +| [music-downloader/src/musify.py](/music-downloader/src/musify.py) | Python | 97 | 2 | 37 | 136 | +| [music-downloader/src/phonetic_compares.py](/music-downloader/src/phonetic_compares.py) | Python | 15 | 0 | 8 | 23 | +| [music-downloader/src/test.py](/music-downloader/src/test.py) | Python | 18 | 1 | 6 | 25 | +| [music-downloader/src/url_to_path.py](/music-downloader/src/url_to_path.py) | Python | 35 | 6 | 16 | 57 | +| [music-downloader/src/youtube_music.py](/music-downloader/src/youtube_music.py) | Python | 63 | 2 | 17 | 82 | + +[Summary](results.md) / Details / [Diff Summary](diff.md) / [Diff Details](diff-details.md) \ No newline at end of file diff --git a/.VSCodeCounter/2022-10-28_12-39-22/diff-details.md b/.VSCodeCounter/2022-10-28_12-39-22/diff-details.md new file mode 100644 index 0000000..40dc8b6 --- /dev/null +++ b/.VSCodeCounter/2022-10-28_12-39-22/diff-details.md @@ -0,0 +1,15 @@ +# Diff Details + +Date : 2022-10-28 12:39:22 + +Directory /home/lars/Projects/music-downloader + +Total : 0 files, 0 codes, 0 comments, 0 blanks, all 0 lines + +[Summary](results.md) / [Details](details.md) / [Diff Summary](diff.md) / Diff Details + +## Files +| filename | language | code | comment | blank | total | +| :--- | :--- | ---: | ---: | ---: | ---: | + +[Summary](results.md) / [Details](details.md) / [Diff Summary](diff.md) / Diff Details \ No newline at end of file diff --git a/.VSCodeCounter/2022-10-28_12-39-22/diff.csv b/.VSCodeCounter/2022-10-28_12-39-22/diff.csv new file mode 100644 index 0000000..b7d8d75 --- /dev/null +++ b/.VSCodeCounter/2022-10-28_12-39-22/diff.csv @@ -0,0 +1,2 @@ +"filename", "language", "", "comment", "blank", "total" +"Total", "-", , 0, 0, 0 \ No newline at end of file diff --git a/.VSCodeCounter/2022-10-28_12-39-22/diff.md b/.VSCodeCounter/2022-10-28_12-39-22/diff.md new file mode 100644 index 0000000..70d009c --- /dev/null +++ b/.VSCodeCounter/2022-10-28_12-39-22/diff.md @@ -0,0 +1,19 @@ +# Diff Summary + +Date : 2022-10-28 12:39:22 + +Directory /home/lars/Projects/music-downloader + +Total : 0 files, 0 codes, 0 comments, 0 blanks, all 0 lines + +[Summary](results.md) / [Details](details.md) / Diff Summary / [Diff Details](diff-details.md) + +## Languages +| language | files | code | comment | blank | total | +| :--- | ---: | ---: | ---: | ---: | ---: | + +## Directories +| path | files | code | comment | blank | total | +| :--- | ---: | ---: | ---: | ---: | ---: | + +[Summary](results.md) / [Details](details.md) / Diff Summary / [Diff Details](diff-details.md) \ No newline at end of file diff --git a/.VSCodeCounter/2022-10-28_12-39-22/diff.txt b/.VSCodeCounter/2022-10-28_12-39-22/diff.txt new file mode 100644 index 0000000..d35f11d --- /dev/null +++ b/.VSCodeCounter/2022-10-28_12-39-22/diff.txt @@ -0,0 +1,22 @@ +Date : 2022-10-28 12:39:22 +Directory : /home/lars/Projects/music-downloader +Total : 0 files, 0 codes, 0 comments, 0 blanks, all 0 lines + +Languages ++----------+------------+------------+------------+------------+------------+ +| language | files | code | comment | blank | total | ++----------+------------+------------+------------+------------+------------+ ++----------+------------+------------+------------+------------+------------+ + +Directories ++------+------------+------------+------------+------------+------------+ +| path | files | code | comment | blank | total | ++------+------------+------------+------------+------------+------------+ ++------+------------+------------+------------+------------+------------+ + +Files ++----------+----------+------------+------------+------------+------------+ +| filename | language | code | comment | blank | total | ++----------+----------+------------+------------+------------+------------+ +| Total | | 0 | 0 | 0 | 0 | ++----------+----------+------------+------------+------------+------------+ \ No newline at end of file diff --git a/.VSCodeCounter/2022-10-28_12-39-22/results.csv b/.VSCodeCounter/2022-10-28_12-39-22/results.csv new file mode 100644 index 0000000..5996e22 --- /dev/null +++ b/.VSCodeCounter/2022-10-28_12-39-22/results.csv @@ -0,0 +1,24 @@ +"filename", "language", "Python", "XML", "Markdown", "pip requirements", "SQLite", "comment", "blank", "total" +"/home/lars/Projects/music-downloader/.idea/dataSources.xml", "XML", 0, 12, 0, 0, 0, 0, 0, 12 +"/home/lars/Projects/music-downloader/.idea/inspectionProfiles/profiles_settings.xml", "XML", 0, 6, 0, 0, 0, 0, 0, 6 +"/home/lars/Projects/music-downloader/.idea/misc.xml", "XML", 0, 4, 0, 0, 0, 0, 0, 4 +"/home/lars/Projects/music-downloader/.idea/modules.xml", "XML", 0, 8, 0, 0, 0, 0, 0, 8 +"/home/lars/Projects/music-downloader/.idea/music-downloader.iml", "XML", 0, 10, 0, 0, 0, 0, 0, 10 +"/home/lars/Projects/music-downloader/.idea/vcs.xml", "XML", 0, 6, 0, 0, 0, 0, 0, 6 +"/home/lars/Projects/music-downloader/README.md", "Markdown", 0, 0, 75, 0, 0, 0, 34, 109 +"/home/lars/Projects/music-downloader/requirements.txt", "pip requirements", 0, 0, 0, 8, 0, 0, 0, 8 +"/home/lars/Projects/music-downloader/src/download.py", "Python", 59, 0, 0, 0, 0, 10, 21, 90 +"/home/lars/Projects/music-downloader/src/download_links.py", "Python", 46, 0, 0, 0, 0, 3, 18, 67 +"/home/lars/Projects/music-downloader/src/main.py", "Python", 78, 0, 0, 0, 0, 0, 26, 104 +"/home/lars/Projects/music-downloader/src/metadata/database.py", "Python", 121, 0, 0, 0, 0, 35, 35, 191 +"/home/lars/Projects/music-downloader/src/metadata/database_structure.sql", "SQLite", 0, 0, 0, 0, 48, 0, 6, 54 +"/home/lars/Projects/music-downloader/src/metadata/download.py", "Python", 211, 0, 0, 0, 0, 33, 60, 304 +"/home/lars/Projects/music-downloader/src/metadata/metadata.py", "Python", 106, 0, 0, 0, 0, 9, 28, 143 +"/home/lars/Projects/music-downloader/src/metadata/object_handeling.py", "Python", 19, 0, 0, 0, 0, 0, 4, 23 +"/home/lars/Projects/music-downloader/src/metadata/options.py", "Python", 87, 0, 0, 0, 0, 8, 24, 119 +"/home/lars/Projects/music-downloader/src/musify.py", "Python", 97, 0, 0, 0, 0, 2, 37, 136 +"/home/lars/Projects/music-downloader/src/phonetic_compares.py", "Python", 15, 0, 0, 0, 0, 0, 8, 23 +"/home/lars/Projects/music-downloader/src/test.py", "Python", 18, 0, 0, 0, 0, 1, 6, 25 +"/home/lars/Projects/music-downloader/src/url_to_path.py", "Python", 35, 0, 0, 0, 0, 6, 16, 57 +"/home/lars/Projects/music-downloader/src/youtube_music.py", "Python", 63, 0, 0, 0, 0, 2, 17, 82 +"Total", "-", 955, 46, 75, 8, 48, 109, 340, 1581 \ No newline at end of file diff --git a/.VSCodeCounter/2022-10-28_12-39-22/results.json b/.VSCodeCounter/2022-10-28_12-39-22/results.json new file mode 100644 index 0000000..87e391e --- /dev/null +++ b/.VSCodeCounter/2022-10-28_12-39-22/results.json @@ -0,0 +1 @@ +{"file:///home/lars/Projects/music-downloader/src/main.py":{"language":"Python","code":78,"comment":0,"blank":26},"file:///home/lars/Projects/music-downloader/src/download.py":{"language":"Python","code":59,"comment":10,"blank":21},"file:///home/lars/Projects/music-downloader/src/download_links.py":{"language":"Python","code":46,"comment":3,"blank":18},"file:///home/lars/Projects/music-downloader/.idea/dataSources.xml":{"language":"XML","code":12,"comment":0,"blank":0},"file:///home/lars/Projects/music-downloader/src/youtube_music.py":{"language":"Python","code":63,"comment":2,"blank":17},"file:///home/lars/Projects/music-downloader/src/phonetic_compares.py":{"language":"Python","code":15,"comment":0,"blank":8},"file:///home/lars/Projects/music-downloader/src/musify.py":{"language":"Python","code":97,"comment":2,"blank":37},"file:///home/lars/Projects/music-downloader/src/url_to_path.py":{"language":"Python","code":35,"comment":6,"blank":16},"file:///home/lars/Projects/music-downloader/src/test.py":{"language":"Python","code":18,"comment":1,"blank":6},"file:///home/lars/Projects/music-downloader/README.md":{"language":"Markdown","code":75,"comment":0,"blank":34},"file:///home/lars/Projects/music-downloader/requirements.txt":{"language":"pip requirements","code":8,"comment":0,"blank":0},"file:///home/lars/Projects/music-downloader/src/metadata/object_handeling.py":{"language":"Python","code":19,"comment":0,"blank":4},"file:///home/lars/Projects/music-downloader/src/metadata/database.py":{"language":"Python","code":121,"comment":35,"blank":35},"file:///home/lars/Projects/music-downloader/src/metadata/metadata.py":{"language":"Python","code":106,"comment":9,"blank":28},"file:///home/lars/Projects/music-downloader/src/metadata/download.py":{"language":"Python","code":211,"comment":33,"blank":60},"file:///home/lars/Projects/music-downloader/.idea/modules.xml":{"language":"XML","code":8,"comment":0,"blank":0},"file:///home/lars/Projects/music-downloader/.idea/music-downloader.iml":{"language":"XML","code":10,"comment":0,"blank":0},"file:///home/lars/Projects/music-downloader/src/metadata/options.py":{"language":"Python","code":87,"comment":8,"blank":24},"file:///home/lars/Projects/music-downloader/src/metadata/database_structure.sql":{"language":"SQLite","code":48,"comment":0,"blank":6},"file:///home/lars/Projects/music-downloader/.idea/inspectionProfiles/profiles_settings.xml":{"language":"XML","code":6,"comment":0,"blank":0},"file:///home/lars/Projects/music-downloader/.idea/vcs.xml":{"language":"XML","code":6,"comment":0,"blank":0},"file:///home/lars/Projects/music-downloader/.idea/misc.xml":{"language":"XML","code":4,"comment":0,"blank":0}} \ No newline at end of file diff --git a/.VSCodeCounter/2022-10-28_12-39-22/results.md b/.VSCodeCounter/2022-10-28_12-39-22/results.md new file mode 100644 index 0000000..d1d74f1 --- /dev/null +++ b/.VSCodeCounter/2022-10-28_12-39-22/results.md @@ -0,0 +1,29 @@ +# Summary + +Date : 2022-10-28 12:39:22 + +Directory /home/lars/Projects/music-downloader + +Total : 22 files, 1132 codes, 109 comments, 340 blanks, all 1581 lines + +Summary / [Details](details.md) / [Diff Summary](diff.md) / [Diff Details](diff-details.md) + +## Languages +| language | files | code | comment | blank | total | +| :--- | ---: | ---: | ---: | ---: | ---: | +| Python | 13 | 955 | 109 | 300 | 1,364 | +| Markdown | 1 | 75 | 0 | 34 | 109 | +| SQLite | 1 | 48 | 0 | 6 | 54 | +| XML | 6 | 46 | 0 | 0 | 46 | +| pip requirements | 1 | 8 | 0 | 0 | 8 | + +## Directories +| path | files | code | comment | blank | total | +| :--- | ---: | ---: | ---: | ---: | ---: | +| . | 22 | 1,132 | 109 | 340 | 1,581 | +| .idea | 6 | 46 | 0 | 0 | 46 | +| .idea/inspectionProfiles | 1 | 6 | 0 | 0 | 6 | +| src | 14 | 1,003 | 109 | 306 | 1,418 | +| src/metadata | 6 | 592 | 85 | 157 | 834 | + +Summary / [Details](details.md) / [Diff Summary](diff.md) / [Diff Details](diff-details.md) \ No newline at end of file diff --git a/.VSCodeCounter/2022-10-28_12-39-22/results.txt b/.VSCodeCounter/2022-10-28_12-39-22/results.txt new file mode 100644 index 0000000..4ec0c84 --- /dev/null +++ b/.VSCodeCounter/2022-10-28_12-39-22/results.txt @@ -0,0 +1,54 @@ +Date : 2022-10-28 12:39:22 +Directory : /home/lars/Projects/music-downloader +Total : 22 files, 1132 codes, 109 comments, 340 blanks, all 1581 lines + +Languages ++------------------+------------+------------+------------+------------+------------+ +| language | files | code | comment | blank | total | ++------------------+------------+------------+------------+------------+------------+ +| Python | 13 | 955 | 109 | 300 | 1,364 | +| Markdown | 1 | 75 | 0 | 34 | 109 | +| SQLite | 1 | 48 | 0 | 6 | 54 | +| XML | 6 | 46 | 0 | 0 | 46 | +| pip requirements | 1 | 8 | 0 | 0 | 8 | ++------------------+------------+------------+------------+------------+------------+ + +Directories ++-------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ +| path | files | code | comment | blank | total | ++-------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ +| . | 22 | 1,132 | 109 | 340 | 1,581 | +| .idea | 6 | 46 | 0 | 0 | 46 | +| .idea/inspectionProfiles | 1 | 6 | 0 | 0 | 6 | +| src | 14 | 1,003 | 109 | 306 | 1,418 | +| src/metadata | 6 | 592 | 85 | 157 | 834 | ++-------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ + +Files ++-------------------------------------------------------------------------------------+------------------+------------+------------+------------+------------+ +| filename | language | code | comment | blank | total | ++-------------------------------------------------------------------------------------+------------------+------------+------------+------------+------------+ +| /home/lars/Projects/music-downloader/.idea/dataSources.xml | XML | 12 | 0 | 0 | 12 | +| /home/lars/Projects/music-downloader/.idea/inspectionProfiles/profiles_settings.xml | XML | 6 | 0 | 0 | 6 | +| /home/lars/Projects/music-downloader/.idea/misc.xml | XML | 4 | 0 | 0 | 4 | +| /home/lars/Projects/music-downloader/.idea/modules.xml | XML | 8 | 0 | 0 | 8 | +| /home/lars/Projects/music-downloader/.idea/music-downloader.iml | XML | 10 | 0 | 0 | 10 | +| /home/lars/Projects/music-downloader/.idea/vcs.xml | XML | 6 | 0 | 0 | 6 | +| /home/lars/Projects/music-downloader/README.md | Markdown | 75 | 0 | 34 | 109 | +| /home/lars/Projects/music-downloader/requirements.txt | pip requirements | 8 | 0 | 0 | 8 | +| /home/lars/Projects/music-downloader/src/download.py | Python | 59 | 10 | 21 | 90 | +| /home/lars/Projects/music-downloader/src/download_links.py | Python | 46 | 3 | 18 | 67 | +| /home/lars/Projects/music-downloader/src/main.py | Python | 78 | 0 | 26 | 104 | +| /home/lars/Projects/music-downloader/src/metadata/database.py | Python | 121 | 35 | 35 | 191 | +| /home/lars/Projects/music-downloader/src/metadata/database_structure.sql | SQLite | 48 | 0 | 6 | 54 | +| /home/lars/Projects/music-downloader/src/metadata/download.py | Python | 211 | 33 | 60 | 304 | +| /home/lars/Projects/music-downloader/src/metadata/metadata.py | Python | 106 | 9 | 28 | 143 | +| /home/lars/Projects/music-downloader/src/metadata/object_handeling.py | Python | 19 | 0 | 4 | 23 | +| /home/lars/Projects/music-downloader/src/metadata/options.py | Python | 87 | 8 | 24 | 119 | +| /home/lars/Projects/music-downloader/src/musify.py | Python | 97 | 2 | 37 | 136 | +| /home/lars/Projects/music-downloader/src/phonetic_compares.py | Python | 15 | 0 | 8 | 23 | +| /home/lars/Projects/music-downloader/src/test.py | Python | 18 | 1 | 6 | 25 | +| /home/lars/Projects/music-downloader/src/url_to_path.py | Python | 35 | 6 | 16 | 57 | +| /home/lars/Projects/music-downloader/src/youtube_music.py | Python | 63 | 2 | 17 | 82 | +| Total | | 1,132 | 109 | 340 | 1,581 | ++-------------------------------------------------------------------------------------+------------------+------------+------------+------------+------------+ \ No newline at end of file diff --git a/.VSCodeCounter/2022-11-07_11-51-29/details.md b/.VSCodeCounter/2022-11-07_11-51-29/details.md new file mode 100644 index 0000000..cb5a02e --- /dev/null +++ b/.VSCodeCounter/2022-11-07_11-51-29/details.md @@ -0,0 +1,43 @@ +# Details + +Date : 2022-11-07 11:51:29 + +Directory /home/lars/Projects/music-downloader + +Total : 28 files, 1366 codes, 127 comments, 388 blanks, all 1881 lines + +[Summary](results.md) / Details / [Diff Summary](diff.md) / [Diff Details](diff-details.md) + +## Files +| filename | language | code | comment | blank | total | +| :--- | :--- | ---: | ---: | ---: | ---: | +| [music-downloader/.VSCodeCounter/2022-10-28_12-39-22/details.md](/music-downloader/.VSCodeCounter/2022-10-28_12-39-22/details.md) | Markdown | 31 | 0 | 6 | 37 | +| [music-downloader/.VSCodeCounter/2022-10-28_12-39-22/diff-details.md](/music-downloader/.VSCodeCounter/2022-10-28_12-39-22/diff-details.md) | Markdown | 9 | 0 | 6 | 15 | +| [music-downloader/.VSCodeCounter/2022-10-28_12-39-22/diff.md](/music-downloader/.VSCodeCounter/2022-10-28_12-39-22/diff.md) | Markdown | 12 | 0 | 7 | 19 | +| [music-downloader/.VSCodeCounter/2022-10-28_12-39-22/results.json](/music-downloader/.VSCodeCounter/2022-10-28_12-39-22/results.json) | JSON | 1 | 0 | 0 | 1 | +| [music-downloader/.VSCodeCounter/2022-10-28_12-39-22/results.md](/music-downloader/.VSCodeCounter/2022-10-28_12-39-22/results.md) | Markdown | 22 | 0 | 7 | 29 | +| [music-downloader/.idea/dataSources.xml](/music-downloader/.idea/dataSources.xml) | XML | 12 | 0 | 0 | 12 | +| [music-downloader/.idea/inspectionProfiles/profiles_settings.xml](/music-downloader/.idea/inspectionProfiles/profiles_settings.xml) | XML | 6 | 0 | 0 | 6 | +| [music-downloader/.idea/misc.xml](/music-downloader/.idea/misc.xml) | XML | 4 | 0 | 0 | 4 | +| [music-downloader/.idea/modules.xml](/music-downloader/.idea/modules.xml) | XML | 8 | 0 | 0 | 8 | +| [music-downloader/.idea/music-downloader.iml](/music-downloader/.idea/music-downloader.iml) | XML | 10 | 0 | 0 | 10 | +| [music-downloader/.idea/vcs.xml](/music-downloader/.idea/vcs.xml) | XML | 6 | 0 | 0 | 6 | +| [music-downloader/README.md](/music-downloader/README.md) | Markdown | 76 | 0 | 35 | 111 | +| [music-downloader/assets/database_structure.sql](/music-downloader/assets/database_structure.sql) | SQLite | 51 | 0 | 6 | 57 | +| [music-downloader/requirements.txt](/music-downloader/requirements.txt) | pip requirements | 8 | 0 | 0 | 8 | +| [music-downloader/src/download.py](/music-downloader/src/download.py) | Python | 60 | 11 | 22 | 93 | +| [music-downloader/src/download_links.py](/music-downloader/src/download_links.py) | Python | 37 | 3 | 15 | 55 | +| [music-downloader/src/main.py](/music-downloader/src/main.py) | Python | 98 | 0 | 31 | 129 | +| [music-downloader/src/metadata/database.py](/music-downloader/src/metadata/database.py) | Python | 153 | 61 | 38 | 252 | +| [music-downloader/src/metadata/database_structure.sql](/music-downloader/src/metadata/database_structure.sql) | SQLite | 51 | 0 | 6 | 57 | +| [music-downloader/src/metadata/download.py](/music-downloader/src/metadata/download.py) | Python | 270 | 24 | 67 | 361 | +| [music-downloader/src/metadata/metadata.py](/music-downloader/src/metadata/metadata.py) | Python | 106 | 9 | 28 | 143 | +| [music-downloader/src/metadata/object_handeling.py](/music-downloader/src/metadata/object_handeling.py) | Python | 19 | 0 | 6 | 25 | +| [music-downloader/src/metadata/options.py](/music-downloader/src/metadata/options.py) | Python | 87 | 8 | 24 | 119 | +| [music-downloader/src/musify.py](/music-downloader/src/musify.py) | Python | 106 | 2 | 40 | 148 | +| [music-downloader/src/phonetic_compares.py](/music-downloader/src/phonetic_compares.py) | Python | 15 | 0 | 8 | 23 | +| [music-downloader/src/test.py](/music-downloader/src/test.py) | Python | 18 | 1 | 6 | 25 | +| [music-downloader/src/url_to_path.py](/music-downloader/src/url_to_path.py) | Python | 27 | 6 | 13 | 46 | +| [music-downloader/src/youtube_music.py](/music-downloader/src/youtube_music.py) | Python | 63 | 2 | 17 | 82 | + +[Summary](results.md) / Details / [Diff Summary](diff.md) / [Diff Details](diff-details.md) \ No newline at end of file diff --git a/.VSCodeCounter/2022-11-07_11-51-29/diff-details.md b/.VSCodeCounter/2022-11-07_11-51-29/diff-details.md new file mode 100644 index 0000000..d089907 --- /dev/null +++ b/.VSCodeCounter/2022-11-07_11-51-29/diff-details.md @@ -0,0 +1,31 @@ +# Diff Details + +Date : 2022-11-07 11:51:29 + +Directory /home/lars/Projects/music-downloader + +Total : 16 files, 234 codes, 18 comments, 48 blanks, all 300 lines + +[Summary](results.md) / [Details](details.md) / [Diff Summary](diff.md) / Diff Details + +## Files +| filename | language | code | comment | blank | total | +| :--- | :--- | ---: | ---: | ---: | ---: | +| [music-downloader/.VSCodeCounter/2022-10-28_12-39-22/details.md](/music-downloader/.VSCodeCounter/2022-10-28_12-39-22/details.md) | Markdown | 31 | 0 | 6 | 37 | +| [music-downloader/.VSCodeCounter/2022-10-28_12-39-22/diff-details.md](/music-downloader/.VSCodeCounter/2022-10-28_12-39-22/diff-details.md) | Markdown | 9 | 0 | 6 | 15 | +| [music-downloader/.VSCodeCounter/2022-10-28_12-39-22/diff.md](/music-downloader/.VSCodeCounter/2022-10-28_12-39-22/diff.md) | Markdown | 12 | 0 | 7 | 19 | +| [music-downloader/.VSCodeCounter/2022-10-28_12-39-22/results.json](/music-downloader/.VSCodeCounter/2022-10-28_12-39-22/results.json) | JSON | 1 | 0 | 0 | 1 | +| [music-downloader/.VSCodeCounter/2022-10-28_12-39-22/results.md](/music-downloader/.VSCodeCounter/2022-10-28_12-39-22/results.md) | Markdown | 22 | 0 | 7 | 29 | +| [music-downloader/README.md](/music-downloader/README.md) | Markdown | 1 | 0 | 1 | 2 | +| [music-downloader/assets/database_structure.sql](/music-downloader/assets/database_structure.sql) | SQLite | 51 | 0 | 6 | 57 | +| [music-downloader/src/download.py](/music-downloader/src/download.py) | Python | 1 | 1 | 1 | 3 | +| [music-downloader/src/download_links.py](/music-downloader/src/download_links.py) | Python | -9 | 0 | -3 | -12 | +| [music-downloader/src/main.py](/music-downloader/src/main.py) | Python | 20 | 0 | 5 | 25 | +| [music-downloader/src/metadata/database.py](/music-downloader/src/metadata/database.py) | Python | 32 | 26 | 3 | 61 | +| [music-downloader/src/metadata/database_structure.sql](/music-downloader/src/metadata/database_structure.sql) | SQLite | 3 | 0 | 0 | 3 | +| [music-downloader/src/metadata/download.py](/music-downloader/src/metadata/download.py) | Python | 59 | -9 | 7 | 57 | +| [music-downloader/src/metadata/object_handeling.py](/music-downloader/src/metadata/object_handeling.py) | Python | 0 | 0 | 2 | 2 | +| [music-downloader/src/musify.py](/music-downloader/src/musify.py) | Python | 9 | 0 | 3 | 12 | +| [music-downloader/src/url_to_path.py](/music-downloader/src/url_to_path.py) | Python | -8 | 0 | -3 | -11 | + +[Summary](results.md) / [Details](details.md) / [Diff Summary](diff.md) / Diff Details \ No newline at end of file diff --git a/.VSCodeCounter/2022-11-07_11-51-29/diff.csv b/.VSCodeCounter/2022-11-07_11-51-29/diff.csv new file mode 100644 index 0000000..dfd7279 --- /dev/null +++ b/.VSCodeCounter/2022-11-07_11-51-29/diff.csv @@ -0,0 +1,18 @@ +"filename", "language", "Python", "SQLite", "Markdown", "JSON", "comment", "blank", "total" +"/home/lars/Projects/music-downloader/.VSCodeCounter/2022-10-28_12-39-22/details.md", "Markdown", 0, 0, 31, 0, 0, 6, 37 +"/home/lars/Projects/music-downloader/.VSCodeCounter/2022-10-28_12-39-22/diff-details.md", "Markdown", 0, 0, 9, 0, 0, 6, 15 +"/home/lars/Projects/music-downloader/.VSCodeCounter/2022-10-28_12-39-22/diff.md", "Markdown", 0, 0, 12, 0, 0, 7, 19 +"/home/lars/Projects/music-downloader/.VSCodeCounter/2022-10-28_12-39-22/results.json", "JSON", 0, 0, 0, 1, 0, 0, 1 +"/home/lars/Projects/music-downloader/.VSCodeCounter/2022-10-28_12-39-22/results.md", "Markdown", 0, 0, 22, 0, 0, 7, 29 +"/home/lars/Projects/music-downloader/README.md", "Markdown", 0, 0, 1, 0, 0, 1, 2 +"/home/lars/Projects/music-downloader/assets/database_structure.sql", "SQLite", 0, 51, 0, 0, 0, 6, 57 +"/home/lars/Projects/music-downloader/src/download.py", "Python", 1, 0, 0, 0, 1, 1, 3 +"/home/lars/Projects/music-downloader/src/download_links.py", "Python", -9, 0, 0, 0, 0, -3, -12 +"/home/lars/Projects/music-downloader/src/main.py", "Python", 20, 0, 0, 0, 0, 5, 25 +"/home/lars/Projects/music-downloader/src/metadata/database.py", "Python", 32, 0, 0, 0, 26, 3, 61 +"/home/lars/Projects/music-downloader/src/metadata/database_structure.sql", "SQLite", 0, 3, 0, 0, 0, 0, 3 +"/home/lars/Projects/music-downloader/src/metadata/download.py", "Python", 59, 0, 0, 0, -9, 7, 57 +"/home/lars/Projects/music-downloader/src/metadata/object_handeling.py", "Python", 0, 0, 0, 0, 0, 2, 2 +"/home/lars/Projects/music-downloader/src/musify.py", "Python", 9, 0, 0, 0, 0, 3, 12 +"/home/lars/Projects/music-downloader/src/url_to_path.py", "Python", -8, 0, 0, 0, 0, -3, -11 +"Total", "-", 104, 54, 75, 1, 18, 48, 300 \ No newline at end of file diff --git a/.VSCodeCounter/2022-11-07_11-51-29/diff.md b/.VSCodeCounter/2022-11-07_11-51-29/diff.md new file mode 100644 index 0000000..16fafb6 --- /dev/null +++ b/.VSCodeCounter/2022-11-07_11-51-29/diff.md @@ -0,0 +1,29 @@ +# Diff Summary + +Date : 2022-11-07 11:51:29 + +Directory /home/lars/Projects/music-downloader + +Total : 16 files, 234 codes, 18 comments, 48 blanks, all 300 lines + +[Summary](results.md) / [Details](details.md) / Diff Summary / [Diff Details](diff-details.md) + +## Languages +| language | files | code | comment | blank | total | +| :--- | ---: | ---: | ---: | ---: | ---: | +| Python | 8 | 104 | 18 | 15 | 137 | +| Markdown | 5 | 75 | 0 | 27 | 102 | +| SQLite | 2 | 54 | 0 | 6 | 60 | +| JSON | 1 | 1 | 0 | 0 | 1 | + +## Directories +| path | files | code | comment | blank | total | +| :--- | ---: | ---: | ---: | ---: | ---: | +| . | 16 | 234 | 18 | 48 | 300 | +| .VSCodeCounter | 5 | 75 | 0 | 26 | 101 | +| .VSCodeCounter/2022-10-28_12-39-22 | 5 | 75 | 0 | 26 | 101 | +| assets | 1 | 51 | 0 | 6 | 57 | +| src | 9 | 107 | 18 | 15 | 140 | +| src/metadata | 4 | 94 | 17 | 12 | 123 | + +[Summary](results.md) / [Details](details.md) / Diff Summary / [Diff Details](diff-details.md) \ No newline at end of file diff --git a/.VSCodeCounter/2022-11-07_11-51-29/diff.txt b/.VSCodeCounter/2022-11-07_11-51-29/diff.txt new file mode 100644 index 0000000..46b6206 --- /dev/null +++ b/.VSCodeCounter/2022-11-07_11-51-29/diff.txt @@ -0,0 +1,48 @@ +Date : 2022-11-07 11:51:29 +Directory : /home/lars/Projects/music-downloader +Total : 16 files, 234 codes, 18 comments, 48 blanks, all 300 lines + +Languages ++----------+------------+------------+------------+------------+------------+ +| language | files | code | comment | blank | total | ++----------+------------+------------+------------+------------+------------+ +| Python | 8 | 104 | 18 | 15 | 137 | +| Markdown | 5 | 75 | 0 | 27 | 102 | +| SQLite | 2 | 54 | 0 | 6 | 60 | +| JSON | 1 | 1 | 0 | 0 | 1 | ++----------+------------+------------+------------+------------+------------+ + +Directories ++-----------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ +| path | files | code | comment | blank | total | ++-----------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ +| . | 16 | 234 | 18 | 48 | 300 | +| .VSCodeCounter | 5 | 75 | 0 | 26 | 101 | +| .VSCodeCounter/2022-10-28_12-39-22 | 5 | 75 | 0 | 26 | 101 | +| assets | 1 | 51 | 0 | 6 | 57 | +| src | 9 | 107 | 18 | 15 | 140 | +| src/metadata | 4 | 94 | 17 | 12 | 123 | ++-----------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ + +Files ++-----------------------------------------------------------------------------------------+----------+------------+------------+------------+------------+ +| filename | language | code | comment | blank | total | ++-----------------------------------------------------------------------------------------+----------+------------+------------+------------+------------+ +| /home/lars/Projects/music-downloader/.VSCodeCounter/2022-10-28_12-39-22/details.md | Markdown | 31 | 0 | 6 | 37 | +| /home/lars/Projects/music-downloader/.VSCodeCounter/2022-10-28_12-39-22/diff-details.md | Markdown | 9 | 0 | 6 | 15 | +| /home/lars/Projects/music-downloader/.VSCodeCounter/2022-10-28_12-39-22/diff.md | Markdown | 12 | 0 | 7 | 19 | +| /home/lars/Projects/music-downloader/.VSCodeCounter/2022-10-28_12-39-22/results.json | JSON | 1 | 0 | 0 | 1 | +| /home/lars/Projects/music-downloader/.VSCodeCounter/2022-10-28_12-39-22/results.md | Markdown | 22 | 0 | 7 | 29 | +| /home/lars/Projects/music-downloader/README.md | Markdown | 1 | 0 | 1 | 2 | +| /home/lars/Projects/music-downloader/assets/database_structure.sql | SQLite | 51 | 0 | 6 | 57 | +| /home/lars/Projects/music-downloader/src/download.py | Python | 1 | 1 | 1 | 3 | +| /home/lars/Projects/music-downloader/src/download_links.py | Python | -9 | 0 | -3 | -12 | +| /home/lars/Projects/music-downloader/src/main.py | Python | 20 | 0 | 5 | 25 | +| /home/lars/Projects/music-downloader/src/metadata/database.py | Python | 32 | 26 | 3 | 61 | +| /home/lars/Projects/music-downloader/src/metadata/database_structure.sql | SQLite | 3 | 0 | 0 | 3 | +| /home/lars/Projects/music-downloader/src/metadata/download.py | Python | 59 | -9 | 7 | 57 | +| /home/lars/Projects/music-downloader/src/metadata/object_handeling.py | Python | 0 | 0 | 2 | 2 | +| /home/lars/Projects/music-downloader/src/musify.py | Python | 9 | 0 | 3 | 12 | +| /home/lars/Projects/music-downloader/src/url_to_path.py | Python | -8 | 0 | -3 | -11 | +| Total | | 234 | 18 | 48 | 300 | ++-----------------------------------------------------------------------------------------+----------+------------+------------+------------+------------+ \ No newline at end of file diff --git a/.VSCodeCounter/2022-11-07_11-51-29/results.csv b/.VSCodeCounter/2022-11-07_11-51-29/results.csv new file mode 100644 index 0000000..0e9db89 --- /dev/null +++ b/.VSCodeCounter/2022-11-07_11-51-29/results.csv @@ -0,0 +1,30 @@ +"filename", "language", "Python", "SQLite", "XML", "pip requirements", "Markdown", "JSON", "comment", "blank", "total" +"/home/lars/Projects/music-downloader/.VSCodeCounter/2022-10-28_12-39-22/details.md", "Markdown", 0, 0, 0, 0, 31, 0, 0, 6, 37 +"/home/lars/Projects/music-downloader/.VSCodeCounter/2022-10-28_12-39-22/diff-details.md", "Markdown", 0, 0, 0, 0, 9, 0, 0, 6, 15 +"/home/lars/Projects/music-downloader/.VSCodeCounter/2022-10-28_12-39-22/diff.md", "Markdown", 0, 0, 0, 0, 12, 0, 0, 7, 19 +"/home/lars/Projects/music-downloader/.VSCodeCounter/2022-10-28_12-39-22/results.json", "JSON", 0, 0, 0, 0, 0, 1, 0, 0, 1 +"/home/lars/Projects/music-downloader/.VSCodeCounter/2022-10-28_12-39-22/results.md", "Markdown", 0, 0, 0, 0, 22, 0, 0, 7, 29 +"/home/lars/Projects/music-downloader/.idea/dataSources.xml", "XML", 0, 0, 12, 0, 0, 0, 0, 0, 12 +"/home/lars/Projects/music-downloader/.idea/inspectionProfiles/profiles_settings.xml", "XML", 0, 0, 6, 0, 0, 0, 0, 0, 6 +"/home/lars/Projects/music-downloader/.idea/misc.xml", "XML", 0, 0, 4, 0, 0, 0, 0, 0, 4 +"/home/lars/Projects/music-downloader/.idea/modules.xml", "XML", 0, 0, 8, 0, 0, 0, 0, 0, 8 +"/home/lars/Projects/music-downloader/.idea/music-downloader.iml", "XML", 0, 0, 10, 0, 0, 0, 0, 0, 10 +"/home/lars/Projects/music-downloader/.idea/vcs.xml", "XML", 0, 0, 6, 0, 0, 0, 0, 0, 6 +"/home/lars/Projects/music-downloader/README.md", "Markdown", 0, 0, 0, 0, 76, 0, 0, 35, 111 +"/home/lars/Projects/music-downloader/assets/database_structure.sql", "SQLite", 0, 51, 0, 0, 0, 0, 0, 6, 57 +"/home/lars/Projects/music-downloader/requirements.txt", "pip requirements", 0, 0, 0, 8, 0, 0, 0, 0, 8 +"/home/lars/Projects/music-downloader/src/download.py", "Python", 60, 0, 0, 0, 0, 0, 11, 22, 93 +"/home/lars/Projects/music-downloader/src/download_links.py", "Python", 37, 0, 0, 0, 0, 0, 3, 15, 55 +"/home/lars/Projects/music-downloader/src/main.py", "Python", 98, 0, 0, 0, 0, 0, 0, 31, 129 +"/home/lars/Projects/music-downloader/src/metadata/database.py", "Python", 153, 0, 0, 0, 0, 0, 61, 38, 252 +"/home/lars/Projects/music-downloader/src/metadata/database_structure.sql", "SQLite", 0, 51, 0, 0, 0, 0, 0, 6, 57 +"/home/lars/Projects/music-downloader/src/metadata/download.py", "Python", 270, 0, 0, 0, 0, 0, 24, 67, 361 +"/home/lars/Projects/music-downloader/src/metadata/metadata.py", "Python", 106, 0, 0, 0, 0, 0, 9, 28, 143 +"/home/lars/Projects/music-downloader/src/metadata/object_handeling.py", "Python", 19, 0, 0, 0, 0, 0, 0, 6, 25 +"/home/lars/Projects/music-downloader/src/metadata/options.py", "Python", 87, 0, 0, 0, 0, 0, 8, 24, 119 +"/home/lars/Projects/music-downloader/src/musify.py", "Python", 106, 0, 0, 0, 0, 0, 2, 40, 148 +"/home/lars/Projects/music-downloader/src/phonetic_compares.py", "Python", 15, 0, 0, 0, 0, 0, 0, 8, 23 +"/home/lars/Projects/music-downloader/src/test.py", "Python", 18, 0, 0, 0, 0, 0, 1, 6, 25 +"/home/lars/Projects/music-downloader/src/url_to_path.py", "Python", 27, 0, 0, 0, 0, 0, 6, 13, 46 +"/home/lars/Projects/music-downloader/src/youtube_music.py", "Python", 63, 0, 0, 0, 0, 0, 2, 17, 82 +"Total", "-", 1059, 102, 46, 8, 150, 1, 127, 388, 1881 \ No newline at end of file diff --git a/.VSCodeCounter/2022-11-07_11-51-29/results.json b/.VSCodeCounter/2022-11-07_11-51-29/results.json new file mode 100644 index 0000000..d531c5e --- /dev/null +++ b/.VSCodeCounter/2022-11-07_11-51-29/results.json @@ -0,0 +1 @@ +{"file:///home/lars/Projects/music-downloader/src/metadata/database.py":{"language":"Python","code":153,"comment":61,"blank":38},"file:///home/lars/Projects/music-downloader/src/url_to_path.py":{"language":"Python","code":27,"comment":6,"blank":13},"file:///home/lars/Projects/music-downloader/src/metadata/download.py":{"language":"Python","code":270,"comment":24,"blank":67},"file:///home/lars/Projects/music-downloader/src/metadata/metadata.py":{"language":"Python","code":106,"comment":9,"blank":28},"file:///home/lars/Projects/music-downloader/src/main.py":{"language":"Python","code":98,"comment":0,"blank":31},"file:///home/lars/Projects/music-downloader/src/metadata/object_handeling.py":{"language":"Python","code":19,"comment":0,"blank":6},"file:///home/lars/Projects/music-downloader/src/download.py":{"language":"Python","code":60,"comment":11,"blank":22},"file:///home/lars/Projects/music-downloader/src/test.py":{"language":"Python","code":18,"comment":1,"blank":6},"file:///home/lars/Projects/music-downloader/src/metadata/database_structure.sql":{"language":"SQLite","code":51,"comment":0,"blank":6},"file:///home/lars/Projects/music-downloader/src/metadata/options.py":{"language":"Python","code":87,"comment":8,"blank":24},"file:///home/lars/Projects/music-downloader/src/download_links.py":{"language":"Python","code":37,"comment":3,"blank":15},"file:///home/lars/Projects/music-downloader/assets/database_structure.sql":{"language":"SQLite","code":51,"comment":0,"blank":6},"file:///home/lars/Projects/music-downloader/src/youtube_music.py":{"language":"Python","code":63,"comment":2,"blank":17},"file:///home/lars/Projects/music-downloader/src/phonetic_compares.py":{"language":"Python","code":15,"comment":0,"blank":8},"file:///home/lars/Projects/music-downloader/src/musify.py":{"language":"Python","code":106,"comment":2,"blank":40},"file:///home/lars/Projects/music-downloader/.idea/dataSources.xml":{"language":"XML","code":12,"comment":0,"blank":0},"file:///home/lars/Projects/music-downloader/requirements.txt":{"language":"pip requirements","code":8,"comment":0,"blank":0},"file:///home/lars/Projects/music-downloader/README.md":{"language":"Markdown","code":76,"comment":0,"blank":35},"file:///home/lars/Projects/music-downloader/.VSCodeCounter/2022-10-28_12-39-22/diff-details.md":{"language":"Markdown","code":9,"comment":0,"blank":6},"file:///home/lars/Projects/music-downloader/.idea/music-downloader.iml":{"language":"XML","code":10,"comment":0,"blank":0},"file:///home/lars/Projects/music-downloader/.idea/vcs.xml":{"language":"XML","code":6,"comment":0,"blank":0},"file:///home/lars/Projects/music-downloader/.VSCodeCounter/2022-10-28_12-39-22/results.md":{"language":"Markdown","code":22,"comment":0,"blank":7},"file:///home/lars/Projects/music-downloader/.idea/misc.xml":{"language":"XML","code":4,"comment":0,"blank":0},"file:///home/lars/Projects/music-downloader/.idea/modules.xml":{"language":"XML","code":8,"comment":0,"blank":0},"file:///home/lars/Projects/music-downloader/.VSCodeCounter/2022-10-28_12-39-22/details.md":{"language":"Markdown","code":31,"comment":0,"blank":6},"file:///home/lars/Projects/music-downloader/.VSCodeCounter/2022-10-28_12-39-22/results.json":{"language":"JSON","code":1,"comment":0,"blank":0},"file:///home/lars/Projects/music-downloader/.VSCodeCounter/2022-10-28_12-39-22/diff.md":{"language":"Markdown","code":12,"comment":0,"blank":7},"file:///home/lars/Projects/music-downloader/.idea/inspectionProfiles/profiles_settings.xml":{"language":"XML","code":6,"comment":0,"blank":0}} \ No newline at end of file diff --git a/.VSCodeCounter/2022-11-07_11-51-29/results.md b/.VSCodeCounter/2022-11-07_11-51-29/results.md new file mode 100644 index 0000000..03d1839 --- /dev/null +++ b/.VSCodeCounter/2022-11-07_11-51-29/results.md @@ -0,0 +1,33 @@ +# Summary + +Date : 2022-11-07 11:51:29 + +Directory /home/lars/Projects/music-downloader + +Total : 28 files, 1366 codes, 127 comments, 388 blanks, all 1881 lines + +Summary / [Details](details.md) / [Diff Summary](diff.md) / [Diff Details](diff-details.md) + +## Languages +| language | files | code | comment | blank | total | +| :--- | ---: | ---: | ---: | ---: | ---: | +| Python | 13 | 1,059 | 127 | 315 | 1,501 | +| Markdown | 5 | 150 | 0 | 61 | 211 | +| SQLite | 2 | 102 | 0 | 12 | 114 | +| XML | 6 | 46 | 0 | 0 | 46 | +| pip requirements | 1 | 8 | 0 | 0 | 8 | +| JSON | 1 | 1 | 0 | 0 | 1 | + +## Directories +| path | files | code | comment | blank | total | +| :--- | ---: | ---: | ---: | ---: | ---: | +| . | 28 | 1,366 | 127 | 388 | 1,881 | +| .VSCodeCounter | 5 | 75 | 0 | 26 | 101 | +| .VSCodeCounter/2022-10-28_12-39-22 | 5 | 75 | 0 | 26 | 101 | +| .idea | 6 | 46 | 0 | 0 | 46 | +| .idea/inspectionProfiles | 1 | 6 | 0 | 0 | 6 | +| assets | 1 | 51 | 0 | 6 | 57 | +| src | 14 | 1,110 | 127 | 321 | 1,558 | +| src/metadata | 6 | 686 | 102 | 169 | 957 | + +Summary / [Details](details.md) / [Diff Summary](diff.md) / [Diff Details](diff-details.md) \ No newline at end of file diff --git a/.VSCodeCounter/2022-11-07_11-51-29/results.txt b/.VSCodeCounter/2022-11-07_11-51-29/results.txt new file mode 100644 index 0000000..7a20f12 --- /dev/null +++ b/.VSCodeCounter/2022-11-07_11-51-29/results.txt @@ -0,0 +1,64 @@ +Date : 2022-11-07 11:51:29 +Directory : /home/lars/Projects/music-downloader +Total : 28 files, 1366 codes, 127 comments, 388 blanks, all 1881 lines + +Languages ++------------------+------------+------------+------------+------------+------------+ +| language | files | code | comment | blank | total | ++------------------+------------+------------+------------+------------+------------+ +| Python | 13 | 1,059 | 127 | 315 | 1,501 | +| Markdown | 5 | 150 | 0 | 61 | 211 | +| SQLite | 2 | 102 | 0 | 12 | 114 | +| XML | 6 | 46 | 0 | 0 | 46 | +| pip requirements | 1 | 8 | 0 | 0 | 8 | +| JSON | 1 | 1 | 0 | 0 | 1 | ++------------------+------------+------------+------------+------------+------------+ + +Directories ++-----------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ +| path | files | code | comment | blank | total | ++-----------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ +| . | 28 | 1,366 | 127 | 388 | 1,881 | +| .VSCodeCounter | 5 | 75 | 0 | 26 | 101 | +| .VSCodeCounter/2022-10-28_12-39-22 | 5 | 75 | 0 | 26 | 101 | +| .idea | 6 | 46 | 0 | 0 | 46 | +| .idea/inspectionProfiles | 1 | 6 | 0 | 0 | 6 | +| assets | 1 | 51 | 0 | 6 | 57 | +| src | 14 | 1,110 | 127 | 321 | 1,558 | +| src/metadata | 6 | 686 | 102 | 169 | 957 | ++-----------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ + +Files ++-----------------------------------------------------------------------------------------+------------------+------------+------------+------------+------------+ +| filename | language | code | comment | blank | total | ++-----------------------------------------------------------------------------------------+------------------+------------+------------+------------+------------+ +| /home/lars/Projects/music-downloader/.VSCodeCounter/2022-10-28_12-39-22/details.md | Markdown | 31 | 0 | 6 | 37 | +| /home/lars/Projects/music-downloader/.VSCodeCounter/2022-10-28_12-39-22/diff-details.md | Markdown | 9 | 0 | 6 | 15 | +| /home/lars/Projects/music-downloader/.VSCodeCounter/2022-10-28_12-39-22/diff.md | Markdown | 12 | 0 | 7 | 19 | +| /home/lars/Projects/music-downloader/.VSCodeCounter/2022-10-28_12-39-22/results.json | JSON | 1 | 0 | 0 | 1 | +| /home/lars/Projects/music-downloader/.VSCodeCounter/2022-10-28_12-39-22/results.md | Markdown | 22 | 0 | 7 | 29 | +| /home/lars/Projects/music-downloader/.idea/dataSources.xml | XML | 12 | 0 | 0 | 12 | +| /home/lars/Projects/music-downloader/.idea/inspectionProfiles/profiles_settings.xml | XML | 6 | 0 | 0 | 6 | +| /home/lars/Projects/music-downloader/.idea/misc.xml | XML | 4 | 0 | 0 | 4 | +| /home/lars/Projects/music-downloader/.idea/modules.xml | XML | 8 | 0 | 0 | 8 | +| /home/lars/Projects/music-downloader/.idea/music-downloader.iml | XML | 10 | 0 | 0 | 10 | +| /home/lars/Projects/music-downloader/.idea/vcs.xml | XML | 6 | 0 | 0 | 6 | +| /home/lars/Projects/music-downloader/README.md | Markdown | 76 | 0 | 35 | 111 | +| /home/lars/Projects/music-downloader/assets/database_structure.sql | SQLite | 51 | 0 | 6 | 57 | +| /home/lars/Projects/music-downloader/requirements.txt | pip requirements | 8 | 0 | 0 | 8 | +| /home/lars/Projects/music-downloader/src/download.py | Python | 60 | 11 | 22 | 93 | +| /home/lars/Projects/music-downloader/src/download_links.py | Python | 37 | 3 | 15 | 55 | +| /home/lars/Projects/music-downloader/src/main.py | Python | 98 | 0 | 31 | 129 | +| /home/lars/Projects/music-downloader/src/metadata/database.py | Python | 153 | 61 | 38 | 252 | +| /home/lars/Projects/music-downloader/src/metadata/database_structure.sql | SQLite | 51 | 0 | 6 | 57 | +| /home/lars/Projects/music-downloader/src/metadata/download.py | Python | 270 | 24 | 67 | 361 | +| /home/lars/Projects/music-downloader/src/metadata/metadata.py | Python | 106 | 9 | 28 | 143 | +| /home/lars/Projects/music-downloader/src/metadata/object_handeling.py | Python | 19 | 0 | 6 | 25 | +| /home/lars/Projects/music-downloader/src/metadata/options.py | Python | 87 | 8 | 24 | 119 | +| /home/lars/Projects/music-downloader/src/musify.py | Python | 106 | 2 | 40 | 148 | +| /home/lars/Projects/music-downloader/src/phonetic_compares.py | Python | 15 | 0 | 8 | 23 | +| /home/lars/Projects/music-downloader/src/test.py | Python | 18 | 1 | 6 | 25 | +| /home/lars/Projects/music-downloader/src/url_to_path.py | Python | 27 | 6 | 13 | 46 | +| /home/lars/Projects/music-downloader/src/youtube_music.py | Python | 63 | 2 | 17 | 82 | +| Total | | 1,366 | 127 | 388 | 1,881 | ++-----------------------------------------------------------------------------------------+------------------+------------+------------+------------+------------+ \ No newline at end of file diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml new file mode 100644 index 0000000..879cd05 --- /dev/null +++ b/.idea/dataSources.xml @@ -0,0 +1,12 @@ + + + + + sqlite.xerial + true + org.sqlite.JDBC + jdbc:sqlite:/tmp/music-downloader/metadata.db + $ProjectFileDir$ + + + \ No newline at end of file diff --git a/README.md b/README.md index 01df802..16d1c88 100644 --- a/README.md +++ b/README.md @@ -103,3 +103,35 @@ There are two bottlenecks with this approach though: **TODO** - look at how the isrc id derived an try to generate it for the tracks without directly getting it from mb. +<<<<<<< HEAD +======= + +**Progress** +- There is a great site whith a huge isrc database [https://isrc.soundexchange.com/](https://isrc.soundexchange.com/). + + +https://slavart.gamesdrive.net/ +https://getmetal.club/ +https://newalbumreleases.net/ +http://download-soundtracks.com/ +https://scnlog.me/ +https://intmusic.net/ +https://www.pluspremieres.ws/ +https://music4newgen.org/ +https://takemetal.org/ +https://coreradio.ru/ +https://alterportal.net/ +https://vk.com/mdcore +https://vk.com/mdrock +https://sophiesfloorboard.blogspot.com/ +https://funkysouls.org/ +https://www.deadpulpit.com/ +https://vk.com/filter_rock +https://en.metal-tracker.com/ +https://thelastdisaster.org/ +https://vk.com/phc +https://free-mp3-download.net/ requires recaptcha +https://vk.com/filter_rock +https://t.me/ffilternews telegram? +https://justanothermusic.site/index.php requires login +>>>>>>> 63f30bffbae20ec3fc368a6093b28e56f0230318 diff --git a/assets/database_structure.sql b/assets/database_structure.sql new file mode 100644 index 0000000..d5262c7 --- /dev/null +++ b/assets/database_structure.sql @@ -0,0 +1,56 @@ +DROP TABLE IF EXISTS artist; +CREATE TABLE artist ( + id TEXT PRIMARY KEY NOT NULL, + name TEXT +); + +DROP TABLE IF EXISTS artist_release_group; +CREATE TABLE artist_release_group ( + artist_id TEXT NOT NULL, + release_group_id TEXT NOT NULL +); + +DROP TABLE IF EXISTS artist_track; +CREATE TABLE artist_track ( + artist_id TEXT NOT NULL, + track_id TEXT NOT NULL +); + +DROP TABLE IF EXISTS release_group; +CREATE TABLE release_group ( + id TEXT PRIMARY KEY NOT NULL, + albumartist TEXT, + albumsort INT, + musicbrainz_albumtype TEXT, + compilation TEXT, + album_artist_id TEXT +); + +DROP TABLE IF EXISTS release_; +CREATE TABLE release_ ( + id TEXT PRIMARY KEY NOT NULL, + release_group_id TEXT NOT NULL, + title TEXT, + copyright TEXT, + album_status TEXT, + language TEXT, + year TEXT, + date TEXT, + country TEXT, + barcode TEXT +); + +DROP TABLE IF EXISTS track; +CREATE TABLE track ( + id TEXT PRIMARY KEY NOT NULL, + downloaded BOOLEAN NOT NULL DEFAULT 0, + release_id TEXT NOT NULL, + track TEXT, + tracknumber TEXT, + isrc TEXT, + genre TEXT, + path TEXT, + file TEXT, + url TEXT, + src TEXT +); diff --git a/src/download.py b/src/download.py index 6f1d185..309de53 100644 --- a/src/download.py +++ b/src/download.py @@ -1,14 +1,11 @@ import mutagen.id3 import requests import os.path -import pandas as pd from mutagen.easyid3 import EasyID3 from pydub import AudioSegment -import json import logging -import musify -import youtube_music +from scraping import musify, youtube_music """ https://en.wikipedia.org/wiki/ID3 @@ -16,40 +13,13 @@ https://mutagen.readthedocs.io/en/latest/user/id3.html # to get all valid keys from mutagen.easyid3 import EasyID3 +print("\n".join(EasyID3.valid_keys.keys())) print(EasyID3.valid_keys.keys()) """ -def write_metadata(row, file_path): - # only convert the file to the proper format if mutagen doesn't work with it due to time - try: - audiofile = EasyID3(file_path) - except mutagen.id3.ID3NoHeaderError: - AudioSegment.from_file(file_path).export(file_path, format="mp3") - audiofile = EasyID3(file_path) - - valid_keys = list(EasyID3.valid_keys.keys()) - - for key in list(row.keys()): - if type(row[key]) == list or key in valid_keys and not pd.isna(row[key]): - if type(row[key]) == int or type(row[key]) == float: - row[key] = str(row[key]) - audiofile[key] = row[key] - - logging.info("saving") - audiofile.save(file_path, v1=2) - - -def path_stuff(path: str, file_: str): - # returns true if it shouldn't be downloaded - if os.path.exists(file_): - logging.info(f"'{file_}' does already exist, thus not downloading.") - return True - os.makedirs(path, exist_ok=True) - return False - - class Download: +<<<<<<< HEAD def __init__(self, session: requests.Session = requests.Session(), file: str = ".cache3.csv", temp: str = "temp", base_path: str = ""): self.session = session @@ -59,16 +29,22 @@ class Download: } self.temp = temp self.file = file +======= + def __init__(self, database, logger: logging.Logger, proxies: dict = None, base_path: str = ""): + if proxies is not None: + musify.set_proxy(proxies) - self.dataframe = pd.read_csv(os.path.join(self.temp, self.file), index_col=0) + self.database = database + self.logger = logger +>>>>>>> 63f30bffbae20ec3fc368a6093b28e56f0230318 - for idx, row in self.dataframe.iterrows(): - row['artist'] = json.loads(row['artist'].replace("'", '"')) + for row in database.get_tracks_to_download(): + row['artist'] = [i['name'] for i in row['artists']] row['file'] = os.path.join(base_path, row['file']) row['path'] = os.path.join(base_path, row['path']) - if path_stuff(row['path'], row['file']): - write_metadata(row, row['file']) + if self.path_stuff(row['path'], row['file']): + self.write_metadata(row, row['file']) continue download_success = None @@ -79,10 +55,41 @@ class Download: download_success = youtube_music.download(row) if download_success == -1: - logging.warning(f"couldn't download {row.url} from {row.src}") + self.logger.warning(f"couldn't download {row['url']} from {row['src']}") continue - write_metadata(row, row['file']) + self.write_metadata(row, row['file']) + + def write_metadata(self, row, file_path): + if not os.path.exists(file_path): + self.logger.warning("something went really wrong") + return False + + # only convert the file to the proper format if mutagen doesn't work with it due to time + try: + audiofile = EasyID3(file_path) + except mutagen.id3.ID3NoHeaderError: + AudioSegment.from_file(file_path).export(file_path, format="mp3") + audiofile = EasyID3(file_path) + + valid_keys = list(EasyID3.valid_keys.keys()) + + for key in list(row.keys()): + if key in valid_keys and row[key] is not None: + if type(row[key]) != list: + row[key] = str(row[key]) + audiofile[key] = row[key] + + self.logger.info("saving") + audiofile.save(file_path, v1=2) + + def path_stuff(self, path: str, file_: str): + # returns true if it shouldn't be downloaded + if os.path.exists(file_): + self.logger.info(f"'{file_}' does already exist, thus not downloading.") + return True + os.makedirs(path, exist_ok=True) + return False if __name__ == "__main__": diff --git a/src/download_links.py b/src/download_links.py index 78e7178..db50bc1 100644 --- a/src/download_links.py +++ b/src/download_links.py @@ -1,55 +1,57 @@ -import json -import os.path -import pandas as pd import requests +import os import logging -import musify -import youtube_music +from scraping import musify, youtube_music class Download: +<<<<<<< HEAD def __init__(self, metadata_csv: str = ".cache1.csv", session: requests.Session = requests.Session(), file: str = ".cache2.csv", temp: str = "temp") -> None: self.temp = temp self.metadata = pd.read_csv(os.path.join(self.temp, metadata_csv), index_col=0) +======= + def __init__(self, database, logger: logging.Logger, music_dir: str, proxies: dict = None) -> None: + self.music_dir = music_dir + self.database = database + self.logger = logger + if proxies is not None: + musify.set_proxy(proxies) +>>>>>>> 63f30bffbae20ec3fc368a6093b28e56f0230318 self.urls = [] - for idx, row in self.metadata.iterrows(): - row['artist'] = json.loads(row['artist'].replace("'", '"')) + for row in self.database.get_tracks_without_src(): + row['artists'] = [artist['name'] for artist in row['artists']] - # check musify - musify_url = musify.get_musify_url(row) - if musify_url is not None: - self.add_url(musify_url, 'musify', dict(row)) + id_ = row['id'] + if os.path.exists(os.path.join(self.music_dir, row['file'])): + self.logger.info(f"skipping the fetching of the download links, cuz {row['file']} already exists.") continue # check YouTube youtube_url = youtube_music.get_youtube_url(row) if youtube_url is not None: - self.add_url(youtube_url, 'youtube', dict(row)) + self.add_url(youtube_url, 'youtube', id_) + continue + + # check musify + musify_url = musify.get_musify_url(row) + if musify_url is not None: + self.add_url(musify_url, 'musify', id_) continue # check musify again, but with a different methode that takes longer musify_url = musify.get_musify_url_slow(row) if musify_url is not None: - self.add_url(musify_url, 'musify', dict(row)) + self.add_url(musify_url, 'musify', id_) continue - logging.warning(f"Didn't find any sources for {row['title']}") + self.logger.warning(f"Didn't find any sources for {row['title']}") - self.dump_urls(file) - - def add_url(self, url: str, src: str, row: dict): - row['url'] = url - row['src'] = src - - self.urls.append(row) - - def dump_urls(self, file: str = ".cache2.csv"): - df = pd.DataFrame(self.urls) - df.to_csv(os.path.join(self.temp, file)) + def add_url(self, url: str, src: str, id_: str): + self.database.set_download_data(id_, url, src) if __name__ == "__main__": @@ -60,4 +62,4 @@ if __name__ == "__main__": s = requests.Session() s.proxies = proxies - download = Download(session=s) + download = Download() diff --git a/src/main.py b/src/main.py index c92a055..736b049 100644 --- a/src/main.py +++ b/src/main.py @@ -1,4 +1,11 @@ +<<<<<<< HEAD import metadata +======= +from metadata.database import Database +from metadata.download import MetadataDownloader +import metadata.download +import metadata.search +>>>>>>> 63f30bffbae20ec3fc368a6093b28e56f0230318 import download_links import url_to_path import download @@ -6,18 +13,40 @@ import download import logging import requests import os +import tempfile +logging.basicConfig(level=logging.INFO) -TEMP = "temp" -STEP_ONE_CACHE = ".cache1.csv" -STEP_TWO_CACHE = ".cache2.csv" -STEP_THREE_CACHE = ".cache3.csv" +TEMP_FOLDER = "music-downloader" +DATABASE_FILE = "metadata.db" +DATABASE_STRUCTURE_FILE = "database_structure.sql" +DATABASE_STRUCTURE_FALLBACK = "https://raw.githubusercontent.com/HeIIow2/music-downloader/new_metadata/assets/database_structure.sql" + +SEARCH_LOGGER = logging.getLogger("mb-cli") +DATABASE_LOGGER = logging.getLogger("database") +METADATA_DOWNLOAD_LOGGER = logging.getLogger("metadata-download") +URL_DOWNLOAD_LOGGER = logging.getLogger("ling-download") +PATH_LOGGER = logging.getLogger("create-paths") +DOWNLOAD_LOGGER = logging.getLogger("download") NOT_A_GENRE = ".", "..", "misc_scripts", "Music", "script", ".git", ".idea" MUSIC_DIR = os.path.expanduser('~/Music') TOR = False +<<<<<<< HEAD +======= + +temp_dir = os.path.join(tempfile.gettempdir(), TEMP_FOLDER) +if not os.path.exists(temp_dir): + os.mkdir(temp_dir) + +database = Database(os.path.join(temp_dir, DATABASE_FILE), + os.path.join(temp_dir, DATABASE_STRUCTURE_FILE), + DATABASE_STRUCTURE_FALLBACK, + DATABASE_LOGGER, + reset_anyways=True) + +>>>>>>> 63f30bffbae20ec3fc368a6093b28e56f0230318 -logging.basicConfig(level=logging.INFO) def get_existing_genre(): @@ -29,28 +58,34 @@ def get_existing_genre(): return valid_directories +<<<<<<< HEAD def search_for_metadata(query: str): search = metadata.Search(query=query, temp=TEMP) +======= +def search_for_metadata(): + search = metadata.search.Search(logger=SEARCH_LOGGER) +>>>>>>> 63f30bffbae20ec3fc368a6093b28e56f0230318 - print(search.options) while True: input_ = input( - "q to quit, ok to download, .. for previous options, . for current options, int for this element: ").lower() + "q to quit, .. for previous options, int for this element, str to search for query, ok to download\n") input_.strip() - if input_ == "q": - exit(0) - if input_ == "ok": - return search - if input_ == ".": - print(search.options) - continue - if input_ == "..": + if input_.lower() == "ok": + break + if input_.lower() == "q": + break + if input_.lower() == "..": + print() print(search.get_previous_options()) continue if input_.isdigit(): + print() print(search.choose(int(input_))) continue + print() + print(search.search_from_query(input_)) + return search.current_option def get_genre(): existing_genres = get_existing_genre() @@ -83,21 +118,31 @@ def cli(start_at: int = 0): logging.info(f"{genre} has been set as genre.") if start_at <= 0: - search = search_for_metadata(query=input("initial query: ")) + search = search_for_metadata() logging.info("Starting Downloading of metadata") - search.download(file=STEP_ONE_CACHE) + metadata_downloader = MetadataDownloader(database, METADATA_DOWNLOAD_LOGGER) + metadata_downloader.download(search) if start_at <= 1: +<<<<<<< HEAD logging.info("Fetching Download Links") download_links.Download(file=STEP_TWO_CACHE, metadata_csv=STEP_ONE_CACHE, temp=TEMP, session=session) +======= + logging.info("creating Paths") + url_to_path.UrlPath(database, PATH_LOGGER, genre=genre) +>>>>>>> 63f30bffbae20ec3fc368a6093b28e56f0230318 if start_at <= 2: - logging.info("creating Paths") - url_to_path.UrlPath(genre=genre) + logging.info("Fetching Download Links") + download_links.Download(database, METADATA_DOWNLOAD_LOGGER, MUSIC_DIR, proxies=proxies) if start_at <= 3: logging.info("starting to download the mp3's") +<<<<<<< HEAD download.Download(session=session, file=STEP_THREE_CACHE, temp=TEMP, base_path=MUSIC_DIR) +======= + download.Download(database, DOWNLOAD_LOGGER, proxies=proxies, base_path=MUSIC_DIR) +>>>>>>> 63f30bffbae20ec3fc368a6093b28e56f0230318 if __name__ == "__main__": diff --git a/src/metadata/database.py b/src/metadata/database.py new file mode 100644 index 0000000..f10ef22 --- /dev/null +++ b/src/metadata/database.py @@ -0,0 +1,251 @@ +import sqlite3 +import os +import logging +import json +import requests + + +class Database: + def __init__(self, path_to_db: str, db_structure: str, db_structure_fallback: str, logger: logging.Logger, reset_anyways: bool = False): + self.logger = logger + self.path_to_db = path_to_db + + self.connection = sqlite3.connect(self.path_to_db) + self.cursor = self.connection.cursor() + + # init database + self.init_db(database_structure=db_structure, database_structure_fallback=db_structure_fallback, reset_anyways=reset_anyways) + + def init_db(self, database_structure: str, database_structure_fallback: str, reset_anyways: bool = False): + # check if db exists + exists = True + try: + query = 'SELECT * FROM track;' + self.cursor.execute(query) + _ = self.cursor.fetchall() + except sqlite3.OperationalError: + exists = False + + if not exists: + self.logger.info("Database does not exist yet.") + + if reset_anyways or not exists: + # reset the database if reset_anyways is true or if an error has been thrown previously. + self.logger.info("Creating/Reseting Database.") + + if not os.path.exists(database_structure): + self.logger.info("database structure file doesn't exist yet, fetching from github") + r = requests.get(database_structure_fallback) + + with open(database_structure, "w") as f: + f.write(r.text) + + # read the file + with open(database_structure, "r") as database_structure_file: + query = database_structure_file.read() + self.cursor.executescript(query) + self.connection.commit() + + def add_artist( + self, + musicbrainz_artistid: str, + artist: str = None + ): + query = "INSERT OR REPLACE INTO artist (id, name) VALUES (?, ?);" + values = musicbrainz_artistid, artist + + self.cursor.execute(query, values) + self.connection.commit() + + def add_release_group( + self, + musicbrainz_releasegroupid: str, + artist_ids: list, + albumartist: str = None, + albumsort: int = None, + musicbrainz_albumtype: str = None, + compilation: str = None, + album_artist_id: str = None + ): + # add adjacency + adjacency_list = [] + for artist_id in artist_ids: + adjacency_list.append((artist_id, musicbrainz_releasegroupid)) + adjacency_values = tuple(adjacency_list) + adjacency_query = "INSERT OR REPLACE INTO artist_release_group (artist_id, release_group_id) VALUES (?, ?);" + self.cursor.executemany(adjacency_query, adjacency_values) + self.connection.commit() + + # add release group + query = "INSERT OR REPLACE INTO release_group (id, albumartist, albumsort, musicbrainz_albumtype, compilation, album_artist_id) VALUES (?, ?, ?, ?, ?, ?);" + values = musicbrainz_releasegroupid, albumartist, albumsort, musicbrainz_albumtype, compilation, album_artist_id + self.cursor.execute(query, values) + self.connection.commit() + + def add_release( + self, + musicbrainz_albumid: str, + release_group_id: str, + title: str = None, + copyright_: str = None, + album_status: str = None, + language: str = None, + year: str = None, + date: str = None, + country: str = None, + barcode: str = None + ): + query = "INSERT OR REPLACE INTO release_ (id, release_group_id, title, copyright, album_status, language, year, date, country, barcode) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);" + values = musicbrainz_albumid, release_group_id, title, copyright_, album_status, language, year, date, country, barcode + + self.cursor.execute(query, values) + self.connection.commit() + + def add_track( + self, + musicbrainz_releasetrackid: str, + musicbrainz_albumid: str, + feature_aritsts: list, + tracknumber: str = None, + track: str = None, + isrc: str = None + ): + # add adjacency + adjacency_list = [] + for artist_id in feature_aritsts: + adjacency_list.append((artist_id, musicbrainz_releasetrackid)) + adjacency_values = tuple(adjacency_list) + adjacency_query = "INSERT OR REPLACE INTO artist_track (artist_id, track_id) VALUES (?, ?);" + self.cursor.executemany(adjacency_query, adjacency_values) + self.connection.commit() + + # add track + query = "INSERT OR REPLACE INTO track (id, release_id, track, isrc, tracknumber) VALUES (?, ?, ?, ?, ?);" + values = musicbrainz_releasetrackid, musicbrainz_albumid, track, isrc, tracknumber + self.cursor.execute(query, values) + self.connection.commit() + + @staticmethod + def get_custom_track_query(custom_where: list) -> str: + where_args = [ + "track.release_id == release_.id", + "release_group.id == release_.release_group_id", + "artist_track.artist_id == artist.id", + "artist_track.track_id == track.id" + ] + where_args.extend(custom_where) + + where_arg = " AND ".join(where_args) + query = f""" +SELECT DISTINCT + json_object( + 'artists', json_group_array( + ( + SELECT DISTINCT json_object( + 'id', artist.id, + 'name', artist.name + ) + ) + ), + 'id', track.id, + 'tracknumber', track.tracknumber, + 'titlesort ', track.tracknumber, + 'musicbrainz_releasetrackid', track.id, + 'musicbrainz_albumid', release_.id, + 'title', track.track, + 'isrc', track.isrc, + 'album', release_.title, + 'copyright', release_.copyright, + 'album_status', release_.album_status, + 'language', release_.language, + 'year', release_.year, + 'date', release_.date, + 'country', release_.country, + 'barcode', release_.barcode, + 'albumartist', release_group.albumartist, + 'albumsort', release_group.albumsort, + 'musicbrainz_albumtype', release_group.musicbrainz_albumtype, + 'compilation', release_group.compilation, + 'album_artist_id', release_group.album_artist_id, + 'path', track.path, + 'file', track.file, + 'genre', track.genre, + 'url', track.url, + 'src', track.src + ) +FROM track, release_, release_group,artist, artist_track +WHERE + {where_arg} +GROUP BY track.id; + """ + return query + + def get_custom_track(self, custom_where: list): + query = Database.get_custom_track_query(custom_where=custom_where) + return [json.loads(i[0]) for i in self.cursor.execute(query)] + + def get_track_metadata(self, musicbrainz_releasetrackid: str): + # this would be vulnerable if musicbrainz_releasetrackid would be user input + resulting_tracks = self.get_custom_track([f'track.id == "{musicbrainz_releasetrackid}"']) + if len(resulting_tracks) != 1: + return -1 + + return resulting_tracks[0] + + def get_tracks_to_download(self): + return self.get_custom_track(['track.downloaded == 0']) + + def get_tracks_without_src(self): + return self.get_custom_track(["(track.url IS NULL OR track.src IS NULL)"]) + + def get_tracks_without_isrc(self): + return self.get_custom_track(["track.isrc IS NULL"]) + + def get_tracks_without_filepath(self): + return self.get_custom_track(["(track.file IS NULL OR track.path IS NULL OR track.genre IS NULL)"]) + + def update_download_status(self, track_id: str): + pass + + def set_download_data(self, track_id: str, url: str, src: str): + query = f""" +UPDATE track +SET url = ?, + src = ? +WHERE '{track_id}' == id; + """ + self.cursor.execute(query, (url, src)) + self.connection.commit() + + def set_filepath(self, track_id: str, file: str, path: str, genre: str): + query = f""" +UPDATE track +SET file = ?, + path = ?, + genre = ? +WHERE '{track_id}' == id; + """ + self.cursor.execute(query, (file, path, genre)) + self.connection.commit() + + +if __name__ == "__main__": + import tempfile + + temp_folder = "music-downloader" + temp_dir = os.path.join(tempfile.gettempdir(), temp_folder) + if not os.path.exists(temp_dir): + os.mkdir(temp_dir) + + temp_dir = get_temp_dir() + DATABASE_FILE = "metadata.db" + DATABASE_STRUCTURE_FILE = "database_structure.sql" + db_path = os.path.join(TEMP_DIR, DATABASE_FILE) + + logging.basicConfig() + + logger = logging.getLogger("database") + logger.setLevel(logging.DEBUG) + + database = Database(os.path.join(temp_dir, "metadata.db"), os.path.join(temp_dir, "database_structure.sql"), logger, + reset_anyways=True) diff --git a/src/metadata/database_structure.sql b/src/metadata/database_structure.sql new file mode 100644 index 0000000..d5262c7 --- /dev/null +++ b/src/metadata/database_structure.sql @@ -0,0 +1,56 @@ +DROP TABLE IF EXISTS artist; +CREATE TABLE artist ( + id TEXT PRIMARY KEY NOT NULL, + name TEXT +); + +DROP TABLE IF EXISTS artist_release_group; +CREATE TABLE artist_release_group ( + artist_id TEXT NOT NULL, + release_group_id TEXT NOT NULL +); + +DROP TABLE IF EXISTS artist_track; +CREATE TABLE artist_track ( + artist_id TEXT NOT NULL, + track_id TEXT NOT NULL +); + +DROP TABLE IF EXISTS release_group; +CREATE TABLE release_group ( + id TEXT PRIMARY KEY NOT NULL, + albumartist TEXT, + albumsort INT, + musicbrainz_albumtype TEXT, + compilation TEXT, + album_artist_id TEXT +); + +DROP TABLE IF EXISTS release_; +CREATE TABLE release_ ( + id TEXT PRIMARY KEY NOT NULL, + release_group_id TEXT NOT NULL, + title TEXT, + copyright TEXT, + album_status TEXT, + language TEXT, + year TEXT, + date TEXT, + country TEXT, + barcode TEXT +); + +DROP TABLE IF EXISTS track; +CREATE TABLE track ( + id TEXT PRIMARY KEY NOT NULL, + downloaded BOOLEAN NOT NULL DEFAULT 0, + release_id TEXT NOT NULL, + track TEXT, + tracknumber TEXT, + isrc TEXT, + genre TEXT, + path TEXT, + file TEXT, + url TEXT, + src TEXT +); diff --git a/src/metadata/download.py b/src/metadata/download.py new file mode 100644 index 0000000..9b7d888 --- /dev/null +++ b/src/metadata/download.py @@ -0,0 +1,360 @@ +from typing import List +import musicbrainzngs +import logging + +try: + from object_handeling import get_elem_from_obj, parse_music_brainz_date + +except ModuleNotFoundError: + from metadata.object_handeling import get_elem_from_obj, parse_music_brainz_date + +# I don't know if it would be feesable to set up my own mb instance +# https://github.com/metabrainz/musicbrainz-docker + +mb_log = logging.getLogger("musicbrainzngs") +mb_log.setLevel(logging.WARNING) +musicbrainzngs.set_useragent("metadata receiver", "0.1", "https://github.com/HeIIow2/music-downloader") + + +# IMPORTANT DOCUMENTATION WHICH CONTAINS FOR EXAMPLE THE INCLUDES +# https://python-musicbrainzngs.readthedocs.io/en/v0.7.1/api/#getting-data + + +class MetadataDownloader: + def __init__(self, database, logger: logging.Logger): + self.database = database + self.logger = logger + + class Artist: + def __init__( + self, + database, + logger, + musicbrainz_artistid: str, + release_groups: List = [], + new_release_groups: bool = True + ): + self.database = database + self.logger = logger + """ + release_groups: list + """ + self.release_groups = release_groups + + self.musicbrainz_artistid = musicbrainz_artistid + + result = musicbrainzngs.get_artist_by_id(self.musicbrainz_artistid, includes=["release-groups", "releases"]) + artist_data = get_elem_from_obj(result, ['artist'], return_if_none={}) + + self.artist = get_elem_from_obj(artist_data, ['name']) + + self.save() + + # STARTING TO FETCH' RELEASE GROUPS. IMPORTANT: DON'T WRITE ANYTHING BESIDES THAT HERE + if not new_release_groups: + return + # sort all release groups by date and add album sort to have them in chronological order. + release_groups = artist_data['release-group-list'] + for i, release_group in enumerate(release_groups): + release_groups[i]['first-release-date'] = parse_music_brainz_date(release_group['first-release-date']) + release_groups.sort(key=lambda x: x['first-release-date']) + + for i, release_group in enumerate(release_groups): + self.release_groups.append(MetadataDownloader.ReleaseGroup( + self.database, + self.logger, + musicbrainz_releasegroupid=release_group['id'], + artists=[self], + albumsort=i + 1 + )) + + def __str__(self): + newline = "\n" + return f"id: {self.musicbrainz_artistid}\nname: {self.artist}\n{newline.join([str(release_group) for release_group in self.release_groups])}" + + def save(self): + self.logger.info(f"artist: {self}") + self.database.add_artist( + musicbrainz_artistid=self.musicbrainz_artistid, + artist=self.artist + ) + + class ReleaseGroup: + def __init__( + self, + database, + logger, + musicbrainz_releasegroupid: str, + artists=[], + albumsort: int = None, + only_download_distinct_releases: bool = True, + fetch_further: bool = True + ): + self.database = database + self.logger = logger + """ + split_artists: list -> if len > 1: album_artist=VariousArtists + releases: list + """ + + self.musicbrainz_releasegroupid = musicbrainz_releasegroupid + self.artists = artists + self.releases = [] + + result = musicbrainzngs.get_release_group_by_id(musicbrainz_releasegroupid, + includes=["artist-credits", "releases"]) + release_group_data = get_elem_from_obj(result, ['release-group'], return_if_none={}) + artist_datas = get_elem_from_obj(release_group_data, ['artist-credit'], return_if_none={}) + release_datas = get_elem_from_obj(release_group_data, ['release-list'], return_if_none={}) + + for artist_data in artist_datas: + artist_id = get_elem_from_obj(artist_data, ['artist', 'id']) + if artist_id is None: + continue + self.append_artist(artist_id) + self.albumartist = "Various Artists" if len(self.artists) > 1 else self.artists[0].artist + self.album_artist_id = None if self.albumartist == "Various Artists" else self.artists[ + 0].musicbrainz_artistid + + self.albumsort = albumsort + self.musicbrainz_albumtype = get_elem_from_obj(release_group_data, ['primary-type']) + self.compilation = "1" if self.musicbrainz_albumtype == "Compilation" else None + + self.save() + + if not fetch_further: + return + + if only_download_distinct_releases: + self.append_distinct_releases(release_datas) + else: + self.append_all_releases(release_datas) + + def __str__(self): + newline = "\n" + return f"{newline.join([str(release_group) for release_group in self.releases])}" + + def save(self): + self.logger.info(f"caching release_group {self}") + self.database.add_release_group( + musicbrainz_releasegroupid=self.musicbrainz_releasegroupid, + artist_ids=[artist.musicbrainz_artistid for artist in self.artists], + albumartist=self.albumartist, + albumsort=self.albumsort, + musicbrainz_albumtype=self.musicbrainz_albumtype, + compilation=self.compilation, + album_artist_id=self.album_artist_id + ) + + def append_artist(self, artist_id: str): + for existing_artist in self.artists: + if artist_id == existing_artist.musicbrainz_artistid: + return existing_artist + new_artist = MetadataDownloader.Artist(self.database, self.logger, artist_id, release_groups=[self], + new_release_groups=False) + self.artists.append(new_artist) + return new_artist + + def append_release(self, release_data: dict): + musicbrainz_albumid = get_elem_from_obj(release_data, ['id']) + if musicbrainz_albumid is None: + return + self.releases.append( + MetadataDownloader.Release(self.database, self.logger, musicbrainz_albumid, release_group=self)) + + def append_distinct_releases(self, release_datas: List[dict]): + titles = {} + + for release_data in release_datas: + title = get_elem_from_obj(release_data, ['title']) + if title is None: + continue + titles[title] = release_data + + for key in titles: + self.append_release(titles[key]) + + def append_all_releases(self, release_datas: List[dict]): + for release_data in release_datas: + self.append_release(release_data) + + class Release: + def __init__( + self, + database, + logger, + musicbrainz_albumid: str, + release_group=None, + fetch_furter: bool = True + ): + self.database = database + self.logger = logger + """ + release_group: ReleaseGroup + tracks: list + """ + self.musicbrainz_albumid = musicbrainz_albumid + self.release_group = release_group + self.tracklist = [] + + result = musicbrainzngs.get_release_by_id(self.musicbrainz_albumid, + includes=["recordings", "labels", "release-groups"]) + release_data = get_elem_from_obj(result, ['release'], return_if_none={}) + label_data = get_elem_from_obj(release_data, ['label-info-list'], return_if_none={}) + recording_datas = get_elem_from_obj(release_data, ['medium-list', 0, 'track-list'], return_if_none=[]) + release_group_data = get_elem_from_obj(release_data, ['release-group'], return_if_none={}) + if self.release_group is None: + self.release_group = MetadataDownloader.ReleaseGroup(self.database, self.logger, + musicbrainz_releasegroupid=get_elem_from_obj( + release_group_data, ['id']), fetch_further=False) + + self.title = get_elem_from_obj(release_data, ['title']) + self.copyright = get_elem_from_obj(label_data, [0, 'label', 'name']) + + self.album_status = get_elem_from_obj(release_data, ['status']) + self.language = get_elem_from_obj(release_data, ['text-representation', 'language']) + self.year = get_elem_from_obj(release_data, ['date'], lambda x: x.split("-")[0]) + self.date = get_elem_from_obj(release_data, ['date']) + self.country = get_elem_from_obj(release_data, ['country']) + self.barcode = get_elem_from_obj(release_data, ['barcode']) + + self.save() + if fetch_furter: + self.append_recordings(recording_datas) + + def __str__(self): + return f"{self.title} ©{self.copyright} {self.album_status}" + + def save(self): + self.logger.info(f"caching release {self}") + self.database.add_release( + musicbrainz_albumid=self.musicbrainz_albumid, + release_group_id=self.release_group.musicbrainz_releasegroupid, + title=self.title, + copyright_=self.copyright, + album_status=self.album_status, + language=self.language, + year=self.year, + date=self.date, + country=self.country, + barcode=self.barcode + ) + + def append_recordings(self, recording_datas: dict): + for i, recording_data in enumerate(recording_datas): + musicbrainz_releasetrackid = get_elem_from_obj(recording_data, ['recording', 'id']) + if musicbrainz_releasetrackid is None: + continue + + self.tracklist.append( + MetadataDownloader.Track(self.database, self.logger, musicbrainz_releasetrackid, self, + track_number=str(i + 1))) + + class Track: + def __init__( + self, + database, + logger, + musicbrainz_releasetrackid: str, + release=None, + track_number: str = None + ): + self.database = database + self.logger = logger + """ + release: Release + feature_artists: list + """ + + self.musicbrainz_releasetrackid = musicbrainz_releasetrackid + self.release = release + self.artists = [] + + self.track_number = track_number + + result = musicbrainzngs.get_recording_by_id(self.musicbrainz_releasetrackid, + includes=["artists", "releases", "recording-rels", "isrcs", + "work-level-rels"]) + recording_data = result['recording'] + release_data = get_elem_from_obj(recording_data, ['release-list', -1]) + if self.release is None: + self.release = MetadataDownloader.Release(self.database, self.logger, + get_elem_from_obj(release_data, ['id']), fetch_furter=False) + + + for artist_data in get_elem_from_obj(recording_data, ['artist-credit'], return_if_none=[]): + self.append_artist(get_elem_from_obj(artist_data, ['artist', 'id'])) + + self.isrc = get_elem_from_obj(recording_data, ['isrc-list', 0]) + self.title = recording_data['title'] + + self.save() + + def __str__(self): + return f"{self.title}: {self.isrc}" + + def save(self): + self.logger.info(f"caching track {self}") + + self.database.add_track( + musicbrainz_releasetrackid=self.musicbrainz_releasetrackid, + musicbrainz_albumid=self.release.musicbrainz_albumid, + feature_aritsts=[artist.musicbrainz_artistid for artist in self.artists], + tracknumber=self.track_number, + track=self.title, + isrc=self.isrc + ) + + def append_artist(self, artist_id: str): + if artist_id is None: + return + + for existing_artist in self.artists: + if artist_id == existing_artist.musicbrainz_artistid: + return existing_artist + new_artist = MetadataDownloader.Artist(self.database, self.logger, artist_id, new_release_groups=False) + self.artists.append(new_artist) + return new_artist + + def download(self, option: dict): + type_ = option['type'] + mb_id = option['id'] + + if type_ == "artist": + self.Artist(self.database, self.logger, mb_id) + elif type_ == "release_group": + self.ReleaseGroup(self.database, self.logger, mb_id) + elif type_ == "release": + self.Release(self.database, self.logger, mb_id) + elif type_ == "track": + self.Track(self.database, self.logger, mb_id) + + +if __name__ == "__main__": + import tempfile + import os + + temp_folder = "music-downloader" + temp_dir = os.path.join(tempfile.gettempdir(), temp_folder) + if not os.path.exists(temp_dir): + os.mkdir(temp_dir) + + logging.basicConfig(level=logging.DEBUG) + db_logger = logging.getLogger("database") + db_logger.setLevel(logging.DEBUG) + + import database + + database_ = database.Database(os.path.join(temp_dir, "metadata.db"), + os.path.join(temp_dir, "database_structure.sql"), db_logger, + reset_anyways=True) + + download_logger = logging.getLogger("metadata downloader") + download_logger.setLevel(logging.INFO) + + downloader = MetadataDownloader(database_, download_logger) + + downloader.download({'id': 'd2006339-9e98-4624-a386-d503328eb854', 'type': 'track'}) + # downloader.download({'id': 'cdd16860-35fd-46af-bd8c-5de7b15ebc31', 'type': 'release'}) + # download({'id': '4b9af532-ef7e-42ab-8b26-c466327cb5e0', 'type': 'release'}) + # download({'id': 'c24ed9e7-6df9-44de-8570-975f1a5a75d1', 'type': 'track'}) diff --git a/src/metadata/metadata.py b/src/metadata/metadata.py new file mode 100644 index 0000000..5567688 --- /dev/null +++ b/src/metadata/metadata.py @@ -0,0 +1,142 @@ +import logging +import musicbrainzngs + +from metadata import options + +mb_log = logging.getLogger("musicbrainzngs") +mb_log.setLevel(logging.WARNING) +musicbrainzngs.set_useragent("metadata receiver", "0.1", "https://github.com/HeIIow2/music-downloader") + +KNOWN_KIND_OF_OPTIONS = ["artist", "release", "track"] + + +class Search: + def __init__(self, query: str = None, artist: str = None, temp: str = "temp"): + if query is None and artist is None: + raise ValueError("no query provided") + + self.options_history = [] + self.current_options = None + self.current_chosen_option = None + + self.temp = temp + + # initial search + if query is not None: + self.set_options( + options.Options([musicbrainzngs.search_artists(query), musicbrainzngs.search_releases(query), + musicbrainzngs.search_recordings(query)])) + elif artist is not None: + self.set_options(options.Options([musicbrainzngs.search_artists(artist=artist)])) + + def browse_artist(self, artist: dict, limit: int = 25): + options_sets = [ + {"artist-list": [artist, ], "artist-count": 1}, + musicbrainzngs.browse_releases(artist=artist["id"], limit=limit), + musicbrainzngs.browse_recordings(artist=artist["id"], limit=limit) + ] + return self.set_options(options.Options(options_sets)) + + def browse_release(self, release: dict, limit: int = 25): + options_sets = [ + musicbrainzngs.browse_artists(release=release["id"], limit=limit), + {"release-list": [release, ], "release-count": 1}, + musicbrainzngs.browse_recordings(release=release["id"], limit=limit) + ] + return self.set_options(options.Options(options_sets)) + + def browse_track(self, track: dict, limit: int = 25): + options_sets = [ + musicbrainzngs.browse_artists(recording=track["id"], limit=limit), + musicbrainzngs.browse_releases(recording=track["id"], limit=limit), + {"recording-list": [track, ], "recording-count": 1} + ] + return self.set_options(options.Options(options_sets)) + + def choose(self, index, limit: int = 25, ignore_limit_for_tracklist: bool = True): + if not self.current_options.choose(index): + return self.current_options + + self.current_chosen_option = self.current_options.get_current_option(komplex=True) + kind = self.current_chosen_option['type'] + if kind == 'artist': + return self.browse_artist(self.current_chosen_option, limit=limit) + if kind == 'release': + release_limit = limit if not ignore_limit_for_tracklist else 100 + release_limit = 100 + return self.browse_release(self.current_chosen_option, limit=release_limit) + if kind == 'track': + track_limit = limit if not ignore_limit_for_tracklist else 100 + return self.browse_track(self.current_chosen_option, limit=track_limit) + + return self.current_options + + def get_options(self): + return self.current_options + + def set_options(self, option_instance): + self.options_history.append(option_instance) + self.current_options = option_instance + + return option_instance + + def get_previous_options(self): + self.options_history.pop(-1) + self.current_options = self.options_history[-1] + return self.current_options + + options = property(fget=get_options) + + +def automated_demo(): + search = Search(query="psychonaut 4") + print(search.options) + print(search.choose(0)) + search.download() + print(search.choose(2)) + search.download() + print(search.choose(4)) + print(search.download()) + + +def interactive_demo(): + search = Search(query=input("initial query: ")) + print(search.options) + while True: + input_ = input( + "d to download, q to quit, .. for previous options, . for current options, int for this element: ").lower() + input_.strip() + if input_ == "q": + break + if input_ == ".": + print(search.options) + continue + if input_ == "..": + print(search.get_previous_options()) + continue + if input_.isdigit(): + print(search.choose(int(input_))) + continue + if input_ == "d": + search.download() + break + + +if __name__ == "__main__": + # interactive_demo() + # automated_demo() + search = Search(query="psychonaut 4") + # search.download_release("27f00fb8-983c-4d5c-950f-51418aac55dc") + search.download_release("1aeb676f-e556-4b17-b45e-64ab69ef0375") + # for track_ in search.download_artist("c0c720b5-012f-4204-a472-981403f37b12"): + # print(track_) + # res = search.download_track("83a30323-aee1-401a-b767-b3c1bdd026c0") + # res = search.download_track("5e1ee2c5-502c-44d3-b1bc-22803441d8c6") + res = search.download_track("86b43bec-eea6-40ae-8624-c1e404204ba1") + # res = search.download_track("5cc28584-10c6-40e2-b6d4-6891e7e7c575") + + for key in res[0]: + if res[0][key] is None: + continue + + print(key, res[0][key]) diff --git a/src/metadata/object_handeling.py b/src/metadata/object_handeling.py new file mode 100644 index 0000000..7922603 --- /dev/null +++ b/src/metadata/object_handeling.py @@ -0,0 +1,24 @@ +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/metadata/options.py b/src/metadata/options.py new file mode 100644 index 0000000..dea4882 --- /dev/null +++ b/src/metadata/options.py @@ -0,0 +1,118 @@ +def get_string_for_artist(artist: dict) -> str: + string = f"'{artist['name']}'" + if "country" in artist: + string += f" from {artist['country']}" + if 'disambiguation' in artist: + string += f", '{artist['disambiguation']}'" + return string + "\n" + + +def get_string_for_release(release: dict) -> str: + string = "" + if "type" in release: + string += f"the {release['type']} titled " + string += f"'{release['title']}'" + if "artist-credit-phrase" in release: + string += f" by: {release['artist-credit-phrase']}" + + return string + "\n" + + +def get_string_for_tracks(tracks: dict) -> str: + # I know it's not the best practice but whatever + return get_string_for_release(tracks) + + +def get_string_for_option(option: dict) -> str: + kind = option['type'] + if kind == "artist": + return get_string_for_artist(option) + if kind == "release": + return get_string_for_release(option) + if kind == "track": + return get_string_for_tracks(option) + return "Error\n" + + +class Options: + def __init__(self, results: list): + self.results = results + + self.artist_count = 0 + self.release_count = 0 + self.track_count = 0 + self.result_list = [] + self.set_options_values() + + self.current_option_ind = None + + def get_current_option(self, komplex: bool = False): + if self.current_option_ind is None: + raise Exception("It must first be chosen, which option to get, before getting it") + + if komplex: + return self.result_list[self.current_option_ind] + + komplex_information = self.result_list[self.current_option_ind] + return { + 'id': komplex_information['id'], + 'type': komplex_information['type'] + } + + def choose(self, index: int) -> bool: + if len(self.result_list) <= index - 1: + return False + self.current_option_ind = index + return True + + def __str__(self) -> str: + string = f"artists: {self.artist_count}; releases {self.release_count}; tracks {self.track_count}\n" + for i, option in enumerate(self.result_list): + string += f"{i})\t{option['type']}:\t" + get_string_for_option(option) + return string + + def set_options_values(self): + for option_set in self.results: + if "artist-list" in option_set: + self.set_artist_values(option_set) + continue + if "release-list" in option_set: + self.set_release_values(option_set) + continue + if "recording-list" in option_set: + self.set_track_values(option_set) + continue + + def set_artist_values(self, option_set: dict): + self.artist_count += option_set['artist-count'] + for artist in option_set['artist-list']: + artist['type'] = "artist" + self.result_list.append(artist) + + def set_release_values(self, option_set: dict): + self.release_count += option_set['release-count'] + for release in option_set['release-list']: + release['type'] = "release" + self.result_list.append(release) + + def set_track_values(self, option_set: dict): + self.track_count += option_set['recording-count'] + for track in option_set['recording-list']: + track['type'] = "track" + self.result_list.append(track) + +""" +example +{'artist-list': [{'id': 'c0c720b5-012f-4204-a472-981403f37b12', 'type': 'Group', 'ext:score': '100', 'name': 'Psychonaut 4', 'sort-name': 'Psychonaut 4', 'country': 'GE', 'area': {'id': '7e081aa0-817b-3ae0-9fe2-4bb4e3b3cc95', 'type': 'Country', 'name': 'Georgia', 'sort-name': 'Georgia', 'life-span': {'ended': 'false'}}, 'begin-area': {'id': '76c77b6c-f1e1-4a58-8fe1-01a7efadd1f7', 'type': 'City', 'name': 'Tbilisi', 'sort-name': 'Tbilisi', 'life-span': {'ended': 'false'}}, 'disambiguation': 'Georgian depressive black metal', 'life-span': {'begin': '2010', 'ended': 'false'}, 'tag-list': [{'count': '0', 'name': 'black metal'}, {'count': '5', 'name': 'depressive black metal'}, {'count': '1', 'name': 'sex'}, {'count': '1', 'name': 'suicide'}, {'count': '1', 'name': 'hopelessness'}, {'count': '1', 'name': 'drugs'}, {'count': '1', 'name': 'depression'}, {'count': '1', 'name': 'alcohol'}]}, {'id': '55e84562-e29b-4967-8d2b-a7fff673cd1e', 'type': 'Group', 'ext:score': '82', 'name': 'Psychonaut', 'sort-name': 'Psychonaut', 'country': 'BE', 'area': {'id': '5b8a5ee5-0bb3-34cf-9a75-c27c44e341fc', 'type': 'Country', 'name': 'Belgium', 'sort-name': 'Belgium', 'life-span': {'ended': 'false'}}, 'begin-area': {'id': 'f05e07a9-0ea2-4b8e-9538-370456752089', 'type': 'City', 'name': 'Mechelen', 'sort-name': 'Mechelen', 'life-span': {'ended': 'false'}}, 'disambiguation': 'Belgian sludge metal band', 'isni-list': ['0000000475468695'], 'life-span': {'begin': '2013', 'ended': 'false'}}, {'id': '35ed62b6-a358-41a5-8c09-80e7aaae1e1b', 'type': 'Group', 'ext:score': '81', 'name': 'Psychonaut', 'sort-name': 'Psychonaut', 'country': 'DE', 'area': {'id': '85752fda-13c4-31a3-bee5-0e5cb1f51dad', 'type': 'Country', 'name': 'Germany', 'sort-name': 'Germany', 'life-span': {'ended': 'false'}}, 'disambiguation': 'German trance group; Lothar Herrmann & Oliver Balser', 'life-span': {'ended': 'false'}}, {'id': 'ad37f13c-73b6-48c0-8606-2321059a41ce', 'type': 'Group', 'ext:score': '81', 'name': 'Psychonaut', 'sort-name': 'Psychonaut', 'country': 'AU', 'area': {'id': '106e0bec-b638-3b37-b731-f53d507dc00e', 'type': 'Country', 'name': 'Australia', 'sort-name': 'Australia', 'life-span': {'ended': 'false'}}, 'begin-area': {'id': '21e8495a-32fa-4a97-8a92-57184e36235a', 'type': 'City', 'name': 'Perth', 'sort-name': 'Perth', 'life-span': {'ended': 'false'}}, 'disambiguation': 'Australian thrash metal band', 'life-span': {'begin': '1997', 'ended': 'false'}}, {'id': '48abf0e3-14e6-4bf9-a14e-45bc7f327891', 'type': 'Group', 'ext:score': '81', 'name': 'Psychonaut', 'sort-name': 'Psychonaut', 'country': 'CZ', 'area': {'id': '51d34c28-61bf-3d21-849f-7492672a9d44', 'type': 'Country', 'name': 'Czechia', 'sort-name': 'Czechia', 'life-span': {'begin': '1993-01-01', 'ended': 'false'}}, 'begin-area': {'id': 'a99b59f9-f506-4c1a-876c-43d8dda0ce0e', 'type': 'City', 'name': 'Opava', 'sort-name': 'Opava', 'life-span': {'ended': 'false'}}, 'disambiguation': 'Czech funk rock band from Opava', 'life-span': {'begin': '1999', 'ended': 'false'}}, {'id': '6ece3c88-f052-4529-bacf-5e12290a331a', 'ext:score': '79', 'name': 'Psychonaut', 'sort-name': 'Psychonaut', 'disambiguation': 'bass/dubstep artist', 'life-span': {'ended': 'false'}}, {'id': '113f46a8-8cc9-4c81-8681-dbc0d947bb5a', 'type': 'Group', 'ext:score': '79', 'name': 'Psychonaut', 'sort-name': 'Psychonaut', 'country': 'US', 'area': {'id': '489ce91b-6658-3307-9877-795b68554c98', 'type': 'Country', 'name': 'United States', 'sort-name': 'United States', 'life-span': {'ended': 'false'}}, 'begin-area': {'id': '4fb3cf81-d45c-4607-aa90-53856d69f5ff', 'type': 'City', 'name': 'Harrisonburg', 'sort-name': 'Harrisonburg', 'life-span': {'ended': 'false'}}, 'disambiguation': 'Garage psych band from Virginia', 'life-span': {'begin': '2016', 'ended': 'false'}}, {'id': '7c976521-a20f-4107-b3e0-766ebae07a56', 'type': 'Person', 'ext:score': '79', 'name': 'Psychonaut', 'sort-name': 'Psychonaut', 'disambiguation': 'Alias of Spanish trance DJ Javi Golo', 'life-span': {'ended': 'false'}}, {'id': 'e2e692b5-efbc-4cd7-ab38-bce0bd457a8b', 'type': 'Group', 'ext:score': '74', 'name': 'Psychonaut 75', 'sort-name': 'Psychonaut 75', 'country': 'US', 'area': {'id': '489ce91b-6658-3307-9877-795b68554c98', 'type': 'Country', 'name': 'United States', 'sort-name': 'United States', 'life-span': {'ended': 'false'}}, 'begin-area': {'id': 'c920948b-83e3-40b7-8fe9-9ab5abaac55b', 'type': 'City', 'name': 'Houston', 'sort-name': 'Houston', 'life-span': {'ended': 'false'}}, 'disambiguation': 'dark industrial dance', 'life-span': {'begin': '1997', 'ended': 'false'}, 'alias-list': [{'sort-name': 'Psychonaut', 'type': 'Artist name', 'begin-date': '1997', 'end-date': '2001', 'alias': 'Psychonaut'}, {'sort-name': 'Psychonaut 75', 'type': 'Artist name', 'begin-date': '2001', 'alias': 'Psychonaut 75'}], 'tag-list': [{'count': '1', 'name': 'industrial'}, {'count': '1', 'name': 'ritual ambient'}]}, {'id': 'fa6521a7-56b5-4e56-b946-fda469becba9', 'type': 'Group', 'ext:score': '69', 'name': '4 Strings', 'sort-name': '4 Strings', 'country': 'NL', 'area': {'id': 'ef1b7cc0-cd26-36f4-8ea0-04d9623786c7', 'type': 'Country', 'name': 'Netherlands', 'sort-name': 'Netherlands', 'life-span': {'ended': 'false'}}, 'disambiguation': 'Netherlands dance duo', 'life-span': {'ended': 'false'}, 'alias-list': [{'sort-name': '4Strings', 'alias': '4Strings'}], 'tag-list': [{'count': '1', 'name': 'dance pop'}, {'count': '0', 'name': 'dutch trance'}, {'count': '1', 'name': 'trance pop'}, {'count': '2', 'name': 'vocal trance'}, {'count': '3', 'name': 'trance'}, {'count': '1', 'name': 'uplifting'}, {'count': '1', 'name': 'uplifting trance'}, {'count': '1', 'name': 'female vocal trance'}, {'count': '1', 'name': 'dutch'}, {'count': '1', 'name': 'pop trance'}, {'count': '1', 'name': 'dance trance'}, {'count': '1', 'name': 'female vocal dance'}, {'count': '1', 'name': 'vocal dance'}, {'count': '2', 'name': 'dance-pop'}]}, {'id': 'bdbd99ab-3f4b-4028-9306-cd71d8695fbc', 'type': 'Group', 'ext:score': '66', 'name': 'Anonymous 4', 'sort-name': 'Anonymous 4', 'country': 'US', 'area': {'id': '489ce91b-6658-3307-9877-795b68554c98', 'type': 'Country', 'name': 'United States', 'sort-name': 'United States', 'life-span': {'ended': 'false'}}, 'disambiguation': 'vocal quartet specialising in medieval chant and polyphony', 'isni-list': ['0000000123640949'], 'life-span': {'begin': '1986', 'ended': 'false'}, 'alias-list': [{'sort-name': 'An English Ladymass', 'alias': 'An English Ladymass'}, {'sort-name': 'A Star in the East', 'alias': 'A Star in the East'}, {'sort-name': 'On Yoolis Night', 'alias': 'On Yoolis Night'}, {'sort-name': 'Eleven Thousand Virgins', 'alias': 'Eleven Thousand Virgins'}, {'sort-name': "Miracles of Saint'Iago", 'alias': "Miracles of Saint'Iago"}, {'sort-name': "Love's Illusion", 'alias': "Love's Illusion"}, {'sort-name': 'The Lily & The Lamb', 'alias': 'The Lily & The Lamb'}], 'tag-list': [{'count': '1', 'name': 'early music'}, {'count': '1', 'name': 'medieval'}, {'count': '2', 'name': 'a cappella'}]}, {'id': 'c869ad3e-6e8f-46e3-89e0-dc9d28d6bc61', 'type': 'Person', 'ext:score': '64', 'name': '4*', 'sort-name': '4*', 'gender': 'male', 'area': {'id': '30bcaa92-9870-4798-be1a-4e0036755316', 'type': 'City', 'name': 'Osaka', 'sort-name': 'Osaka', 'life-span': {'ended': 'false'}}, 'life-span': {'ended': 'false'}}, {'id': '3d1dc3db-4fc3-42b2-a8cd-4d8983637450', 'type': 'Group', 'ext:score': '63', 'name': 'Radio 4', 'sort-name': 'Radio 4', 'country': 'US', 'area': {'id': '489ce91b-6658-3307-9877-795b68554c98', 'type': 'Country', 'name': 'United States', 'sort-name': 'United States', 'life-span': {'ended': 'false'}}, 'begin-area': {'id': 'a71b0d32-7752-49e9-8594-2247ad6ac12c', 'type': 'District', 'name': 'Brooklyn', 'sort-name': 'Brooklyn', 'life-span': {'ended': 'false'}}, 'isni-list': ['000000011882564X'], 'life-span': {'begin': '1999', 'ended': 'false'}, 'alias-list': [{'sort-name': 'Radio4', 'alias': 'Radio4'}], 'tag-list': [{'count': '1', 'name': 'indie rock'}, {'count': '1', 'name': 'rock and indie'}, {'count': '1', 'name': 'dance-punk'}, {'count': '1', 'name': 'american'}, {'count': '1', 'name': 'post-punk revival'}]}, {'id': '5fdbf7e7-8643-44ad-94a7-3d669e3876e4', 'type': 'Group', 'ext:score': '62', 'name': 'Twenty 4 Seven', 'sort-name': 'Twenty 4 Seven', 'country': 'NL', 'area': {'id': 'ef1b7cc0-cd26-36f4-8ea0-04d9623786c7', 'type': 'Country', 'name': 'Netherlands', 'sort-name': 'Netherlands', 'life-span': {'ended': 'false'}}, 'disambiguation': 'euro house', 'isni-list': ['0000000122900577'], 'life-span': {'begin': '1989', 'ended': 'false'}, 'alias-list': [{'sort-name': 'Twenty 4th Street', 'alias': 'Twenty 4th Street'}, {'sort-name': 'Twenty Four Seven', 'alias': 'Twenty Four Seven'}, {'sort-name': '24-7', 'alias': '24-7'}], 'tag-list': [{'count': '1', 'name': 'dance'}, {'count': '1', 'name': 'euro house'}, {'count': '1', 'name': 'house'}, {'count': '1', 'name': 'eurodance'}]}, {'id': '32ff0254-5c1f-425c-910a-ff62ff888ca2', 'type': 'Group', 'ext:score': '62', 'name': 'Lab 4', 'sort-name': 'Lab 4', 'country': 'GB', 'area': {'id': '8a754a16-0027-3a29-b6d7-2b40ea0481ed', 'type': 'Country', 'name': 'United Kingdom', 'sort-name': 'United Kingdom', 'life-span': {'ended': 'false'}}, 'life-span': {'begin': '1994', 'ended': 'false'}, 'alias-list': [{'sort-name': 'LAB4', 'alias': 'LAB4'}, {'sort-name': 'Lab-4', 'alias': 'Lab-4'}]}, {'id': '95711c34-6d6a-4acd-a713-e460a7915494', 'type': 'Group', 'ext:score': '62', 'name': '4', 'sort-name': '4', 'country': 'US', 'area': {'id': '489ce91b-6658-3307-9877-795b68554c98', 'type': 'Country', 'name': 'United States', 'sort-name': 'United States', 'life-span': {'ended': 'false'}}, 'begin-area': {'id': '398c6575-6e22-44f9-b93a-349c9f5c8889', 'type': 'City', 'name': 'Costa Mesa', 'sort-name': 'Costa Mesa', 'life-span': {'ended': 'false'}}, 'disambiguation': '1990s California alternative/punk band', 'life-span': {'ended': 'false'}}, {'id': 'a21768f9-0bb1-41f2-9860-90e95aaa0f34', 'type': 'Group', 'ext:score': '61', 'name': '4 Clubbers', 'sort-name': '4 Clubbers', 'country': 'DE', 'area': {'id': '85752fda-13c4-31a3-bee5-0e5cb1f51dad', 'type': 'Country', 'name': 'Germany', 'sort-name': 'Germany', 'life-span': {'ended': 'false'}}, 'life-span': {'ended': 'false'}, 'alias-list': [{'sort-name': '4Clubbers', 'alias': '4Clubbers'}]}, {'id': '7e5134b6-add1-4cbe-accd-f01547a4b6c3', 'ext:score': '61', 'name': '4$', 'sort-name': '4$', 'disambiguation': 'trap', 'life-span': {'ended': 'false'}}, {'id': 'c0e9bf2e-9c31-4ff1-8e23-0021652a9015', 'type': 'Group', 'ext:score': '61', 'name': '4 Позиции Бруно', 'sort-name': '4 Positions of Bruno', 'country': 'RU', 'area': {'id': '1f1fc3a4-9500-39b8-9f10-f0a465557eef', 'type': 'Country', 'name': 'Russia', 'sort-name': 'Russia', 'life-span': {'ended': 'false'}}, 'begin-area': {'id': '0918dfa2-f667-4dbe-bac9-644d81a06612', 'type': 'City', 'name': 'Yekaterinburg', 'sort-name': 'Yekaterinburg', 'life-span': {'ended': 'false'}}, 'life-span': {'begin': '2002', 'ended': 'false'}, 'alias-list': [{'sort-name': '4 pozitsii Bruno', 'alias': '4 pozitsii Bruno'}, {'locale': 'en', 'sort-name': 'Bruno’s 4 Positions', 'alias': 'Bruno’s 4 Positions'}], 'tag-list': [{'count': '3', 'name': 'electronic'}, {'count': '3', 'name': 'ambient'}, {'count': '3', 'name': 'experimental'}]}, {'id': 'df659499-80f2-4301-bb46-0eade0213f5b', 'ext:score': '61', 'name': ':4:', 'sort-name': 'channel4four', 'disambiguation': 'experimental music', 'life-span': {'ended': 'false'}, 'alias-list': [{'sort-name': 'channel4four', 'alias': 'channel4four'}]}, {'id': 'a5891a6b-d0a9-4264-b47d-bcf526bbb826', 'ext:score': '61', 'name': 'Eponymous 4', 'sort-name': 'Eponymous 4', 'area': {'id': '10adc6b5-63bf-4b4e-993e-ed83b05c22fc', 'type': 'City', 'name': 'Seattle', 'sort-name': 'Seattle', 'life-span': {'ended': 'false'}}, 'life-span': {'begin': '1999', 'ended': 'false'}}, {'id': '318ec65f-9046-4c41-a60f-664f5f997ba5', 'ext:score': '61', 'name': '⦅౪⦆', 'sort-name': '⦅౪⦆', 'life-span': {'ended': 'false'}, 'alias-list': [{'sort-name': 'drunkenhigh', 'alias': 'drunkenhigh'}], 'tag-list': [{'count': '1', 'name': 'sillyname'}, {'count': '1', 'name': '!hyperfocus'}]}, {'id': 'e3891ce8-1f22-4ee9-8b3d-5c8f1bb8dbd2', 'ext:score': '61', 'name': '4', 'sort-name': '4', 'disambiguation': '/v/', 'life-span': {'ended': 'false'}}, {'id': 'ceb3cccd-3c71-4acf-a2da-146abd021760', 'ext:score': '61', 'name': '4', 'sort-name': '4', 'disambiguation': 'rapper us ?', 'life-span': {'ended': 'false'}}, {'id': 'a5c07e9f-1995-4c81-9425-7fea1489c37d', 'type': 'Group', 'ext:score': '60', 'name': '4 Promille', 'sort-name': '4 Promille', 'country': 'DE', 'area': {'id': '85752fda-13c4-31a3-bee5-0e5cb1f51dad', 'type': 'Country', 'name': 'Germany', 'sort-name': 'Germany', 'life-span': {'ended': 'false'}}, 'life-span': {'ended': 'false'}, 'alias-list': [{'sort-name': 'Promille', 'alias': 'Promille'}], 'tag-list': [{'count': '1', 'name': 'punk'}]}], 'artist-count': 941} +{'release-list': [{'id': 'd6e6be74-b7a4-4982-955a-d3361885541c', 'ext:score': '100', 'title': '40%', 'status': 'Official', 'text-representation': {'language': 'eng', 'script': 'Latn'}, 'artist-credit': [{'name': 'Psychonaut 4', 'artist': {'id': 'c0c720b5-012f-4204-a472-981403f37b12', 'name': 'Psychonaut 4', 'sort-name': 'Psychonaut 4', 'disambiguation': 'Georgian depressive black metal'}}], 'release-group': {'id': 'a557bf98-e902-4336-8b28-ecbede7f93bf', 'type': 'Album', 'title': '40%', 'primary-type': 'Album', 'secondary-type-list': ['Demo']}, 'date': '2011-06-04', 'country': 'UA', 'release-event-list': [{'date': '2011-06-04', 'area': {'id': '904768d0-61ca-3c40-93ac-93adc36fef4b', 'name': 'Ukraine', 'sort-name': 'Ukraine', 'iso-3166-1-code-list': ['UA']}}], 'label-info-list': [{'label': {'id': 'ce4c6532-a7a4-47e7-83c1-169e68e461df', 'name': 'Depressive Illusions Records'}}], 'medium-list': [{'format': 'Cassette', 'disc-list': [], 'disc-count': 0, 'track-list': [], 'track-count': 3}], 'medium-track-count': 3, 'medium-count': 1, 'tag-list': [], 'artist-credit-phrase': 'Psychonaut 4'}, {'id': '4b9af532-ef7e-42ab-8b26-c466327cb5e0', 'ext:score': '100', 'title': 'Dipsomania', 'status': 'Official', 'packaging': 'Digipak', 'text-representation': {'language': 'eng', 'script': 'Latn'}, 'artist-credit': [{'name': 'Psychonaut 4', 'artist': {'id': 'c0c720b5-012f-4204-a472-981403f37b12', 'name': 'Psychonaut 4', 'sort-name': 'Psychonaut 4', 'disambiguation': 'Georgian depressive black metal'}}], 'release-group': {'id': 'a37697eb-176b-4242-95fc-27de66342463', 'type': 'Album', 'title': 'Dipsomania', 'primary-type': 'Album'}, 'date': '2015-04-25', 'country': 'AT', 'release-event-list': [{'date': '2015-04-25', 'area': {'id': 'caac77d1-a5c8-3e6e-8e27-90b44dcc1446', 'name': 'Austria', 'sort-name': 'Austria', 'iso-3166-1-code-list': ['AT']}}], 'label-info-list': [{'catalog-number': 'TR025CD', 'label': {'id': 'f6824a99-ac3b-4cc4-95fe-8e47769b33a7', 'name': 'Talheim Records'}}], 'medium-list': [{'format': 'CD', 'disc-list': [], 'disc-count': 0, 'track-list': [], 'track-count': 12}], 'medium-track-count': 12, 'medium-count': 1, 'tag-list': [{'count': '1', 'name': 'metal'}, {'count': '0', 'name': 'dipsomania'}, {'count': '1', 'name': 'psychonaut 4'}, {'count': '0', 'name': 'psychonaut'}, {'count': '1', 'name': 'depressive'}, {'count': '1', 'name': 'black'}], 'artist-credit-phrase': 'Psychonaut 4'}, {'id': 'ab61f4b0-88e8-43b5-9b24-af184c6f1f12', 'ext:score': '100', 'title': 'Free Portion ov Madness I', 'status': 'Official', 'packaging': 'None', 'text-representation': {'language': 'eng', 'script': 'Latn'}, 'artist-credit': [{'name': 'Psychonaut 4', 'artist': {'id': 'c0c720b5-012f-4204-a472-981403f37b12', 'name': 'Psychonaut 4', 'sort-name': 'Psychonaut 4', 'disambiguation': 'Georgian depressive black metal'}}], 'release-group': {'id': 'fe7f2480-fa00-483e-bad6-e6fc839ec3ea', 'type': 'Single', 'title': 'Free Portion ov Madness I', 'primary-type': 'Single'}, 'date': '2014-04-11', 'country': 'XW', 'release-event-list': [{'date': '2014-04-11', 'area': {'id': '525d4e18-3d00-31b9-a58b-a146a916de8f', 'name': '[Worldwide]', 'sort-name': '[Worldwide]', 'iso-3166-1-code-list': ['XW']}}], 'barcode': '', 'label-info-list': [{'label': {'id': '157afde4-4bf5-4039-8ad2-5a15acc85176', 'name': '[no label]'}}], 'medium-list': [{'format': 'Digital Media', 'disc-list': [], 'disc-count': 0, 'track-list': [], 'track-count': 1}], 'medium-track-count': 1, 'medium-count': 1, 'tag-list': [{'count': '2', 'name': 'metal'}, {'count': '1', 'name': 'dsbm'}, {'count': '1', 'name': 'suicidal black metal'}, {'count': '1', 'name': 'depressive suicidal black metal'}, {'count': '2', 'name': 'post black metal'}, {'count': '1', 'name': 'depressive rock'}, {'count': '1', 'name': 'tbilisi'}, {'count': '1', 'name': 'post suicidal black metal'}, {'count': '2', 'name': 'depressive black metal'}], 'artist-credit-phrase': 'Psychonaut 4'}, {'id': 'a3015b31-3b62-4ffa-9184-9fb7bfd39660', 'ext:score': '100', 'title': 'Have a Nice Trip', 'status': 'Official', 'text-representation': {'language': 'eng', 'script': 'Latn'}, 'artist-credit': [{'name': 'Psychonaut 4', 'artist': {'id': 'c0c720b5-012f-4204-a472-981403f37b12', 'name': 'Psychonaut 4', 'sort-name': 'Psychonaut 4', 'disambiguation': 'Georgian depressive black metal'}}], 'release-group': {'id': '9b1453e9-5b74-43c3-8e4f-0f4c78e46aff', 'type': 'Album', 'title': 'Have a Nice Trip', 'primary-type': 'Album'}, 'date': '2012-08-11', 'country': 'UA', 'release-event-list': [{'date': '2012-08-11', 'area': {'id': '904768d0-61ca-3c40-93ac-93adc36fef4b', 'name': 'Ukraine', 'sort-name': 'Ukraine', 'iso-3166-1-code-list': ['UA']}}], 'label-info-list': [{'catalog-number': 'cut 668', 'label': {'id': 'ce4c6532-a7a4-47e7-83c1-169e68e461df', 'name': 'Depressive Illusions Records'}}], 'medium-list': [{'format': 'Cassette', 'disc-list': [], 'disc-count': 0, 'track-list': [], 'track-count': 11}], 'medium-track-count': 11, 'medium-count': 1, 'tag-list': [], 'artist-credit-phrase': 'Psychonaut 4'}, {'id': '0d229a02-74f6-4c77-8c20-6612295870ae', 'ext:score': '100', 'title': 'Neurasthenia', 'status': 'Official', 'packaging': 'Digipak', 'text-representation': {'language': 'eng', 'script': 'Latn'}, 'artist-credit': [{'name': 'Psychonaut 4', 'artist': {'id': 'c0c720b5-012f-4204-a472-981403f37b12', 'name': 'Psychonaut 4', 'sort-name': 'Psychonaut 4', 'disambiguation': 'Georgian depressive black metal'}}], 'release-group': {'id': '855c95c7-b7c2-40b2-b453-e3d37f43401e', 'type': 'Album', 'title': 'Neurasthenia', 'primary-type': 'Album'}, 'date': '2016-10-07', 'country': 'AT', 'release-event-list': [{'date': '2016-10-07', 'area': {'id': 'caac77d1-a5c8-3e6e-8e27-90b44dcc1446', 'name': 'Austria', 'sort-name': 'Austria', 'iso-3166-1-code-list': ['AT']}}], 'label-info-list': [{'catalog-number': 'TR045CD', 'label': {'id': 'f6824a99-ac3b-4cc4-95fe-8e47769b33a7', 'name': 'Talheim Records'}}], 'medium-list': [{'format': 'CD', 'disc-list': [], 'disc-count': 0, 'track-list': [], 'track-count': 10}], 'medium-track-count': 10, 'medium-count': 1, 'tag-list': [], 'artist-credit-phrase': 'Psychonaut 4'}, {'id': 'f8d4b24d-2c46-4e9c-8078-0c0f337c84dd', 'ext:score': '100', 'title': 'Beautyfall', 'status': 'Official', 'packaging': 'Digipak', 'text-representation': {'language': 'mul', 'script': 'Qaaa'}, 'artist-credit': [{'name': 'Psychonaut 4', 'artist': {'id': 'c0c720b5-012f-4204-a472-981403f37b12', 'name': 'Psychonaut 4', 'sort-name': 'Psychonaut 4', 'disambiguation': 'Georgian depressive black metal'}}], 'release-group': {'id': 'b58eaf6e-1cb3-4d61-bf3b-54dd6971e0d0', 'type': 'Album', 'title': 'Beautyfall', 'primary-type': 'Album'}, 'date': '2020-10-31', 'country': 'GE', 'release-event-list': [{'date': '2020-10-31', 'area': {'id': '7e081aa0-817b-3ae0-9fe2-4bb4e3b3cc95', 'name': 'Georgia', 'sort-name': 'Georgia', 'iso-3166-1-code-list': ['GE']}}], 'label-info-list': [{'catalog-number': 'TR068CD', 'label': {'id': 'f6824a99-ac3b-4cc4-95fe-8e47769b33a7', 'name': 'Talheim Records'}}], 'medium-list': [{'format': 'CD', 'disc-list': [], 'disc-count': 0, 'track-list': [], 'track-count': 8}], 'medium-track-count': 8, 'medium-count': 1, 'tag-list': [{'count': '1', 'name': 'depressive black metal'}], 'artist-credit-phrase': 'Psychonaut 4'}, {'id': 'afb2e006-7e6f-4f37-b43e-f844bf3ea94b', 'ext:score': '100', 'title': 'Beautyfall / სულდაცემა', 'status': 'Official', 'packaging': 'None', 'text-representation': {'language': 'eng', 'script': 'Latn'}, 'artist-credit': [{'name': 'Psychonaut 4', 'artist': {'id': 'c0c720b5-012f-4204-a472-981403f37b12', 'name': 'Psychonaut 4', 'sort-name': 'Psychonaut 4', 'disambiguation': 'Georgian depressive black metal'}}], 'release-group': {'id': 'b58eaf6e-1cb3-4d61-bf3b-54dd6971e0d0', 'type': 'Album', 'title': 'Beautyfall', 'primary-type': 'Album'}, 'date': '2020-10-30', 'country': 'XW', 'release-event-list': [{'date': '2020-10-30', 'area': {'id': '525d4e18-3d00-31b9-a58b-a146a916de8f', 'name': '[Worldwide]', 'sort-name': '[Worldwide]', 'iso-3166-1-code-list': ['XW']}}], 'medium-list': [{'format': 'Digital Media', 'disc-list': [], 'disc-count': 0, 'track-list': [], 'track-count': 8}], 'medium-track-count': 8, 'medium-count': 1, 'tag-list': [{'count': '1', 'name': 'post suicidal black metal'}, {'count': '2', 'name': 'metal'}, {'count': '2', 'name': 'black metal'}, {'count': '1', 'name': 'tbilisi'}, {'count': '1', 'name': 'depressive suicidal black metal'}, {'count': '1', 'name': 'metal metal'}, {'count': '1', 'name': 'suicidal depressive black'}, {'count': '1', 'name': 'depressivesuicidalblackmetal'}], 'artist-credit-phrase': 'Psychonaut 4'}, {'id': 'da90dfc2-95cf-4c24-8549-84339d6d3076', 'ext:score': '100', 'title': 'Have a Nice Trip', 'status': 'Official', 'packaging': 'None', 'artist-credit': [{'name': 'Psychonaut 4', 'artist': {'id': 'c0c720b5-012f-4204-a472-981403f37b12', 'name': 'Psychonaut 4', 'sort-name': 'Psychonaut 4', 'disambiguation': 'Georgian depressive black metal'}}], 'release-group': {'id': '9b1453e9-5b74-43c3-8e4f-0f4c78e46aff', 'type': 'Album', 'title': 'Have a Nice Trip', 'primary-type': 'Album'}, 'date': '2018-08-15', 'country': 'XW', 'release-event-list': [{'date': '2018-08-15', 'area': {'id': '525d4e18-3d00-31b9-a58b-a146a916de8f', 'name': '[Worldwide]', 'sort-name': '[Worldwide]', 'iso-3166-1-code-list': ['XW']}}], 'barcode': '4061707064036', 'asin': 'B07G2D5GZ9', 'label-info-list': [{'catalog-number': 'none', 'label': {'id': 'f6824a99-ac3b-4cc4-95fe-8e47769b33a7', 'name': 'Talheim Records'}}], 'medium-list': [{'format': 'Digital Media', 'disc-list': [], 'disc-count': 0, 'track-list': [], 'track-count': 11}], 'medium-track-count': 11, 'medium-count': 1, 'tag-list': [{'count': '2', 'name': 'depressive black metal'}, {'count': '1', 'name': 'tbilisi'}, {'count': '1', 'name': 'depressive rock'}, {'count': '2', 'name': 'metal'}, {'count': '1', 'name': 'post suicidal black metal'}, {'count': '1', 'name': 'depressive/black-metal'}, {'count': '1', 'name': 'depressive suicidal black metal'}, {'count': '1', 'name': 'suicidal black metal'}, {'count': '2', 'name': 'post black metal'}, {'count': '1', 'name': 'dsbm'}], 'artist-credit-phrase': 'Psychonaut 4'}, {'id': '2b7e9b09-8651-490f-a638-0b99e157a12c', 'ext:score': '100', 'title': 'Have a Nice Trip', 'status': 'Official', 'packaging': 'Digipak', 'text-representation': {'language': 'mul', 'script': 'Qaaa'}, 'artist-credit': [{'name': 'Psychonaut 4', 'artist': {'id': 'c0c720b5-012f-4204-a472-981403f37b12', 'name': 'Psychonaut 4', 'sort-name': 'Psychonaut 4', 'disambiguation': 'Georgian depressive black metal'}}], 'release-group': {'id': '9b1453e9-5b74-43c3-8e4f-0f4c78e46aff', 'type': 'Album', 'title': 'Have a Nice Trip', 'primary-type': 'Album'}, 'date': '2018-12-01', 'country': 'AT', 'release-event-list': [{'date': '2018-12-01', 'area': {'id': 'caac77d1-a5c8-3e6e-8e27-90b44dcc1446', 'name': 'Austria', 'sort-name': 'Austria', 'iso-3166-1-code-list': ['AT']}}], 'barcode': '', 'label-info-list': [{'catalog-number': 'TR050CD', 'label': {'id': 'f6824a99-ac3b-4cc4-95fe-8e47769b33a7', 'name': 'Talheim Records'}}], 'medium-list': [{'format': 'CD', 'disc-list': [], 'disc-count': 1, 'track-list': [], 'track-count': 12}], 'medium-track-count': 12, 'medium-count': 1, 'tag-list': [], 'artist-credit-phrase': 'Psychonaut 4'}, {'id': 'e37ff962-2c1e-4af2-887d-89d2d88c4bea', 'ext:score': '100', 'title': 'Have a Nice Trip', 'status': 'Official', 'text-representation': {'language': 'mul', 'script': 'Qaaa'}, 'artist-credit': [{'name': 'Psychonaut 4', 'artist': {'id': 'c0c720b5-012f-4204-a472-981403f37b12', 'name': 'Psychonaut 4', 'sort-name': 'Psychonaut 4', 'disambiguation': 'Georgian depressive black metal'}}], 'release-group': {'id': '9b1453e9-5b74-43c3-8e4f-0f4c78e46aff', 'type': 'Album', 'title': 'Have a Nice Trip', 'primary-type': 'Album'}, 'date': '2012-11', 'country': 'DE', 'release-event-list': [{'date': '2012-11', 'area': {'id': '85752fda-13c4-31a3-bee5-0e5cb1f51dad', 'name': 'Germany', 'sort-name': 'Germany', 'iso-3166-1-code-list': ['DE']}}], 'label-info-list': [{'catalog-number': 'SAD 008', 'label': {'id': 'efb18b12-0baa-4592-ac33-14cdd684aaff', 'name': 'Solitude and Despair Music'}}, {'catalog-number': 'DNW025', 'label': {'id': 'ebc3b589-d7ff-4978-baa4-434588819f6d', 'name': 'Der Neue Weg'}}], 'medium-list': [{'format': 'CD', 'disc-list': [], 'disc-count': 0, 'track-list': [], 'track-count': 11}], 'medium-track-count': 11, 'medium-count': 1, 'tag-list': [], 'artist-credit-phrase': 'Psychonaut 4'}, {'id': '3fb91753-fb1a-47d5-b83f-5cf88a12b904', 'ext:score': '71', 'title': 'Liber Al Vel Legis', 'status': 'Official', 'packaging': 'Digipak', 'text-representation': {'language': 'eng', 'script': 'Latn'}, 'artist-credit': [{'name': 'Psychonaut', 'artist': {'id': 'e2e692b5-efbc-4cd7-ab38-bce0bd457a8b', 'name': 'Psychonaut 75', 'sort-name': 'Psychonaut 75', 'disambiguation': 'dark industrial dance'}}], 'release-group': {'id': 'bd708a0f-26c5-3fd9-981f-b2149401c8d3', 'type': 'Album', 'title': 'Liber Al Vel Legis', 'primary-type': 'Album'}, 'date': '2001', 'country': 'FR', 'release-event-list': [{'date': '2001', 'area': {'id': '08310658-51eb-3801-80de-5a0739207115', 'name': 'France', 'sort-name': 'France', 'iso-3166-1-code-list': ['FR']}}], 'label-info-list': [{'catalog-number': 'ATNR 013', 'label': {'id': '820c7c21-2d29-4e2f-83cb-692cfd335fab', 'name': 'Athanor'}}], 'medium-list': [{'format': 'CD', 'disc-list': [], 'disc-count': 0, 'track-list': [], 'track-count': 16}], 'medium-track-count': 16, 'medium-count': 1, 'tag-list': [], 'artist-credit-phrase': 'Psychonaut'}, {'id': '5c08a8fe-c644-494b-9a1f-a0943b0cd18b', 'ext:score': '71', 'title': 'Pylon of Daath', 'status': 'Official', 'text-representation': {'language': 'eng', 'script': 'Latn'}, 'artist-credit': [{'name': 'Psychonaut', 'artist': {'id': 'e2e692b5-efbc-4cd7-ab38-bce0bd457a8b', 'name': 'Psychonaut 75', 'sort-name': 'Psychonaut 75', 'disambiguation': 'dark industrial dance'}}], 'release-group': {'id': '7b1b38a1-e024-48d3-a824-b412e633bd4b', 'type': 'Album', 'title': 'Pylon of Daath', 'primary-type': 'Album'}, 'date': '1999', 'country': 'US', 'release-event-list': [{'date': '1999', 'area': {'id': '489ce91b-6658-3307-9877-795b68554c98', 'name': 'United States', 'sort-name': 'United States', 'iso-3166-1-code-list': ['US']}}], 'label-info-list': [{'catalog-number': 'LBRF 008', 'label': {'id': '7ea66d0f-b37d-4010-879e-5fa4c3efe4ad', 'name': 'Live Bait Recording Foundation'}}], 'medium-list': [{'format': 'Cassette', 'disc-list': [], 'disc-count': 0, 'track-list': [], 'track-count': 11}], 'medium-track-count': 11, 'medium-count': 1, 'tag-list': [], 'artist-credit-phrase': 'Psychonaut'}, {'id': '037f04c6-5326-4df2-87fc-a2855e6c43a8', 'ext:score': '71', 'title': 'Antagonistic Pathways', 'status': 'Official', 'text-representation': {'language': 'eng', 'script': 'Latn'}, 'artist-credit': [{'name': 'Psychonaut', 'artist': {'id': '35ed62b6-a358-41a5-8c09-80e7aaae1e1b', 'name': 'Psychonaut', 'sort-name': 'Psychonaut', 'disambiguation': 'German trance group; Lothar Herrmann & Oliver Balser'}}], 'release-group': {'id': 'cb177b0b-1ea2-3f04-8515-846038dac043', 'type': 'Album', 'title': 'Antagonistic Pathways', 'primary-type': 'Album'}, 'date': '2002-07-23', 'country': 'DE', 'release-event-list': [{'date': '2002-07-23', 'area': {'id': '85752fda-13c4-31a3-bee5-0e5cb1f51dad', 'name': 'Germany', 'sort-name': 'Germany', 'iso-3166-1-code-list': ['DE']}}], 'barcode': '5413356178920', 'asin': 'B00006643W', 'label-info-list': [{'catalog-number': 'GTN 1105.20', 'label': {'id': 'e7e72105-7013-495a-b360-47dbe4c7e4e0', 'name': 'GTN'}}], 'medium-list': [{'format': 'CD', 'disc-list': [], 'disc-count': 0, 'track-list': [], 'track-count': 8}], 'medium-track-count': 8, 'medium-count': 1, 'tag-list': [], 'artist-credit-phrase': 'Psychonaut'}, {'id': 'b44b3d0d-6b9d-4ff8-8e80-274cdf3ead59', 'ext:score': '71', 'title': 'Volume 1', 'status': 'Official', 'text-representation': {'language': 'eng', 'script': 'Latn'}, 'artist-credit': [{'name': 'Psychonaut', 'artist': {'id': '35ed62b6-a358-41a5-8c09-80e7aaae1e1b', 'name': 'Psychonaut', 'sort-name': 'Psychonaut', 'disambiguation': 'German trance group; Lothar Herrmann & Oliver Balser'}}], 'release-group': {'id': 'fab107ed-fae5-3a96-abb2-04bb0c3bbb7b', 'type': 'Album', 'title': 'Volume 1', 'primary-type': 'Album'}, 'date': '2000-09', 'country': 'DE', 'release-event-list': [{'date': '2000-09', 'area': {'id': '85752fda-13c4-31a3-bee5-0e5cb1f51dad', 'name': 'Germany', 'sort-name': 'Germany', 'iso-3166-1-code-list': ['DE']}}], 'barcode': '5413356771121', 'label-info-list': [{'catalog-number': 'GTN 1035.20', 'label': {'id': 'e7e72105-7013-495a-b360-47dbe4c7e4e0', 'name': 'GTN'}}], 'medium-list': [{'format': 'CD', 'disc-list': [], 'disc-count': 0, 'track-list': [], 'track-count': 8}], 'medium-track-count': 8, 'medium-count': 1, 'tag-list': [], 'artist-credit-phrase': 'Psychonaut'}, {'id': '98a85c0c-7315-4c0b-b6fb-4c4fcbaccf8c', 'ext:score': '71', 'title': 'Zos Vel Thanatos', 'status': 'Official', 'text-representation': {'language': 'eng', 'script': 'Latn'}, 'artist-credit': [{'name': 'Psychonaut', 'artist': {'id': 'e2e692b5-efbc-4cd7-ab38-bce0bd457a8b', 'name': 'Psychonaut 75', 'sort-name': 'Psychonaut 75', 'disambiguation': 'dark industrial dance'}}], 'release-group': {'id': '4f4be69c-cfb3-35f5-8388-b9e197dc4b2e', 'type': 'Album', 'title': 'Zos Vel Thanatos', 'primary-type': 'Album'}, 'date': '1999', 'country': 'US', 'release-event-list': [{'date': '1999', 'area': {'id': '489ce91b-6658-3307-9877-795b68554c98', 'name': 'United States', 'sort-name': 'United States', 'iso-3166-1-code-list': ['US']}}], 'label-info-list': [{'catalog-number': 'AJNA02.1999', 'label': {'id': '8bffe0f3-8f22-4090-9e64-6d18d4ab7efc', 'name': 'The Ajna Offensive'}}], 'medium-list': [{'format': '7" Vinyl', 'disc-list': [], 'disc-count': 0, 'track-list': [], 'track-count': 2}], 'medium-track-count': 2, 'medium-count': 1, 'tag-list': [], 'artist-credit-phrase': 'Psychonaut'}, {'id': 'db637be8-0f8f-4235-b8fc-dd844341395e', 'ext:score': '71', 'title': "The Witches' Sabbath", 'status': 'Official', 'packaging': 'Jewel Case', 'text-representation': {'language': 'eng', 'script': 'Latn'}, 'artist-credit': [{'name': 'Psychonaut', 'artist': {'id': 'e2e692b5-efbc-4cd7-ab38-bce0bd457a8b', 'name': 'Psychonaut 75', 'sort-name': 'Psychonaut 75', 'disambiguation': 'dark industrial dance'}}], 'release-group': {'id': '66a591cf-f7df-3000-b32c-56fe91a7e2f9', 'type': 'Album', 'title': "The Witches' Sabbath", 'primary-type': 'Album'}, 'date': '2000', 'country': 'FR', 'release-event-list': [{'date': '2000', 'area': {'id': '08310658-51eb-3801-80de-5a0739207115', 'name': 'France', 'sort-name': 'France', 'iso-3166-1-code-list': ['FR']}}], 'barcode': '', 'label-info-list': [{'catalog-number': 'ATNR 007', 'label': {'id': '820c7c21-2d29-4e2f-83cb-692cfd335fab', 'name': 'Athanor'}}], 'medium-list': [{'format': 'CD', 'disc-list': [], 'disc-count': 0, 'track-list': [], 'track-count': 10}], 'medium-track-count': 10, 'medium-count': 1, 'tag-list': [], 'artist-credit-phrase': 'Psychonaut'}, {'id': '9c9824e3-6fb7-4e02-a92b-469d72f36281', 'ext:score': '71', 'title': 'Free-Rider', 'status': 'Official', 'text-representation': {'language': 'eng', 'script': 'Latn'}, 'artist-credit': [{'name': 'Psychonaut', 'artist': {'id': '35ed62b6-a358-41a5-8c09-80e7aaae1e1b', 'name': 'Psychonaut', 'sort-name': 'Psychonaut', 'disambiguation': 'German trance group; Lothar Herrmann & Oliver Balser'}}], 'release-group': {'id': 'ace22e92-832d-4a5d-942d-c5ae6fbd5715', 'type': 'Album', 'title': 'Free-Rider', 'primary-type': 'Album'}, 'date': '2010-09-03', 'country': 'DE', 'release-event-list': [{'date': '2010-09-03', 'area': {'id': '85752fda-13c4-31a3-bee5-0e5cb1f51dad', 'name': 'Germany', 'sort-name': 'Germany', 'iso-3166-1-code-list': ['DE']}}], 'asin': 'B003GNIO7E', 'label-info-list': [{'label': {'id': 'd8a2de8d-1e2b-4ff7-9b78-61dfb26e545d', 'name': 'Crotus Records'}}], 'medium-list': [{'format': 'CD', 'disc-list': [], 'disc-count': 0, 'track-list': [], 'track-count': 9}], 'medium-track-count': 9, 'medium-count': 1, 'tag-list': [], 'artist-credit-phrase': 'Psychonaut'}, {'id': 'c4d68715-beec-4d3a-ab74-1f18682ee437', 'ext:score': '71', 'title': 'Interbeing', 'status': 'Official', 'packaging': 'None', 'text-representation': {'language': 'eng', 'script': 'Latn'}, 'artist-credit': [{'name': 'Psychonaut', 'artist': {'id': '55e84562-e29b-4967-8d2b-a7fff673cd1e', 'name': 'Psychonaut', 'sort-name': 'Psychonaut', 'disambiguation': 'Belgian sludge metal band'}}], 'release-group': {'id': '59e54a49-9a2c-429f-9bbd-56f729018289', 'type': 'Single', 'title': 'Interbeing', 'primary-type': 'Single'}, 'date': '2022-06-28', 'country': 'XW', 'release-event-list': [{'date': '2022-06-28', 'area': {'id': '525d4e18-3d00-31b9-a58b-a146a916de8f', 'name': '[Worldwide]', 'sort-name': '[Worldwide]', 'iso-3166-1-code-list': ['XW']}}], 'label-info-list': [{'label': {'id': 'df682909-df86-4be1-b2e8-1bd12c06bd01', 'name': 'Pelagic Records'}}], 'medium-list': [{'format': 'Digital Media', 'disc-list': [], 'disc-count': 0, 'track-list': [], 'track-count': 1}], 'medium-track-count': 1, 'medium-count': 1, 'tag-list': [], 'artist-credit-phrase': 'Psychonaut'}, {'id': '1b0bfd1c-8698-4177-9bb6-5395f4c2691b', 'ext:score': '71', 'title': '... Jsem tady', 'status': 'Official', 'text-representation': {'language': 'ces', 'script': 'Latn'}, 'artist-credit': [{'name': 'Psychonaut', 'artist': {'id': '48abf0e3-14e6-4bf9-a14e-45bc7f327891', 'name': 'Psychonaut', 'sort-name': 'Psychonaut', 'disambiguation': 'Czech funk rock band from Opava'}}], 'release-group': {'id': '9844eb39-01f0-4144-9dad-b1872b950a89', 'type': 'Album', 'title': '... Jsem tady', 'primary-type': 'Album'}, 'date': '2004', 'country': 'CZ', 'release-event-list': [{'date': '2004', 'area': {'id': '51d34c28-61bf-3d21-849f-7492672a9d44', 'name': 'Czechia', 'sort-name': 'Czechia', 'iso-3166-1-code-list': ['CZ']}}], 'barcode': '8594056290130', 'label-info-list': [{'catalog-number': 'XP 013', 'label': {'id': '1e2ea326-ffac-45f0-a8f3-2e2966d84016', 'name': 'X Production'}}], 'medium-list': [{'format': 'CD', 'disc-list': [], 'disc-count': 0, 'track-list': [], 'track-count': 13}], 'medium-track-count': 13, 'medium-count': 1, 'tag-list': [], 'artist-credit-phrase': 'Psychonaut'}, {'id': '159542de-8d9c-46dc-818b-d9c52c3ca90b', 'ext:score': '71', 'title': 'Live at Penthouse Studio', 'status': 'Official', 'packaging': 'None', 'text-representation': {'language': 'eng', 'script': 'Latn'}, 'artist-credit': [{'name': 'Psychonaut', 'artist': {'id': '55e84562-e29b-4967-8d2b-a7fff673cd1e', 'name': 'Psychonaut', 'sort-name': 'Psychonaut', 'disambiguation': 'Belgian sludge metal band'}}], 'release-group': {'id': 'fc7913fe-6a17-4819-8efd-02b0b3613797', 'type': 'EP', 'title': 'Live at Penthouse Studio', 'primary-type': 'EP', 'secondary-type-list': ['Live']}, 'date': '2021-02-05', 'country': 'XW', 'release-event-list': [{'date': '2021-02-05', 'area': {'id': '525d4e18-3d00-31b9-a58b-a146a916de8f', 'name': '[Worldwide]', 'sort-name': '[Worldwide]', 'iso-3166-1-code-list': ['XW']}}], 'label-info-list': [{'label': {'id': 'df682909-df86-4be1-b2e8-1bd12c06bd01', 'name': 'Pelagic Records'}}], 'medium-list': [{'format': 'Digital Media', 'disc-list': [], 'disc-count': 0, 'track-list': [], 'track-count': 3}], 'medium-track-count': 3, 'medium-count': 1, 'tag-list': [], 'artist-credit-phrase': 'Psychonaut'}, {'id': '3d9703c6-be9e-4043-8a5c-5ea0ea4af923', 'ext:score': '71', 'title': 'The Story of Your Enslavement', 'status': 'Official', 'packaging': 'None', 'text-representation': {'language': 'eng', 'script': 'Latn'}, 'artist-credit': [{'name': 'Psychonaut', 'artist': {'id': '55e84562-e29b-4967-8d2b-a7fff673cd1e', 'name': 'Psychonaut', 'sort-name': 'Psychonaut', 'disambiguation': 'Belgian sludge metal band'}}], 'release-group': {'id': '287f1682-d297-4f6e-8248-0461d3ef06b0', 'type': 'Single', 'title': 'The Story of Your Enslavement', 'primary-type': 'Single'}, 'date': '2020-02-14', 'country': 'XW', 'release-event-list': [{'date': '2020-02-14', 'area': {'id': '525d4e18-3d00-31b9-a58b-a146a916de8f', 'name': '[Worldwide]', 'sort-name': '[Worldwide]', 'iso-3166-1-code-list': ['XW']}}], 'label-info-list': [{'label': {'id': 'df682909-df86-4be1-b2e8-1bd12c06bd01', 'name': 'Pelagic Records'}}], 'medium-list': [{'format': 'Digital Media', 'disc-list': [], 'disc-count': 0, 'track-list': [], 'track-count': 1}], 'medium-track-count': 1, 'medium-count': 1, 'tag-list': [], 'artist-credit-phrase': 'Psychonaut'}, {'id': '7c206048-26d6-4296-b5dd-0bf02dc0c691', 'ext:score': '71', 'title': 'Kabuddah', 'status': 'Official', 'packaging': 'None', 'text-representation': {'language': 'eng', 'script': 'Latn'}, 'artist-credit': [{'name': 'Psychonaut', 'artist': {'id': '55e84562-e29b-4967-8d2b-a7fff673cd1e', 'name': 'Psychonaut', 'sort-name': 'Psychonaut', 'disambiguation': 'Belgian sludge metal band'}}], 'release-group': {'id': 'ac846cfe-e7ff-4e7d-bbfd-a50331fd1396', 'type': 'Single', 'title': 'Kabuddah', 'primary-type': 'Single'}, 'date': '2020-01-31', 'country': 'XW', 'release-event-list': [{'date': '2020-01-31', 'area': {'id': '525d4e18-3d00-31b9-a58b-a146a916de8f', 'name': '[Worldwide]', 'sort-name': '[Worldwide]', 'iso-3166-1-code-list': ['XW']}}], 'label-info-list': [{'label': {'id': 'df682909-df86-4be1-b2e8-1bd12c06bd01', 'name': 'Pelagic Records'}}], 'medium-list': [{'format': 'Digital Media', 'disc-list': [], 'disc-count': 0, 'track-list': [], 'track-count': 1}], 'medium-track-count': 1, 'medium-count': 1, 'tag-list': [], 'artist-credit-phrase': 'Psychonaut'}, {'id': '09cd3222-c863-4c55-a14e-dd7c02ec18be', 'ext:score': '71', 'title': 'Violate Consensus Reality', 'status': 'Official', 'packaging': 'None', 'text-representation': {'language': 'eng', 'script': 'Latn'}, 'artist-credit': [{'name': 'Psychonaut', 'artist': {'id': '55e84562-e29b-4967-8d2b-a7fff673cd1e', 'name': 'Psychonaut', 'sort-name': 'Psychonaut', 'disambiguation': 'Belgian sludge metal band'}}], 'release-group': {'id': '9fd45de3-4497-43c4-88c5-8fbb26cfdfbd', 'type': 'Single', 'title': 'Violate Consensus Reality', 'primary-type': 'Single'}, 'date': '2022-09-08', 'country': 'XW', 'release-event-list': [{'date': '2022-09-08', 'area': {'id': '525d4e18-3d00-31b9-a58b-a146a916de8f', 'name': '[Worldwide]', 'sort-name': '[Worldwide]', 'iso-3166-1-code-list': ['XW']}}], 'label-info-list': [{'label': {'id': 'df682909-df86-4be1-b2e8-1bd12c06bd01', 'name': 'Pelagic Records'}}], 'medium-list': [{'format': 'Digital Media', 'disc-list': [], 'disc-count': 0, 'track-list': [], 'track-count': 2}], 'medium-track-count': 2, 'medium-count': 1, 'tag-list': [], 'artist-credit-phrase': 'Psychonaut'}, {'id': 'e5df1bf9-e573-4549-9791-aa5f87704f69', 'ext:score': '71', 'title': 'Violate Consensus Reality', 'status': 'Official', 'packaging': 'None', 'text-representation': {'language': 'eng', 'script': 'Latn'}, 'artist-credit': [{'name': 'Psychonaut', 'artist': {'id': '55e84562-e29b-4967-8d2b-a7fff673cd1e', 'name': 'Psychonaut', 'sort-name': 'Psychonaut', 'disambiguation': 'Belgian sludge metal band'}}], 'release-group': {'id': 'f1d9dcc3-6287-475b-8ddb-2cc3b8e0e348', 'type': 'Album', 'title': 'Violate Consensus Reality', 'primary-type': 'Album'}, 'date': '2022-10-28', 'country': 'XW', 'release-event-list': [{'date': '2022-10-28', 'area': {'id': '525d4e18-3d00-31b9-a58b-a146a916de8f', 'name': '[Worldwide]', 'sort-name': '[Worldwide]', 'iso-3166-1-code-list': ['XW']}}], 'label-info-list': [{'label': {'id': 'df682909-df86-4be1-b2e8-1bd12c06bd01', 'name': 'Pelagic Records'}}], 'medium-list': [{'format': 'Digital Media', 'disc-list': [], 'disc-count': 0, 'track-list': [], 'track-count': 8}], 'medium-track-count': 8, 'medium-count': 1, 'tag-list': [], 'artist-credit-phrase': 'Psychonaut'}, {'id': '8ce4c671-50e6-4fb7-a1e7-e93344dce010', 'ext:score': '71', 'title': 'Zeměven', 'status': 'Official', 'text-representation': {'language': 'ces', 'script': 'Latn'}, 'artist-credit': [{'name': 'Psychonaut', 'artist': {'id': '48abf0e3-14e6-4bf9-a14e-45bc7f327891', 'name': 'Psychonaut', 'sort-name': 'Psychonaut', 'disambiguation': 'Czech funk rock band from Opava'}}], 'release-group': {'id': '29a0c11f-3700-4133-a15a-5da0d6b1b119', 'type': 'Album', 'title': 'Zeměven', 'primary-type': 'Album'}, 'date': '2001', 'country': 'CZ', 'release-event-list': [{'date': '2001', 'area': {'id': '51d34c28-61bf-3d21-849f-7492672a9d44', 'name': 'Czechia', 'sort-name': 'Czechia', 'iso-3166-1-code-list': ['CZ']}}], 'medium-list': [{'format': 'CD', 'disc-list': [], 'disc-count': 0, 'track-list': [], 'track-count': 9}], 'medium-track-count': 9, 'medium-count': 1, 'tag-list': [], 'artist-credit-phrase': 'Psychonaut'}], 'release-count': 1421} +{'recording-list': [{'id': '20011be9-e649-4a11-ab66-7a2d8bc20c91', 'ext:score': '100', 'title': 'Have a Nice Trip', 'length': '540000', 'artist-credit': [{'name': 'Psychonaut 4', 'artist': {'id': 'c0c720b5-012f-4204-a472-981403f37b12', 'name': 'Psychonaut 4', 'sort-name': 'Psychonaut 4', 'disambiguation': 'Georgian depressive black metal'}}], 'release-list': [{'id': 'e37ff962-2c1e-4af2-887d-89d2d88c4bea', 'title': 'Have a Nice Trip', 'status': 'Official', 'release-group': {'id': '9b1453e9-5b74-43c3-8e4f-0f4c78e46aff', 'type': 'Album', 'title': 'Have a Nice Trip', 'primary-type': 'Album'}, 'date': '2012-11', 'country': 'DE', 'release-event-list': [{'date': '2012-11', 'area': {'id': '85752fda-13c4-31a3-bee5-0e5cb1f51dad', 'name': 'Germany', 'sort-name': 'Germany', 'iso-3166-1-code-list': ['DE']}}], 'medium-list': [{'position': '1', 'format': 'CD', 'track-list': [{'id': '89d0bbd9-5e9c-41c2-80a7-a156e93a8b3a', 'number': '4', 'title': 'Have A Nice Trip', 'length': '540000', 'track_or_recording_length': '540000'}], 'track-count': 11}], 'medium-track-count': 11, 'medium-count': 1}, {'id': 'a3015b31-3b62-4ffa-9184-9fb7bfd39660', 'title': 'Have a Nice Trip', 'status': 'Official', 'release-group': {'id': '9b1453e9-5b74-43c3-8e4f-0f4c78e46aff', 'type': 'Album', 'title': 'Have a Nice Trip', 'primary-type': 'Album'}, 'date': '2012-08-11', 'country': 'UA', 'release-event-list': [{'date': '2012-08-11', 'area': {'id': '904768d0-61ca-3c40-93ac-93adc36fef4b', 'name': 'Ukraine', 'sort-name': 'Ukraine', 'iso-3166-1-code-list': ['UA']}}], 'medium-list': [{'position': '1', 'format': 'Cassette', 'track-list': [{'id': 'be904c08-c8e8-36cb-9ba1-9c479e40afba', 'number': 'A4', 'title': 'Have a Nice Trip', 'length': '540000', 'track_or_recording_length': '540000'}], 'track-count': 11}], 'medium-track-count': 11, 'medium-count': 1}], 'tag-list': [{'count': '1', 'name': 'rock'}, {'count': '1', 'name': 'metal'}, {'count': '2', 'name': 'black metal'}], 'artist-credit-phrase': 'Psychonaut 4'}, {'id': 'c5a7fef9-6391-44be-a5ab-73ac88ff7226', 'ext:score': '100', 'title': 'Overdose Was the Best Way to Die', 'length': '323000', 'artist-credit': [{'name': 'Psychonaut 4', 'artist': {'id': 'c0c720b5-012f-4204-a472-981403f37b12', 'name': 'Psychonaut 4', 'sort-name': 'Psychonaut 4', 'disambiguation': 'Georgian depressive black metal'}}], 'release-list': [{'id': 'e37ff962-2c1e-4af2-887d-89d2d88c4bea', 'title': 'Have a Nice Trip', 'status': 'Official', 'release-group': {'id': '9b1453e9-5b74-43c3-8e4f-0f4c78e46aff', 'type': 'Album', 'title': 'Have a Nice Trip', 'primary-type': 'Album'}, 'date': '2012-11', 'country': 'DE', 'release-event-list': [{'date': '2012-11', 'area': {'id': '85752fda-13c4-31a3-bee5-0e5cb1f51dad', 'name': 'Germany', 'sort-name': 'Germany', 'iso-3166-1-code-list': ['DE']}}], 'medium-list': [{'position': '1', 'format': 'CD', 'track-list': [{'id': 'ad6ea3fd-6df4-43e0-85d0-f388692966b7', 'number': '5', 'title': 'Overdose Was The Best Way To Die', 'length': '323000', 'track_or_recording_length': '323000'}], 'track-count': 11}], 'medium-track-count': 11, 'medium-count': 1}, {'id': 'a3015b31-3b62-4ffa-9184-9fb7bfd39660', 'title': 'Have a Nice Trip', 'status': 'Official', 'release-group': {'id': '9b1453e9-5b74-43c3-8e4f-0f4c78e46aff', 'type': 'Album', 'title': 'Have a Nice Trip', 'primary-type': 'Album'}, 'date': '2012-08-11', 'country': 'UA', 'release-event-list': [{'date': '2012-08-11', 'area': {'id': '904768d0-61ca-3c40-93ac-93adc36fef4b', 'name': 'Ukraine', 'sort-name': 'Ukraine', 'iso-3166-1-code-list': ['UA']}}], 'medium-list': [{'position': '1', 'format': 'Cassette', 'track-list': [{'id': '689f7df6-81a7-3e2a-8c3f-90999bcf468e', 'number': 'A5', 'title': 'Overdose Was the Best Way to Die', 'length': '323000', 'track_or_recording_length': '323000'}], 'track-count': 11}], 'medium-track-count': 11, 'medium-count': 1}], 'artist-credit-phrase': 'Psychonaut 4'}, {'id': '13a505ec-97ab-4053-adf9-c54b5cb944ae', 'ext:score': '100', 'title': 'Drop by Drop', 'length': '411000', 'artist-credit': [{'name': 'Psychonaut 4', 'artist': {'id': 'c0c720b5-012f-4204-a472-981403f37b12', 'name': 'Psychonaut 4', 'sort-name': 'Psychonaut 4', 'disambiguation': 'Georgian depressive black metal'}}], 'release-list': [{'id': 'e37ff962-2c1e-4af2-887d-89d2d88c4bea', 'title': 'Have a Nice Trip', 'status': 'Official', 'release-group': {'id': '9b1453e9-5b74-43c3-8e4f-0f4c78e46aff', 'type': 'Album', 'title': 'Have a Nice Trip', 'primary-type': 'Album'}, 'date': '2012-11', 'country': 'DE', 'release-event-list': [{'date': '2012-11', 'area': {'id': '85752fda-13c4-31a3-bee5-0e5cb1f51dad', 'name': 'Germany', 'sort-name': 'Germany', 'iso-3166-1-code-list': ['DE']}}], 'medium-list': [{'position': '1', 'format': 'CD', 'track-list': [{'id': '3b58ca08-cf3c-430b-8f1d-fce2d7e5f9af', 'number': '8', 'title': 'Drop By Drop (Album Version)', 'length': '411000', 'track_or_recording_length': '411000'}], 'track-count': 11}], 'medium-track-count': 11, 'medium-count': 1}, {'id': 'a3015b31-3b62-4ffa-9184-9fb7bfd39660', 'title': 'Have a Nice Trip', 'status': 'Official', 'release-group': {'id': '9b1453e9-5b74-43c3-8e4f-0f4c78e46aff', 'type': 'Album', 'title': 'Have a Nice Trip', 'primary-type': 'Album'}, 'date': '2012-08-11', 'country': 'UA', 'release-event-list': [{'date': '2012-08-11', 'area': {'id': '904768d0-61ca-3c40-93ac-93adc36fef4b', 'name': 'Ukraine', 'sort-name': 'Ukraine', 'iso-3166-1-code-list': ['UA']}}], 'medium-list': [{'position': '1', 'format': 'Cassette', 'track-list': [{'id': 'ed936633-cf6e-323a-858b-8577dcc1f8e4', 'number': 'B3', 'title': 'Drop by Drop', 'length': '411000', 'track_or_recording_length': '411000'}], 'track-count': 11}], 'medium-track-count': 11, 'medium-count': 1}], 'artist-credit-phrase': 'Psychonaut 4'}, {'id': '71c9beb2-7cf2-416a-a69e-ce6aa05aa327', 'ext:score': '100', 'title': 'Intro', 'length': '139000', 'artist-credit': [{'name': 'Psychonaut 4', 'artist': {'id': 'c0c720b5-012f-4204-a472-981403f37b12', 'name': 'Psychonaut 4', 'sort-name': 'Psychonaut 4', 'disambiguation': 'Georgian depressive black metal'}}], 'release-list': [{'id': 'e37ff962-2c1e-4af2-887d-89d2d88c4bea', 'title': 'Have a Nice Trip', 'status': 'Official', 'release-group': {'id': '9b1453e9-5b74-43c3-8e4f-0f4c78e46aff', 'type': 'Album', 'title': 'Have a Nice Trip', 'primary-type': 'Album'}, 'date': '2012-11', 'country': 'DE', 'release-event-list': [{'date': '2012-11', 'area': {'id': '85752fda-13c4-31a3-bee5-0e5cb1f51dad', 'name': 'Germany', 'sort-name': 'Germany', 'iso-3166-1-code-list': ['DE']}}], 'medium-list': [{'position': '1', 'format': 'CD', 'track-list': [{'id': '84f768d5-a6af-429f-8384-b1f998eda172', 'number': '6', 'title': 'Intro (Part II)', 'length': '139000', 'track_or_recording_length': '139000'}], 'track-count': 11}], 'medium-track-count': 11, 'medium-count': 1}, {'id': 'a3015b31-3b62-4ffa-9184-9fb7bfd39660', 'title': 'Have a Nice Trip', 'status': 'Official', 'release-group': {'id': '9b1453e9-5b74-43c3-8e4f-0f4c78e46aff', 'type': 'Album', 'title': 'Have a Nice Trip', 'primary-type': 'Album'}, 'date': '2012-08-11', 'country': 'UA', 'release-event-list': [{'date': '2012-08-11', 'area': {'id': '904768d0-61ca-3c40-93ac-93adc36fef4b', 'name': 'Ukraine', 'sort-name': 'Ukraine', 'iso-3166-1-code-list': ['UA']}}], 'medium-list': [{'position': '1', 'format': 'Cassette', 'track-list': [{'id': 'b2e1c794-eb71-3b7c-aa9c-73bd31fa28df', 'number': 'B1', 'title': 'Intro', 'length': '139000', 'track_or_recording_length': '139000'}], 'track-count': 11}], 'medium-track-count': 11, 'medium-count': 1}], 'artist-credit-phrase': 'Psychonaut 4'}, {'id': 'b9bcdb8b-4bd4-4252-8f30-8b9a746474e0', 'ext:score': '100', 'title': 'Pseudo', 'length': '279000', 'artist-credit': [{'name': 'Psychonaut 4', 'artist': {'id': 'c0c720b5-012f-4204-a472-981403f37b12', 'name': 'Psychonaut 4', 'sort-name': 'Psychonaut 4', 'disambiguation': 'Georgian depressive black metal'}}], 'release-list': [{'id': 'e37ff962-2c1e-4af2-887d-89d2d88c4bea', 'title': 'Have a Nice Trip', 'status': 'Official', 'release-group': {'id': '9b1453e9-5b74-43c3-8e4f-0f4c78e46aff', 'type': 'Album', 'title': 'Have a Nice Trip', 'primary-type': 'Album'}, 'date': '2012-11', 'country': 'DE', 'release-event-list': [{'date': '2012-11', 'area': {'id': '85752fda-13c4-31a3-bee5-0e5cb1f51dad', 'name': 'Germany', 'sort-name': 'Germany', 'iso-3166-1-code-list': ['DE']}}], 'medium-list': [{'position': '1', 'format': 'CD', 'track-list': [{'id': 'f7e18fd1-ac16-43b4-ab30-586f0775ec16', 'number': '3', 'title': 'Pseudo', 'length': '345000', 'track_or_recording_length': '345000'}], 'track-count': 11}], 'medium-track-count': 11, 'medium-count': 1}, {'id': 'a3015b31-3b62-4ffa-9184-9fb7bfd39660', 'title': 'Have a Nice Trip', 'status': 'Official', 'release-group': {'id': '9b1453e9-5b74-43c3-8e4f-0f4c78e46aff', 'type': 'Album', 'title': 'Have a Nice Trip', 'primary-type': 'Album'}, 'date': '2012-08-11', 'country': 'UA', 'release-event-list': [{'date': '2012-08-11', 'area': {'id': '904768d0-61ca-3c40-93ac-93adc36fef4b', 'name': 'Ukraine', 'sort-name': 'Ukraine', 'iso-3166-1-code-list': ['UA']}}], 'medium-list': [{'position': '1', 'format': 'Cassette', 'track-list': [{'id': '27b4f585-1f3a-3a3e-a6fc-3785e9f98f09', 'number': 'B2', 'title': 'Pseudo', 'length': '279000', 'track_or_recording_length': '279000'}], 'track-count': 11}], 'medium-track-count': 11, 'medium-count': 1}], 'artist-credit-phrase': 'Psychonaut 4'}, {'id': 'b55c4962-f80f-4bd9-9103-4fd319a7b6f6', 'ext:score': '100', 'title': 'Lethargic Dialogue', 'length': '279000', 'artist-credit': [{'name': 'Psychonaut 4', 'artist': {'id': 'c0c720b5-012f-4204-a472-981403f37b12', 'name': 'Psychonaut 4', 'sort-name': 'Psychonaut 4', 'disambiguation': 'Georgian depressive black metal'}}], 'release-list': [{'id': 'e37ff962-2c1e-4af2-887d-89d2d88c4bea', 'title': 'Have a Nice Trip', 'status': 'Official', 'release-group': {'id': '9b1453e9-5b74-43c3-8e4f-0f4c78e46aff', 'type': 'Album', 'title': 'Have a Nice Trip', 'primary-type': 'Album'}, 'date': '2012-11', 'country': 'DE', 'release-event-list': [{'date': '2012-11', 'area': {'id': '85752fda-13c4-31a3-bee5-0e5cb1f51dad', 'name': 'Germany', 'sort-name': 'Germany', 'iso-3166-1-code-list': ['DE']}}], 'medium-list': [{'position': '1', 'format': 'CD', 'track-list': [{'id': '711cda4c-cd20-4974-893d-6a3b5a0a4ae2', 'number': '7', 'title': 'Lethargic Dialogue', 'length': '279000', 'track_or_recording_length': '279000'}], 'track-count': 11}], 'medium-track-count': 11, 'medium-count': 1}, {'id': 'a3015b31-3b62-4ffa-9184-9fb7bfd39660', 'title': 'Have a Nice Trip', 'status': 'Official', 'release-group': {'id': '9b1453e9-5b74-43c3-8e4f-0f4c78e46aff', 'type': 'Album', 'title': 'Have a Nice Trip', 'primary-type': 'Album'}, 'date': '2012-08-11', 'country': 'UA', 'release-event-list': [{'date': '2012-08-11', 'area': {'id': '904768d0-61ca-3c40-93ac-93adc36fef4b', 'name': 'Ukraine', 'sort-name': 'Ukraine', 'iso-3166-1-code-list': ['UA']}}], 'medium-list': [{'position': '1', 'format': 'Cassette', 'track-list': [{'id': 'ceb575a9-fc84-379c-844f-b32413d1dd9f', 'number': 'A3', 'title': 'Lethargic Dialogue', 'length': '345000', 'track_or_recording_length': '345000'}], 'track-count': 11}], 'medium-track-count': 11, 'medium-count': 1}], 'artist-credit-phrase': 'Psychonaut 4'}, {'id': '917481b6-8549-4127-a319-4b8c2198e863', 'ext:score': '100', 'title': 'Hate Parade', 'length': '285000', 'artist-credit': [{'name': 'Psychonaut 4', 'artist': {'id': 'c0c720b5-012f-4204-a472-981403f37b12', 'name': 'Psychonaut 4', 'sort-name': 'Psychonaut 4', 'disambiguation': 'Georgian depressive black metal'}}], 'release-list': [{'id': 'e37ff962-2c1e-4af2-887d-89d2d88c4bea', 'title': 'Have a Nice Trip', 'status': 'Official', 'release-group': {'id': '9b1453e9-5b74-43c3-8e4f-0f4c78e46aff', 'type': 'Album', 'title': 'Have a Nice Trip', 'primary-type': 'Album'}, 'date': '2012-11', 'country': 'DE', 'release-event-list': [{'date': '2012-11', 'area': {'id': '85752fda-13c4-31a3-bee5-0e5cb1f51dad', 'name': 'Germany', 'sort-name': 'Germany', 'iso-3166-1-code-list': ['DE']}}], 'medium-list': [{'position': '1', 'format': 'CD', 'track-list': [{'id': 'c8e6b62c-c350-4cbe-90e2-2a8db4d636aa', 'number': '9', 'title': 'Hate Parade', 'length': '285000', 'track_or_recording_length': '285000'}], 'track-count': 11}], 'medium-track-count': 11, 'medium-count': 1}, {'id': 'a3015b31-3b62-4ffa-9184-9fb7bfd39660', 'title': 'Have a Nice Trip', 'status': 'Official', 'release-group': {'id': '9b1453e9-5b74-43c3-8e4f-0f4c78e46aff', 'type': 'Album', 'title': 'Have a Nice Trip', 'primary-type': 'Album'}, 'date': '2012-08-11', 'country': 'UA', 'release-event-list': [{'date': '2012-08-11', 'area': {'id': '904768d0-61ca-3c40-93ac-93adc36fef4b', 'name': 'Ukraine', 'sort-name': 'Ukraine', 'iso-3166-1-code-list': ['UA']}}], 'medium-list': [{'position': '1', 'format': 'Cassette', 'track-list': [{'id': '03faf038-50d1-363a-b0dd-e0b9a2f0133a', 'number': 'B4', 'title': 'Hate Parade', 'length': '285000', 'track_or_recording_length': '285000'}], 'track-count': 11}], 'medium-track-count': 11, 'medium-count': 1}], 'artist-credit-phrase': 'Psychonaut 4'}, {'id': 'f301ed85-0a36-4d41-b60a-bb31b59ab4e4', 'ext:score': '100', 'title': 'ყლე', 'length': '279000', 'artist-credit': [{'name': 'Psychonaut 4', 'artist': {'id': 'c0c720b5-012f-4204-a472-981403f37b12', 'name': 'Psychonaut 4', 'sort-name': 'Psychonaut 4', 'disambiguation': 'Georgian depressive black metal'}}], 'release-list': [{'id': 'e37ff962-2c1e-4af2-887d-89d2d88c4bea', 'title': 'Have a Nice Trip', 'status': 'Official', 'release-group': {'id': '9b1453e9-5b74-43c3-8e4f-0f4c78e46aff', 'type': 'Album', 'title': 'Have a Nice Trip', 'primary-type': 'Album'}, 'date': '2012-11', 'country': 'DE', 'release-event-list': [{'date': '2012-11', 'area': {'id': '85752fda-13c4-31a3-bee5-0e5cb1f51dad', 'name': 'Germany', 'sort-name': 'Germany', 'iso-3166-1-code-list': ['DE']}}], 'medium-list': [{'position': '1', 'format': 'CD', 'track-list': [{'id': 'bef00ad1-df23-4626-90d5-940f1e8b15e7', 'number': '11', 'title': 'ყლე', 'length': '279000', 'track_or_recording_length': '279000'}], 'track-count': 11}], 'medium-track-count': 11, 'medium-count': 1}, {'id': 'a3015b31-3b62-4ffa-9184-9fb7bfd39660', 'title': 'Have a Nice Trip', 'status': 'Official', 'release-group': {'id': '9b1453e9-5b74-43c3-8e4f-0f4c78e46aff', 'type': 'Album', 'title': 'Have a Nice Trip', 'primary-type': 'Album'}, 'date': '2012-08-11', 'country': 'UA', 'release-event-list': [{'date': '2012-08-11', 'area': {'id': '904768d0-61ca-3c40-93ac-93adc36fef4b', 'name': 'Ukraine', 'sort-name': 'Ukraine', 'iso-3166-1-code-list': ['UA']}}], 'medium-list': [{'position': '1', 'format': 'Cassette', 'track-list': [{'id': 'f7262517-fe52-365b-9770-7f167bb111a8', 'number': 'B6', 'title': 'ყლე', 'length': '279000', 'track_or_recording_length': '279000'}], 'track-count': 11}], 'medium-track-count': 11, 'medium-count': 1}], 'artist-credit-phrase': 'Psychonaut 4'}, {'id': '1cdce395-3618-471b-a7ce-cf05b5e23261', 'ext:score': '100', 'title': 'Parasite', 'length': '458000', 'artist-credit': [{'name': 'Psychonaut 4', 'artist': {'id': 'c0c720b5-012f-4204-a472-981403f37b12', 'name': 'Psychonaut 4', 'sort-name': 'Psychonaut 4', 'disambiguation': 'Georgian depressive black metal'}}], 'release-list': [{'id': 'e37ff962-2c1e-4af2-887d-89d2d88c4bea', 'title': 'Have a Nice Trip', 'status': 'Official', 'release-group': {'id': '9b1453e9-5b74-43c3-8e4f-0f4c78e46aff', 'type': 'Album', 'title': 'Have a Nice Trip', 'primary-type': 'Album'}, 'date': '2012-11', 'country': 'DE', 'release-event-list': [{'date': '2012-11', 'area': {'id': '85752fda-13c4-31a3-bee5-0e5cb1f51dad', 'name': 'Germany', 'sort-name': 'Germany', 'iso-3166-1-code-list': ['DE']}}], 'medium-list': [{'position': '1', 'format': 'CD', 'track-list': [{'id': 'e7a0d5cd-ba0b-41f0-b723-a12a2a47a250', 'number': '2', 'title': 'Parasite', 'length': '458000', 'track_or_recording_length': '458000'}], 'track-count': 11}], 'medium-track-count': 11, 'medium-count': 1}, {'id': 'a3015b31-3b62-4ffa-9184-9fb7bfd39660', 'title': 'Have a Nice Trip', 'status': 'Official', 'release-group': {'id': '9b1453e9-5b74-43c3-8e4f-0f4c78e46aff', 'type': 'Album', 'title': 'Have a Nice Trip', 'primary-type': 'Album'}, 'date': '2012-08-11', 'country': 'UA', 'release-event-list': [{'date': '2012-08-11', 'area': {'id': '904768d0-61ca-3c40-93ac-93adc36fef4b', 'name': 'Ukraine', 'sort-name': 'Ukraine', 'iso-3166-1-code-list': ['UA']}}], 'medium-list': [{'position': '1', 'format': 'Cassette', 'track-list': [{'id': '0b711166-1075-3c69-9c14-a28ca5d0e4e0', 'number': 'A2', 'title': 'Parasite', 'length': '458000', 'track_or_recording_length': '458000'}], 'track-count': 11}], 'medium-track-count': 11, 'medium-count': 1}], 'tag-list': [{'count': '1', 'name': 'rock'}, {'count': '1', 'name': 'metal'}, {'count': '2', 'name': 'black metal'}], 'artist-credit-phrase': 'Psychonaut 4'}, {'id': '4c10e8fb-4e22-4ed1-8a06-8e00c0e104bd', 'ext:score': '100', 'title': 'Intro', 'length': '211000', 'artist-credit': [{'name': 'Psychonaut 4', 'artist': {'id': 'c0c720b5-012f-4204-a472-981403f37b12', 'name': 'Psychonaut 4', 'sort-name': 'Psychonaut 4', 'disambiguation': 'Georgian depressive black metal'}}], 'release-list': [{'id': 'a3015b31-3b62-4ffa-9184-9fb7bfd39660', 'title': 'Have a Nice Trip', 'status': 'Official', 'release-group': {'id': '9b1453e9-5b74-43c3-8e4f-0f4c78e46aff', 'type': 'Album', 'title': 'Have a Nice Trip', 'primary-type': 'Album'}, 'date': '2012-08-11', 'country': 'UA', 'release-event-list': [{'date': '2012-08-11', 'area': {'id': '904768d0-61ca-3c40-93ac-93adc36fef4b', 'name': 'Ukraine', 'sort-name': 'Ukraine', 'iso-3166-1-code-list': ['UA']}}], 'medium-list': [{'position': '1', 'format': 'Cassette', 'track-list': [{'id': 'faa570ec-44de-3ee3-a007-1231230197bb', 'number': 'A1', 'title': 'Intro', 'length': '211000', 'track_or_recording_length': '211000'}], 'track-count': 11}], 'medium-track-count': 11, 'medium-count': 1}], 'tag-list': [{'count': '1', 'name': 'rock'}, {'count': '1', 'name': 'metal'}, {'count': '2', 'name': 'black metal'}], 'artist-credit-phrase': 'Psychonaut 4'}, {'id': 'dd53f08d-27e0-4ce7-aae2-b79e8a53526f', 'ext:score': '100', 'title': 'Serial Lier', 'length': '380000', 'artist-credit': [{'name': 'Psychonaut 4', 'artist': {'id': 'c0c720b5-012f-4204-a472-981403f37b12', 'name': 'Psychonaut 4', 'sort-name': 'Psychonaut 4', 'disambiguation': 'Georgian depressive black metal'}}], 'release-list': [{'id': 'd6e6be74-b7a4-4982-955a-d3361885541c', 'title': '40%', 'status': 'Official', 'release-group': {'id': 'a557bf98-e902-4336-8b28-ecbede7f93bf', 'type': 'Album', 'title': '40%', 'primary-type': 'Album', 'secondary-type-list': ['Demo']}, 'date': '2011-06-04', 'country': 'UA', 'release-event-list': [{'date': '2011-06-04', 'area': {'id': '904768d0-61ca-3c40-93ac-93adc36fef4b', 'name': 'Ukraine', 'sort-name': 'Ukraine', 'iso-3166-1-code-list': ['UA']}}], 'medium-list': [{'position': '1', 'format': 'Cassette', 'track-list': [{'id': '4ea2d709-a6b9-3704-b1c1-1b200997d1a7', 'number': '1', 'title': 'Serial Lier', 'length': '380000', 'track_or_recording_length': '380000'}], 'track-count': 3}], 'medium-track-count': 3, 'medium-count': 1}], 'tag-list': [{'count': '1', 'name': 'rock'}, {'count': '1', 'name': 'metal'}, {'count': '2', 'name': 'black metal'}], 'artist-credit-phrase': 'Psychonaut 4'}, {'id': '97aaef50-a3bf-40e2-b38d-ea47b40611af', 'ext:score': '100', 'title': 'Parasite', 'length': '425000', 'artist-credit': [{'name': 'Psychonaut 4', 'artist': {'id': 'c0c720b5-012f-4204-a472-981403f37b12', 'name': 'Psychonaut 4', 'sort-name': 'Psychonaut 4', 'disambiguation': 'Georgian depressive black metal'}}], 'release-list': [{'id': 'd6e6be74-b7a4-4982-955a-d3361885541c', 'title': '40%', 'status': 'Official', 'release-group': {'id': 'a557bf98-e902-4336-8b28-ecbede7f93bf', 'type': 'Album', 'title': '40%', 'primary-type': 'Album', 'secondary-type-list': ['Demo']}, 'date': '2011-06-04', 'country': 'UA', 'release-event-list': [{'date': '2011-06-04', 'area': {'id': '904768d0-61ca-3c40-93ac-93adc36fef4b', 'name': 'Ukraine', 'sort-name': 'Ukraine', 'iso-3166-1-code-list': ['UA']}}], 'medium-list': [{'position': '1', 'format': 'Cassette', 'track-list': [{'id': 'c24b8241-8611-3259-b976-3118a5465e42', 'number': '2', 'title': 'Parasite', 'length': '425000', 'track_or_recording_length': '425000'}], 'track-count': 3}], 'medium-track-count': 3, 'medium-count': 1}], 'artist-credit-phrase': 'Psychonaut 4'}, {'id': 'd44a8347-ba77-4a1b-9d61-d55f1c60c52f', 'ext:score': '100', 'title': 'Antihuman [Drug = Друг]', 'length': '548000', 'artist-credit': [{'name': 'Psychonaut 4', 'artist': {'id': 'c0c720b5-012f-4204-a472-981403f37b12', 'name': 'Psychonaut 4', 'sort-name': 'Psychonaut 4', 'disambiguation': 'Georgian depressive black metal'}}], 'release-list': [{'id': 'e37ff962-2c1e-4af2-887d-89d2d88c4bea', 'title': 'Have a Nice Trip', 'status': 'Official', 'release-group': {'id': '9b1453e9-5b74-43c3-8e4f-0f4c78e46aff', 'type': 'Album', 'title': 'Have a Nice Trip', 'primary-type': 'Album'}, 'date': '2012-11', 'country': 'DE', 'release-event-list': [{'date': '2012-11', 'area': {'id': '85752fda-13c4-31a3-bee5-0e5cb1f51dad', 'name': 'Germany', 'sort-name': 'Germany', 'iso-3166-1-code-list': ['DE']}}], 'medium-list': [{'position': '1', 'format': 'CD', 'track-list': [{'id': 'bd8d9250-749e-4968-ab24-8f5c74ebc58a', 'number': '10', 'title': 'Antihuman [Drug=Друг]', 'length': '548000', 'track_or_recording_length': '548000'}], 'track-count': 11}], 'medium-track-count': 11, 'medium-count': 1}, {'id': 'a3015b31-3b62-4ffa-9184-9fb7bfd39660', 'title': 'Have a Nice Trip', 'status': 'Official', 'release-group': {'id': '9b1453e9-5b74-43c3-8e4f-0f4c78e46aff', 'type': 'Album', 'title': 'Have a Nice Trip', 'primary-type': 'Album'}, 'date': '2012-08-11', 'country': 'UA', 'release-event-list': [{'date': '2012-08-11', 'area': {'id': '904768d0-61ca-3c40-93ac-93adc36fef4b', 'name': 'Ukraine', 'sort-name': 'Ukraine', 'iso-3166-1-code-list': ['UA']}}], 'medium-list': [{'position': '1', 'format': 'Cassette', 'track-list': [{'id': 'be7a59ea-5000-3cad-ae91-4e2f540a33e8', 'number': 'B5', 'title': 'Antihuman [Drug=Друг]', 'length': '548000', 'track_or_recording_length': '548000'}], 'track-count': 11}], 'medium-track-count': 11, 'medium-count': 1}, {'id': 'd6e6be74-b7a4-4982-955a-d3361885541c', 'title': '40%', 'status': 'Official', 'release-group': {'id': 'a557bf98-e902-4336-8b28-ecbede7f93bf', 'type': 'Album', 'title': '40%', 'primary-type': 'Album', 'secondary-type-list': ['Demo']}, 'date': '2011-06-04', 'country': 'UA', 'release-event-list': [{'date': '2011-06-04', 'area': {'id': '904768d0-61ca-3c40-93ac-93adc36fef4b', 'name': 'Ukraine', 'sort-name': 'Ukraine', 'iso-3166-1-code-list': ['UA']}}], 'medium-list': [{'position': '1', 'format': 'Cassette', 'track-list': [{'id': 'e3931ca8-1281-3b38-b4cb-6f2f0503a87b', 'number': '3', 'title': 'Antihuman [Drug = Друг]', 'length': '548000', 'track_or_recording_length': '548000'}], 'track-count': 3}], 'medium-track-count': 3, 'medium-count': 1}], 'artist-credit-phrase': 'Psychonaut 4'}, {'id': 'e183ef11-de34-4fa4-9381-44a7117e8611', 'ext:score': '100', 'title': 'Nackskott', 'length': '175000', 'artist-credit': [{'name': 'Psychonaut 4', 'artist': {'id': 'c0c720b5-012f-4204-a472-981403f37b12', 'name': 'Psychonaut 4', 'sort-name': 'Psychonaut 4', 'disambiguation': 'Georgian depressive black metal'}}], 'release-list': [{'id': '27f00fb8-983c-4d5c-950f-51418aac55dc', 'title': 'Tired, Numb, Still Alive', 'status': 'Official', 'artist-credit': [{'name': 'Unjoy', 'artist': {'id': '52685d5c-f5a9-40f9-8664-dc083ff242bd', 'name': 'Unjoy', 'sort-name': 'Unjoy', 'disambiguation': 'Black Metal band'}}, ' / ', {'name': 'Psychonaut 4', 'artist': {'id': 'c0c720b5-012f-4204-a472-981403f37b12', 'name': 'Psychonaut 4', 'sort-name': 'Psychonaut 4', 'disambiguation': 'Georgian depressive black metal'}}, ' / ', {'name': 'Eurythmie', 'artist': {'id': 'c148d680-4739-429a-8550-53c098ac0b16', 'name': 'Eurythmie', 'sort-name': 'Eurythmie'}}], 'release-group': {'id': '6f46e1a4-0d33-48f5-b809-b131c369c97e', 'type': 'Album', 'title': 'Tired, Numb, Still Alive', 'primary-type': 'Album'}, 'date': '2012-06-15', 'country': 'DE', 'release-event-list': [{'date': '2012-06-15', 'area': {'id': '85752fda-13c4-31a3-bee5-0e5cb1f51dad', 'name': 'Germany', 'sort-name': 'Germany', 'iso-3166-1-code-list': ['DE']}}], 'medium-list': [{'position': '1', 'format': 'CD', 'track-list': [{'id': 'f788c549-ece2-370b-95de-b6219347da45', 'number': '6', 'title': 'Nackskott', 'length': '175000', 'track_or_recording_length': '175000'}], 'track-count': 6}], 'medium-track-count': 6, 'medium-count': 1, 'artist-credit-phrase': 'Unjoy / Psychonaut 4 / Eurythmie'}], 'artist-credit-phrase': 'Psychonaut 4'}, {'id': '83a30323-aee1-401a-b767-b3c1bdd026c0', 'ext:score': '100', 'title': 'Drop by Drop', 'length': '414000', 'artist-credit': [{'name': 'Psychonaut 4', 'artist': {'id': 'c0c720b5-012f-4204-a472-981403f37b12', 'name': 'Psychonaut 4', 'sort-name': 'Psychonaut 4', 'disambiguation': 'Georgian depressive black metal'}}], 'release-list': [{'id': '27f00fb8-983c-4d5c-950f-51418aac55dc', 'title': 'Tired, Numb, Still Alive', 'status': 'Official', 'artist-credit': [{'name': 'Unjoy', 'artist': {'id': '52685d5c-f5a9-40f9-8664-dc083ff242bd', 'name': 'Unjoy', 'sort-name': 'Unjoy', 'disambiguation': 'Black Metal band'}}, ' / ', {'name': 'Psychonaut 4', 'artist': {'id': 'c0c720b5-012f-4204-a472-981403f37b12', 'name': 'Psychonaut 4', 'sort-name': 'Psychonaut 4', 'disambiguation': 'Georgian depressive black metal'}}, ' / ', {'name': 'Eurythmie', 'artist': {'id': 'c148d680-4739-429a-8550-53c098ac0b16', 'name': 'Eurythmie', 'sort-name': 'Eurythmie'}}], 'release-group': {'id': '6f46e1a4-0d33-48f5-b809-b131c369c97e', 'type': 'Album', 'title': 'Tired, Numb, Still Alive', 'primary-type': 'Album'}, 'date': '2012-06-15', 'country': 'DE', 'release-event-list': [{'date': '2012-06-15', 'area': {'id': '85752fda-13c4-31a3-bee5-0e5cb1f51dad', 'name': 'Germany', 'sort-name': 'Germany', 'iso-3166-1-code-list': ['DE']}}], 'medium-list': [{'position': '1', 'format': 'CD', 'track-list': [{'id': 'f8efd599-3df2-342c-9521-b531ca74c89d', 'number': '5', 'title': 'Drop by Drop', 'length': '414000', 'track_or_recording_length': '414000'}], 'track-count': 6}], 'medium-track-count': 6, 'medium-count': 1, 'artist-credit-phrase': 'Unjoy / Psychonaut 4 / Eurythmie'}], 'artist-credit-phrase': 'Psychonaut 4'}, {'id': '975a7b3d-d66d-431b-8a76-4aba81ddad83', 'ext:score': '100', 'title': 'I Measure Time in Mililiters', 'length': '377920', 'artist-credit': [{'name': 'Psychonaut 4', 'artist': {'id': 'c0c720b5-012f-4204-a472-981403f37b12', 'name': 'Psychonaut 4', 'sort-name': 'Psychonaut 4', 'disambiguation': 'Georgian depressive black metal'}}], 'release-list': [{'id': '66b70765-0c9f-41ff-9190-4830a3bd59e4', 'title': 'Urban Negativism', 'status': 'Official', 'artist-credit': [{'name': 'Psychonaut 4', 'artist': {'id': 'c0c720b5-012f-4204-a472-981403f37b12', 'name': 'Psychonaut 4', 'sort-name': 'Psychonaut 4', 'disambiguation': 'Georgian depressive black metal'}}, ' / ', {'name': 'In Luna', 'artist': {'id': 'fe7d5a6d-ad23-4444-aad5-14b7bf64304c', 'name': 'In Luna', 'sort-name': 'In Luna'}}, ' / ', {'name': 'Ofdrykkja', 'artist': {'id': '553462b6-fbb5-4bee-8536-d524ae3b5d13', 'name': 'Ofdrykkja', 'sort-name': 'Ofdrykkja'}}, ' / ', {'name': 'Vanhelga', 'artist': {'id': '20365b56-cd76-4f49-8e1d-87251724bbd8', 'name': 'Vanhelga', 'sort-name': 'Vanhelga'}}], 'release-group': {'id': '3378a8fd-8d9a-4bdf-85f1-10d630213053', 'type': 'Album', 'title': 'Urban Negativism', 'primary-type': 'Album'}, 'date': '2015-04-06', 'country': 'XW', 'release-event-list': [{'date': '2015-04-06', 'area': {'id': '525d4e18-3d00-31b9-a58b-a146a916de8f', 'name': '[Worldwide]', 'sort-name': '[Worldwide]', 'iso-3166-1-code-list': ['XW']}}], 'medium-list': [{'position': '1', 'format': 'CD', 'track-list': [{'id': '7debff25-a430-458b-9aea-f139d276b4c6', 'number': '7', 'title': 'I Measure Time in Mililiters', 'length': '377920', 'track_or_recording_length': '377920'}], 'track-count': 10}], 'medium-track-count': 10, 'medium-count': 1, 'artist-credit-phrase': 'Psychonaut 4 / In Luna / Ofdrykkja / Vanhelga'}], 'artist-credit-phrase': 'Psychonaut 4'}, {'id': 'cb0ad965-c7d6-4510-994c-1351134e9879', 'ext:score': '100', 'title': 'In a Good Movie Hero Always Dies', 'length': '199453', 'artist-credit': [{'name': 'Psychonaut 4', 'artist': {'id': 'c0c720b5-012f-4204-a472-981403f37b12', 'name': 'Psychonaut 4', 'sort-name': 'Psychonaut 4', 'disambiguation': 'Georgian depressive black metal'}}], 'release-list': [{'id': '66b70765-0c9f-41ff-9190-4830a3bd59e4', 'title': 'Urban Negativism', 'status': 'Official', 'artist-credit': [{'name': 'Psychonaut 4', 'artist': {'id': 'c0c720b5-012f-4204-a472-981403f37b12', 'name': 'Psychonaut 4', 'sort-name': 'Psychonaut 4', 'disambiguation': 'Georgian depressive black metal'}}, ' / ', {'name': 'In Luna', 'artist': {'id': 'fe7d5a6d-ad23-4444-aad5-14b7bf64304c', 'name': 'In Luna', 'sort-name': 'In Luna'}}, ' / ', {'name': 'Ofdrykkja', 'artist': {'id': '553462b6-fbb5-4bee-8536-d524ae3b5d13', 'name': 'Ofdrykkja', 'sort-name': 'Ofdrykkja'}}, ' / ', {'name': 'Vanhelga', 'artist': {'id': '20365b56-cd76-4f49-8e1d-87251724bbd8', 'name': 'Vanhelga', 'sort-name': 'Vanhelga'}}], 'release-group': {'id': '3378a8fd-8d9a-4bdf-85f1-10d630213053', 'type': 'Album', 'title': 'Urban Negativism', 'primary-type': 'Album'}, 'date': '2015-04-06', 'country': 'XW', 'release-event-list': [{'date': '2015-04-06', 'area': {'id': '525d4e18-3d00-31b9-a58b-a146a916de8f', 'name': '[Worldwide]', 'sort-name': '[Worldwide]', 'iso-3166-1-code-list': ['XW']}}], 'medium-list': [{'position': '1', 'format': 'CD', 'track-list': [{'id': '15d8d514-9a95-44a5-a3a3-fc62ccc67d48', 'number': '8', 'title': 'In a Good Movie Hero Always Dies', 'length': '199453', 'track_or_recording_length': '199453'}], 'track-count': 10}], 'medium-track-count': 10, 'medium-count': 1, 'artist-credit-phrase': 'Psychonaut 4 / In Luna / Ofdrykkja / Vanhelga'}], 'artist-credit-phrase': 'Psychonaut 4'}, {'id': '0c8cbbc4-84be-4f5a-8a56-be1777029960', 'ext:score': '100', 'title': 'Not a Love Song', 'length': '353613', 'artist-credit': [{'name': 'Psychonaut 4', 'artist': {'id': 'c0c720b5-012f-4204-a472-981403f37b12', 'name': 'Psychonaut 4', 'sort-name': 'Psychonaut 4', 'disambiguation': 'Georgian depressive black metal'}}], 'release-list': [{'id': '66b70765-0c9f-41ff-9190-4830a3bd59e4', 'title': 'Urban Negativism', 'status': 'Official', 'artist-credit': [{'name': 'Psychonaut 4', 'artist': {'id': 'c0c720b5-012f-4204-a472-981403f37b12', 'name': 'Psychonaut 4', 'sort-name': 'Psychonaut 4', 'disambiguation': 'Georgian depressive black metal'}}, ' / ', {'name': 'In Luna', 'artist': {'id': 'fe7d5a6d-ad23-4444-aad5-14b7bf64304c', 'name': 'In Luna', 'sort-name': 'In Luna'}}, ' / ', {'name': 'Ofdrykkja', 'artist': {'id': '553462b6-fbb5-4bee-8536-d524ae3b5d13', 'name': 'Ofdrykkja', 'sort-name': 'Ofdrykkja'}}, ' / ', {'name': 'Vanhelga', 'artist': {'id': '20365b56-cd76-4f49-8e1d-87251724bbd8', 'name': 'Vanhelga', 'sort-name': 'Vanhelga'}}], 'release-group': {'id': '3378a8fd-8d9a-4bdf-85f1-10d630213053', 'type': 'Album', 'title': 'Urban Negativism', 'primary-type': 'Album'}, 'date': '2015-04-06', 'country': 'XW', 'release-event-list': [{'date': '2015-04-06', 'area': {'id': '525d4e18-3d00-31b9-a58b-a146a916de8f', 'name': '[Worldwide]', 'sort-name': '[Worldwide]', 'iso-3166-1-code-list': ['XW']}}], 'medium-list': [{'position': '1', 'format': 'CD', 'track-list': [{'id': 'ed49885b-037d-45a7-b642-8dc11a194c89', 'number': '6', 'title': 'Not a Love Song', 'length': '353613', 'track_or_recording_length': '353613'}], 'track-count': 10}], 'medium-track-count': 10, 'medium-count': 1, 'artist-credit-phrase': 'Psychonaut 4 / In Luna / Ofdrykkja / Vanhelga'}], 'artist-credit-phrase': 'Psychonaut 4'}, {'id': 'f6424595-9d4d-4a44-908b-99bdea5fa7e7', 'ext:score': '100', 'title': 'Prologue', 'length': '450000', 'artist-credit': [{'name': 'Psychonaut 4', 'artist': {'id': 'c0c720b5-012f-4204-a472-981403f37b12', 'name': 'Psychonaut 4', 'sort-name': 'Psychonaut 4', 'disambiguation': 'Georgian depressive black metal'}}], 'release-list': [{'id': '0d229a02-74f6-4c77-8c20-6612295870ae', 'title': 'Neurasthenia', 'status': 'Official', 'release-group': {'id': '855c95c7-b7c2-40b2-b453-e3d37f43401e', 'type': 'Album', 'title': 'Neurasthenia', 'primary-type': 'Album'}, 'date': '2016-10-07', 'country': 'AT', 'release-event-list': [{'date': '2016-10-07', 'area': {'id': 'caac77d1-a5c8-3e6e-8e27-90b44dcc1446', 'name': 'Austria', 'sort-name': 'Austria', 'iso-3166-1-code-list': ['AT']}}], 'medium-list': [{'position': '1', 'format': 'CD', 'track-list': [{'id': '015ba6d6-cb44-41a6-931f-3e40f68a0f2c', 'number': '1', 'title': 'Prologue', 'length': '450000', 'track_or_recording_length': '450000'}], 'track-count': 10}], 'medium-track-count': 10, 'medium-count': 1}], 'artist-credit-phrase': 'Psychonaut 4'}, {'id': 'a154d647-80ea-4372-8590-b307ed10999c', 'ext:score': '100', 'title': 'Death Is a Form of Art', 'length': '498000', 'artist-credit': [{'name': 'Psychonaut 4', 'artist': {'id': 'c0c720b5-012f-4204-a472-981403f37b12', 'name': 'Psychonaut 4', 'sort-name': 'Psychonaut 4', 'disambiguation': 'Georgian depressive black metal'}}], 'release-list': [{'id': '0d229a02-74f6-4c77-8c20-6612295870ae', 'title': 'Neurasthenia', 'status': 'Official', 'release-group': {'id': '855c95c7-b7c2-40b2-b453-e3d37f43401e', 'type': 'Album', 'title': 'Neurasthenia', 'primary-type': 'Album'}, 'date': '2016-10-07', 'country': 'AT', 'release-event-list': [{'date': '2016-10-07', 'area': {'id': 'caac77d1-a5c8-3e6e-8e27-90b44dcc1446', 'name': 'Austria', 'sort-name': 'Austria', 'iso-3166-1-code-list': ['AT']}}], 'medium-list': [{'position': '1', 'format': 'CD', 'track-list': [{'id': '2e558d0a-ce38-4b78-8902-497a1d1e8a91', 'number': '2', 'title': 'Death Is a Form of Art', 'length': '498000', 'track_or_recording_length': '498000'}], 'track-count': 10}], 'medium-track-count': 10, 'medium-count': 1}], 'artist-credit-phrase': 'Psychonaut 4'}, {'id': '19baec10-dd65-4826-bc2b-daaea6004cb7', 'ext:score': '100', 'title': 'Bad t.RIP', 'length': '519000', 'artist-credit': [{'name': 'Psychonaut 4', 'artist': {'id': 'c0c720b5-012f-4204-a472-981403f37b12', 'name': 'Psychonaut 4', 'sort-name': 'Psychonaut 4', 'disambiguation': 'Georgian depressive black metal'}}], 'release-list': [{'id': '0d229a02-74f6-4c77-8c20-6612295870ae', 'title': 'Neurasthenia', 'status': 'Official', 'release-group': {'id': '855c95c7-b7c2-40b2-b453-e3d37f43401e', 'type': 'Album', 'title': 'Neurasthenia', 'primary-type': 'Album'}, 'date': '2016-10-07', 'country': 'AT', 'release-event-list': [{'date': '2016-10-07', 'area': {'id': 'caac77d1-a5c8-3e6e-8e27-90b44dcc1446', 'name': 'Austria', 'sort-name': 'Austria', 'iso-3166-1-code-list': ['AT']}}], 'medium-list': [{'position': '1', 'format': 'CD', 'track-list': [{'id': '44106bbc-dbed-485d-9c91-2ee100680109', 'number': '5', 'title': 'Bad t.RIP', 'length': '519000', 'track_or_recording_length': '519000'}], 'track-count': 10}], 'medium-track-count': 10, 'medium-count': 1}], 'artist-credit-phrase': 'Psychonaut 4'}, {'id': '5ff80fa9-8e63-42e7-9a3d-49befc5101c9', 'ext:score': '100', 'title': 'Song Written in Paris', 'length': '353000', 'artist-credit': [{'name': 'Psychonaut 4', 'artist': {'id': 'c0c720b5-012f-4204-a472-981403f37b12', 'name': 'Psychonaut 4', 'sort-name': 'Psychonaut 4', 'disambiguation': 'Georgian depressive black metal'}}], 'release-list': [{'id': '0d229a02-74f6-4c77-8c20-6612295870ae', 'title': 'Neurasthenia', 'status': 'Official', 'release-group': {'id': '855c95c7-b7c2-40b2-b453-e3d37f43401e', 'type': 'Album', 'title': 'Neurasthenia', 'primary-type': 'Album'}, 'date': '2016-10-07', 'country': 'AT', 'release-event-list': [{'date': '2016-10-07', 'area': {'id': 'caac77d1-a5c8-3e6e-8e27-90b44dcc1446', 'name': 'Austria', 'sort-name': 'Austria', 'iso-3166-1-code-list': ['AT']}}], 'medium-list': [{'position': '1', 'format': 'CD', 'track-list': [{'id': '883577c7-eac4-4274-ab31-3d55adf910da', 'number': '6', 'title': 'Song Written in Paris', 'length': '353000', 'track_or_recording_length': '353000'}], 'track-count': 10}], 'medium-track-count': 10, 'medium-count': 1}], 'artist-credit-phrase': 'Psychonaut 4'}, {'id': '0f8ddb6b-5e2f-4817-83bf-069edd6c7daf', 'ext:score': '100', 'title': 'Thoughts of Death', 'length': '457000', 'artist-credit': [{'name': 'Psychonaut 4', 'artist': {'id': 'c0c720b5-012f-4204-a472-981403f37b12', 'name': 'Psychonaut 4', 'sort-name': 'Psychonaut 4', 'disambiguation': 'Georgian depressive black metal'}}], 'release-list': [{'id': '0d229a02-74f6-4c77-8c20-6612295870ae', 'title': 'Neurasthenia', 'status': 'Official', 'release-group': {'id': '855c95c7-b7c2-40b2-b453-e3d37f43401e', 'type': 'Album', 'title': 'Neurasthenia', 'primary-type': 'Album'}, 'date': '2016-10-07', 'country': 'AT', 'release-event-list': [{'date': '2016-10-07', 'area': {'id': 'caac77d1-a5c8-3e6e-8e27-90b44dcc1446', 'name': 'Austria', 'sort-name': 'Austria', 'iso-3166-1-code-list': ['AT']}}], 'medium-list': [{'position': '1', 'format': 'CD', 'track-list': [{'id': 'e6737a96-7646-4e1d-93d0-57df708a99d1', 'number': '10', 'title': 'Thoughts of Death', 'length': '457000', 'track_or_recording_length': '457000'}], 'track-count': 10}], 'medium-track-count': 10, 'medium-count': 1}], 'artist-credit-phrase': 'Psychonaut 4'}, {'id': '7bdd08b3-efd7-4421-89bf-d499b36c230d', 'ext:score': '100', 'title': 'I Wanna Be Your Dog', 'length': '308000', 'artist-credit': [{'name': 'Psychonaut 4', 'artist': {'id': 'c0c720b5-012f-4204-a472-981403f37b12', 'name': 'Psychonaut 4', 'sort-name': 'Psychonaut 4', 'disambiguation': 'Georgian depressive black metal'}}], 'release-list': [{'id': 'ab61f4b0-88e8-43b5-9b24-af184c6f1f12', 'title': 'Free Portion ov Madness I', 'status': 'Official', 'release-group': {'id': 'fe7f2480-fa00-483e-bad6-e6fc839ec3ea', 'type': 'Single', 'title': 'Free Portion ov Madness I', 'primary-type': 'Single'}, 'date': '2014-04-11', 'country': 'XW', 'release-event-list': [{'date': '2014-04-11', 'area': {'id': '525d4e18-3d00-31b9-a58b-a146a916de8f', 'name': '[Worldwide]', 'sort-name': '[Worldwide]', 'iso-3166-1-code-list': ['XW']}}], 'medium-list': [{'position': '1', 'format': 'Digital Media', 'track-list': [{'id': '2cbaf960-3e0d-4ccf-933d-7a790f051dec', 'number': '1', 'title': 'I Wanna Be Your Dog', 'length': '308000', 'track_or_recording_length': '308000'}], 'track-count': 1}], 'medium-track-count': 1, 'medium-count': 1}], 'artist-credit-phrase': 'Psychonaut 4'}, {'id': '6b40186b-6678-4328-a4b8-eb7c9806a9fb', 'ext:score': '100', 'title': 'Sweet Decadance', 'length': '718000', 'artist-credit': [{'name': 'Psychonaut 4', 'artist': {'id': 'c0c720b5-012f-4204-a472-981403f37b12', 'name': 'Psychonaut 4', 'sort-name': 'Psychonaut 4', 'disambiguation': 'Georgian depressive black metal'}}], 'release-list': [{'id': '0d229a02-74f6-4c77-8c20-6612295870ae', 'title': 'Neurasthenia', 'status': 'Official', 'release-group': {'id': '855c95c7-b7c2-40b2-b453-e3d37f43401e', 'type': 'Album', 'title': 'Neurasthenia', 'primary-type': 'Album'}, 'date': '2016-10-07', 'country': 'AT', 'release-event-list': [{'date': '2016-10-07', 'area': {'id': 'caac77d1-a5c8-3e6e-8e27-90b44dcc1446', 'name': 'Austria', 'sort-name': 'Austria', 'iso-3166-1-code-list': ['AT']}}], 'medium-list': [{'position': '1', 'format': 'CD', 'track-list': [{'id': 'b01fa728-514b-4730-879c-4ffc1df2554a', 'number': '3', 'title': 'Sweet Decadance', 'length': '718000', 'track_or_recording_length': '718000'}], 'track-count': 10}], 'medium-track-count': 10, 'medium-count': 1}], 'artist-credit-phrase': 'Psychonaut 4'}], 'recording-count': 13023} + +""" + +if __name__ == "__main__": + import musicbrainzngs + + musicbrainzngs.set_useragent("metadata receiver", "0.1", "https://github.com/HeIIow2/music-downloader") + options = Options([musicbrainzngs.search_artists("Crystal F")]) + options.choose(0) + print(options.get_current_option()) diff --git a/src/metadata/search.py b/src/metadata/search.py new file mode 100644 index 0000000..158fb31 --- /dev/null +++ b/src/metadata/search.py @@ -0,0 +1,370 @@ +from typing import List +import logging +import musicbrainzngs + +try: + from object_handeling import get_elem_from_obj, parse_music_brainz_date + +except ModuleNotFoundError: + from metadata.object_handeling import get_elem_from_obj, parse_music_brainz_date + +mb_log = logging.getLogger("musicbrainzngs") +mb_log.setLevel(logging.WARNING) +musicbrainzngs.set_useragent("metadata receiver", "0.1", "https://github.com/HeIIow2/music-downloader") + +MAX_PARAMATERS = 3 +OPTION_TYPES = ['artist', 'release_group', 'release', 'recording'] + + +class Option: + def __init__(self, type_: str, id_: str, name: str, additional_info: str = "") -> None: + if type_ not in OPTION_TYPES: + raise ValueError(f"type: {type_} doesn't exist. Leagal Values: {OPTION_TYPES}") + self.type = type_ + self.name = name + self.id = id_ + + self.additional_info = additional_info + + def __getitem__(self, item): + map_ = { + "id": self.id, + "type": self.type, + "kind": self.type, + "name": self.name + } + return map_[item] + + def __repr__(self) -> str: + type_repr = { + 'artist': 'artist\t\t', + 'release_group': 'release group\t', + 'release': 'release\t\t', + 'recording': 'recording\t' + } + return f"{type_repr[self.type]}: \"{self.name}\"{self.additional_info}" + +class MultipleOptions: + def __init__(self, option_list: List[Option]) -> None: + self.option_list = option_list + + def __repr__(self) -> str: + return "\n".join([f"{str(i).zfill(2)}) {choice.__repr__()}" for i, choice in enumerate(self.option_list)]) + + +class Search: + def __init__(self, logger: logging.Logger) -> None: + self.logger = logger + + self.options_history = [] + self.current_option: Option + + def append_new_choices(self, new_choices: List[Option]) -> MultipleOptions: + self.options_history.append(new_choices) + return MultipleOptions(new_choices) + + def get_previous_options(self): + self.options_history.pop(-1) + return MultipleOptions(self.options_history[-1]) + + @staticmethod + def fetch_new_options_from_artist(artist: Option): + """ + returning list of artist and every release group + """ + result = musicbrainzngs.get_artist_by_id(artist.id, includes=["release-groups", "releases"]) + artist_data = get_elem_from_obj(result, ['artist'], return_if_none={}) + + result = [artist] + + # sort all release groups by date and add album sort to have them in chronological order. + release_group_list = artist_data['release-group-list'] + for i, release_group in enumerate(release_group_list): + release_group_list[i]['first-release-date'] = parse_music_brainz_date(release_group['first-release-date']) + release_group_list.sort(key=lambda x: x['first-release-date']) + release_group_list = [Option("release_group", get_elem_from_obj(release_group_, ['id']), + get_elem_from_obj(release_group_, ['title']), + additional_info=f" ({get_elem_from_obj(release_group_, ['type'])}) from {get_elem_from_obj(release_group_, ['first-release-date'])}") + for release_group_ in release_group_list] + + result.extend(release_group_list) + return result + + @staticmethod + def fetch_new_options_from_release_group(release_group: Option): + """ + returning list including the artists, the releases and the tracklist of the first release + """ + results = [] + + result = musicbrainzngs.get_release_group_by_id(release_group.id, + includes=["artist-credits", "releases"]) + release_group_data = get_elem_from_obj(result, ['release-group'], return_if_none={}) + artist_datas = get_elem_from_obj(release_group_data, ['artist-credit'], return_if_none={}) + release_datas = get_elem_from_obj(release_group_data, ['release-list'], return_if_none={}) + + # appending all the artists to results + for artist_data in artist_datas: + results.append(Option('artist', get_elem_from_obj(artist_data, ['artist', 'id']), + get_elem_from_obj(artist_data, ['artist', 'name']))) + + # appending initial release group + results.append(release_group) + + # appending all releases + first_release = None + for i, release_data in enumerate(release_datas): + results.append( + Option('release', get_elem_from_obj(release_data, ['id']), get_elem_from_obj(release_data, ['title']), + additional_info=f" ({get_elem_from_obj(release_data, ['status'])})")) + if i == 0: + first_release = results[-1] + + # append tracklist of first release + if first_release is not None: + results.extend(Search.fetch_new_options_from_release(first_release, only_tracklist=True)) + + return results + + @staticmethod + def fetch_new_options_from_release(release: Option, only_tracklist: bool = False): + """ + artists + release group + release + tracklist + """ + results = [] + result = musicbrainzngs.get_release_by_id(release.id, + includes=["recordings", "labels", "release-groups", "artist-credits"]) + release_data = get_elem_from_obj(result, ['release'], return_if_none={}) + label_data = get_elem_from_obj(release_data, ['label-info-list'], return_if_none={}) + recording_datas = get_elem_from_obj(release_data, ['medium-list', 0, 'track-list'], return_if_none=[]) + release_group_data = get_elem_from_obj(release_data, ['release-group'], return_if_none={}) + artist_datas = get_elem_from_obj(release_data, ['artist-credit'], return_if_none={}) + + # appending all the artists to results + for artist_data in artist_datas: + results.append(Option('artist', get_elem_from_obj(artist_data, ['artist', 'id']), + get_elem_from_obj(artist_data, ['artist', 'name']))) + + # appending the according release group + results.append(Option("release_group", get_elem_from_obj(release_group_data, ['id']), + get_elem_from_obj(release_group_data, ['title']), + additional_info=f" ({get_elem_from_obj(release_group_data, ['type'])}) from {get_elem_from_obj(release_group_data, ['first-release-date'])}")) + + # appending the release + results.append(release) + + # appending the tracklist, but first putting it in a list, in case of only_tracklist being True to + # return this instead + tracklist = [] + for i, recording_data in enumerate(recording_datas): + recording_data = recording_data['recording'] + tracklist.append(Option('recording', get_elem_from_obj(recording_data, ['id']), + get_elem_from_obj(recording_data, ['title']), + f" ({get_elem_from_obj(recording_data, ['length'])}) from {get_elem_from_obj(recording_data, ['artist-credit-phrase'])}")) + + if only_tracklist: + return tracklist + results.extend(tracklist) + return results + + @staticmethod + def fetch_new_options_from_record(recording: Option): + """ + artists, release, record + """ + results = [] + + result = musicbrainzngs.get_recording_by_id(recording.id, includes=["artists", "releases"]) + recording_data = result['recording'] + release_datas = get_elem_from_obj(recording_data, ['release-list']) + artist_datas = get_elem_from_obj(recording_data, ['artist-credit'], return_if_none={}) + + # appending all the artists to results + for artist_data in artist_datas: + results.append(Option('artist', get_elem_from_obj(artist_data, ['artist', 'id']), + get_elem_from_obj(artist_data, ['artist', 'name']))) + + # appending all releases + for i, release_data in enumerate(release_datas): + results.append( + Option('release', get_elem_from_obj(release_data, ['id']), get_elem_from_obj(release_data, ['title']), + additional_info=f" ({get_elem_from_obj(release_data, ['status'])})")) + + results.append(recording) + + return results + + def fetch_new_options(self) -> MultipleOptions: + if self.current_option is None: + return -1 + + result = [] + if self.current_option.type == 'artist': + result = self.fetch_new_options_from_artist(self.current_option) + elif self.current_option.type == 'release_group': + result = self.fetch_new_options_from_release_group(self.current_option) + elif self.current_option.type == 'release': + result = self.fetch_new_options_from_release(self.current_option) + elif self.current_option.type == 'recording': + result = self.fetch_new_options_from_record(self.current_option) + + return self.append_new_choices(result) + + def choose(self, index: int) -> MultipleOptions: + if len(self.options_history) == 0: + logging.error("initial query neaded before choosing") + return MultipleOptions([]) + + latest_options = self.options_history[-1] + if index >= len(latest_options): + logging.error("index outside of options") + return MultipleOptions([]) + + self.current_option = latest_options[index] + return self.fetch_new_options() + + @staticmethod + def search_recording_from_text(artist: str = None, release_group: str = None, recording: str = None, query: str = None): + result = musicbrainzngs.search_recordings(artist=artist, release=release_group, recording=recording, query=query) + recording_list = get_elem_from_obj(result, ['recording-list'], return_if_none=[]) + + resulting_options = [ + Option("recording", get_elem_from_obj(recording_, ['id']), get_elem_from_obj(recording_, ['title']), + additional_info=f" of {get_elem_from_obj(recording_, ['release-list', 0, 'title'])} by {get_elem_from_obj(recording_, ['artist-credit', 0, 'name'])}") + for recording_ in recording_list] + return resulting_options + + @staticmethod + def search_release_group_from_text(artist: str = None, release_group: str = None, query: str = None): + result = musicbrainzngs.search_release_groups(artist=artist, releasegroup=release_group, query=query) + release_group_list = get_elem_from_obj(result, ['release-group-list'], return_if_none=[]) + + resulting_options = [Option("release_group", get_elem_from_obj(release_group_, ['id']), + get_elem_from_obj(release_group_, ['title']), + additional_info=f" by {get_elem_from_obj(release_group_, ['artist-credit', 0, 'name'])}") + for release_group_ in release_group_list] + return resulting_options + + @staticmethod + def search_artist_from_text(artist: str = None, query: str = None): + result = musicbrainzngs.search_artists(artist=artist, query=query) + artist_list = get_elem_from_obj(result, ['artist-list'], return_if_none=[]) + + resulting_options = [Option("artist", get_elem_from_obj(artist_, ['id']), get_elem_from_obj(artist_, ['name']), + additional_info=f": {', '.join([i['name'] for i in get_elem_from_obj(artist_, ['tag-list'], return_if_none=[])])}") + for artist_ in artist_list] + return resulting_options + + def search_from_text(self, artist: str = None, release_group: str = None, recording: str = None) -> MultipleOptions: + self.logger.info(f"searching specified artist: \"{artist}\", release group: \"{release_group}\", recording: \"{recording}\"") + if artist is None and release_group is None and recording is None: + self.logger.error("either artist, release group or recording has to be set") + return -1 + + if recording is not None: + self.logger.info("search for recording") + results = self.search_recording_from_text(artist=artist, release_group=release_group, recording=recording) + elif release_group is not None: + self.logger.info("search for release group") + results = self.search_release_group_from_text(artist=artist, release_group=release_group) + else: + self.logger.info("search for artist") + results = self.search_artist_from_text(artist=artist) + + return self.append_new_choices(results) + + def search_from_text_unspecified(self, query: str) -> MultipleOptions: + self.logger.info(f"searching unspecified: \"{query}\"") + + results = [] + results.extend(self.search_artist_from_text(query=query)) + results.extend(self.search_release_group_from_text(query=query)) + results.extend(self.search_recording_from_text(query=query)) + + return self.append_new_choices(results) + + def search_from_query(self, query: str) -> MultipleOptions: + if query is None: + return MultipleOptions([]) + """ + mit # wird ein neuer Parameter gestartet + der Buchstabe dahinter legt die Art des Parameters fest + "#a Psychonaut 4 #r Tired, Numb and #t Drop by Drop" + if no # is in the query it gets treated as "unspecified query" + :param query: + :return: + """ + + if not '#' in query: + return self.search_from_text_unspecified(query) + + artist = None + release_group = None + recording = None + + query = query.strip() + parameters = query.split('#') + parameters.remove('') + + if len(parameters) > MAX_PARAMATERS: + raise ValueError(f"too many parameters. Only {MAX_PARAMATERS} are allowed") + + for parameter in parameters: + splitted = parameter.split(" ") + type_ = splitted[0] + input_ = " ".join(splitted[1:]).strip() + + if type_ == "a": + artist = input_ + continue + if type_ == "r": + release_group = input_ + continue + if type_ == "t": + recording = input_ + continue + + return self.search_from_text(artist=artist, release_group=release_group, recording=recording) + + +def automated_demo(): + search = Search(logger=logger_) + search.search_from_text(artist="I Prevail") + + # choose an artist + search.choose(0) + # choose a release group + search.choose(9) + # choose a release + search.choose(2) + # choose a recording + search.choose(4) + + +def interactive_demo(): + search = Search(logger=logger_) + while True: + input_ = input("q to quit, .. for previous options, int for this element, str to search for query, ok to download: ") + input_.strip() + if input_.lower() == "ok": + break + if input_.lower() == "q": + break + if input_.lower() == "..": + search.get_previous_options() + continue + if input_.isdigit(): + search.choose(int(input_)) + continue + search.search_from_query(input_) + + +if __name__ == "__main__": + logging.basicConfig(level=logging.DEBUG) + logger_ = logging.getLogger("test") + + interactive_demo() + diff --git a/src/musify.py b/src/scraping/musify.py similarity index 82% rename from src/musify.py rename to src/scraping/musify.py index 795d067..add0e42 100644 --- a/src/musify.py +++ b/src/scraping/musify.py @@ -1,8 +1,16 @@ import logging +import time + import requests import bs4 -import phonetic_compares +try: + import phonetic_compares +except ModuleNotFoundError: + from scraping import phonetic_compares + +TRIES = 5 +TIMEOUT = 10 session = requests.Session() session.headers = { @@ -12,8 +20,8 @@ session.headers = { def get_musify_url(row): - title = row.title - artists = row.artist + title = row['title'] + artists = row['artists'] url = f"https://musify.club/search/suggestions?term={artists[0]} - {title}" @@ -40,7 +48,10 @@ def get_download_link(default_url): def download_from_musify(file, url): logging.info(f"downloading: '{url}'") - r = session.get(url) + try: + r = session.get(url, timeout=15) + except requests.exceptions.ConnectionError: + return -1 if r.status_code != 200: if r.status_code == 404: logging.warning(f"{r.url} was not found") @@ -60,18 +71,25 @@ def download(row): return download_from_musify(file_, url) -def get_soup_of_search(query: str): +def get_soup_of_search(query: str, trie=0): url = f"https://musify.club/search?searchText={query}" logging.debug(f"Trying to get soup from {url}") r = session.get(url) if r.status_code != 200: + if r.status_code in [503] and trie < TRIES: + logging.warning(f"youtube blocked downloading. ({trie}-{TRIES})") + logging.warning(f"retrying in {TIMEOUT} seconds again") + time.sleep(TIMEOUT) + return get_soup_of_search(query, trie=trie+1) + + logging.warning("too many tries, returning") raise ConnectionError(f"{r.url} returned {r.status_code}:\n{r.content}") return bs4.BeautifulSoup(r.content, features="html.parser") def search_for_track(row): - track = row.title - artist = row.artist + track = row['title'] + artist = row['artists'] soup = get_soup_of_search(f"{artist[0]} - {track}") tracklist_container_soup = soup.find_all("div", {"class": "playlist"}) diff --git a/src/phonetic_compares.py b/src/scraping/phonetic_compares.py similarity index 100% rename from src/phonetic_compares.py rename to src/scraping/phonetic_compares.py diff --git a/src/youtube_music.py b/src/scraping/youtube_music.py similarity index 95% rename from src/youtube_music.py rename to src/scraping/youtube_music.py index a138e2a..940f6be 100644 --- a/src/youtube_music.py +++ b/src/scraping/youtube_music.py @@ -3,7 +3,10 @@ import pandas as pd import logging import time -import phonetic_compares +try: + import phonetic_compares +except ModuleNotFoundError: + from scraping import phonetic_compares YDL_OPTIONS = {'format': 'bestaudio', 'noplaylist': 'True'} YOUTUBE_URL_KEY = 'webpage_url' diff --git a/src/url_to_path.py b/src/url_to_path.py index 301741c..dd4a28f 100644 --- a/src/url_to_path.py +++ b/src/url_to_path.py @@ -1,30 +1,17 @@ import os.path -import shlex -import pandas as pd -import json +import logging class UrlPath: - def __init__(self, genre: str, temp: str = "temp", file: str = ".cache3.csv", step_two_file: str = ".cache2.csv"): - self.temp = temp - self.file = file - self.metadata = pd.read_csv(os.path.join(self.temp, step_two_file), index_col=0) + def __init__(self, database, logger: logging.Logger, genre: str): + self.database = database + self.logger = logger self.genre = genre - new_metadata = [] - - for idx, row in self.metadata.iterrows(): + for row in self.database.get_tracks_without_filepath(): file, path = self.get_path_from_row(row) - new_row = dict(row) - new_row['path'] = path - new_row['file'] = file - new_row['genre'] = self.genre - new_metadata.append(new_row) - - new_df = pd.DataFrame(new_metadata) - new_df.to_csv(os.path.join(self.temp, self.file)) - + self.database.set_filepath(row['id'], file, path, genre) def get_path_from_row(self, row): """ @@ -33,7 +20,9 @@ class UrlPath: :param row: :return: path: """ - return os.path.join(self.get_genre(), self.get_artist(row), self.get_album(row), f"{self.get_song(row)}.mp3"), os.path.join(self.get_genre(), self.get_artist(row), self.get_album(row)) + return os.path.join(self.get_genre(), self.get_artist(row), self.get_album(row), + f"{self.get_song(row)}.mp3"), os.path.join(self.get_genre(), self.get_artist(row), + self.get_album(row)) def escape_part(self, part: str): return part.replace("/", " ") @@ -45,7 +34,7 @@ class UrlPath: return self.escape_part(row['album']) def get_artist(self, row): - artists = json.loads(row['artist'].replace("'", '"')) + artists = [artist['name'] for artist in row['artists']] return self.escape_part(artists[0]) def get_song(self, row):