Merge branch 'rework' into develop
This commit is contained in:
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"The All\u2010American Rejects [2003]": null,
|
||||
"Move Along [2005]": null,
|
||||
"B-Sides & Rarities [2007]": null,
|
||||
"When the World Comes Down [2009]": null,
|
||||
"Kids in the Street [2012]": null,
|
||||
"The All\u2010American Rejects [2000]": null
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
[
|
||||
{
|
||||
"The All\u2010American Rejects [2003]": [
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
]
|
||||
},
|
||||
{
|
||||
"Move Along [2005]": [
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
]
|
||||
},
|
||||
{
|
||||
"B-Sides & Rarities [2007]": [
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
]
|
||||
},
|
||||
{
|
||||
"When the World Comes Down [2009]": [
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
]
|
||||
},
|
||||
{
|
||||
"Kids in the Street [2012]": [
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
]
|
||||
},
|
||||
{
|
||||
"The All\u2010American Rejects [2000]": [
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -1,111 +0,0 @@
|
||||
[
|
||||
{
|
||||
"The All\u2010American Rejects [2003]": [
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Move Along [2005]": [
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics"
|
||||
]
|
||||
},
|
||||
{
|
||||
"B-Sides & Rarities [2007]": [
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics"
|
||||
]
|
||||
},
|
||||
{
|
||||
"When the World Comes Down [2009]": [
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Kids in the Street [2012]": [
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics"
|
||||
]
|
||||
},
|
||||
{
|
||||
"The All\u2010American Rejects [2000]": [
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics",
|
||||
"No Lyrics"
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -1 +0,0 @@
|
||||
[{"The All\u2010American Rejects [2003]": ["No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics"]}, {"Move Along [2005]": ["No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics"]}, {"B-Sides & Rarities [2007]": ["No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics"]}, {"When the World Comes Down [2009]": ["No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics"]}, {"Kids in the Street [2012]": ["No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics"]}, {"The All\u2010American Rejects [2000]": ["No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics", "No Lyrics"]}]
|
||||
6569
output.json
6569
output.json
File diff suppressed because it is too large
Load Diff
208
poetry.lock
generated
208
poetry.lock
generated
@@ -109,6 +109,17 @@ optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
version = "0.4.3"
|
||||
|
||||
[[package]]
|
||||
category = "main"
|
||||
description = "Composable style cycles"
|
||||
name = "cycler"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
version = "0.10.0"
|
||||
|
||||
[package.dependencies]
|
||||
six = "*"
|
||||
|
||||
[[package]]
|
||||
category = "main"
|
||||
description = "Internationalized Domain Names in Applications (IDNA)"
|
||||
@@ -147,6 +158,32 @@ parso = ">=0.5.2"
|
||||
[package.extras]
|
||||
testing = ["colorama (0.4.1)", "docopt", "pytest (>=3.9.0,<5.0.0)"]
|
||||
|
||||
[[package]]
|
||||
category = "main"
|
||||
description = "A fast implementation of the Cassowary constraint solver"
|
||||
name = "kiwisolver"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
version = "1.1.0"
|
||||
|
||||
[package.dependencies]
|
||||
setuptools = "*"
|
||||
|
||||
[[package]]
|
||||
category = "main"
|
||||
description = "Python plotting package"
|
||||
name = "matplotlib"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
version = "3.2.0"
|
||||
|
||||
[package.dependencies]
|
||||
cycler = ">=0.10"
|
||||
kiwisolver = ">=1.0.1"
|
||||
numpy = ">=1.11"
|
||||
pyparsing = ">=2.0.1,<2.0.4 || >2.0.4,<2.1.2 || >2.1.2,<2.1.6 || >2.1.6"
|
||||
python-dateutil = ">=2.1"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "McCabe checker, plugin for flake8"
|
||||
@@ -163,6 +200,14 @@ optional = false
|
||||
python-versions = ">=3.5"
|
||||
version = "8.2.0"
|
||||
|
||||
[[package]]
|
||||
category = "main"
|
||||
description = "multidict implementation"
|
||||
name = "multidict"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
version = "4.7.5"
|
||||
|
||||
[[package]]
|
||||
category = "main"
|
||||
description = "Python bindings for the MusicBrainz NGS and the Cover Art Archive webservices"
|
||||
@@ -210,6 +255,14 @@ optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
version = "0.7.0"
|
||||
|
||||
[[package]]
|
||||
category = "main"
|
||||
description = "Python Imaging Library (Fork)"
|
||||
name = "pillow"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
version = "7.0.0"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "plugin and hook calling mechanisms for python"
|
||||
@@ -306,7 +359,7 @@ toml = "*"
|
||||
dev = ["isort", "flake8", "pytest", "mypy"]
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
category = "main"
|
||||
description = "Python parsing module"
|
||||
name = "pyparsing"
|
||||
optional = false
|
||||
@@ -339,6 +392,17 @@ version = ">=0.12"
|
||||
checkqa-mypy = ["mypy (v0.761)"]
|
||||
testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"]
|
||||
|
||||
[[package]]
|
||||
category = "main"
|
||||
description = "Extensions to the standard Python datetime module"
|
||||
name = "python-dateutil"
|
||||
optional = false
|
||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
|
||||
version = "2.8.1"
|
||||
|
||||
[package.dependencies]
|
||||
six = ">=1.5"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "JSON RPC 2.0 server library"
|
||||
@@ -418,7 +482,7 @@ version = "0.16.0"
|
||||
dev = ["pytest"]
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
category = "main"
|
||||
description = "Python 2 and 3 compatibility utilities"
|
||||
name = "six"
|
||||
optional = false
|
||||
@@ -487,6 +551,19 @@ optional = false
|
||||
python-versions = "*"
|
||||
version = "0.1.8"
|
||||
|
||||
[[package]]
|
||||
category = "main"
|
||||
description = "A little word cloud generator"
|
||||
name = "wordcloud"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
version = "1.6.0"
|
||||
|
||||
[package.dependencies]
|
||||
matplotlib = "*"
|
||||
numpy = ">=1.6.1"
|
||||
pillow = "*"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "A formatter for Python code."
|
||||
@@ -509,7 +586,7 @@ docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"]
|
||||
testing = ["jaraco.itertools", "func-timeout"]
|
||||
|
||||
[metadata]
|
||||
content-hash = "b3e363ce109826fbe16ba73b6102a62254689ead837a371aecd2857fa5ed9f45"
|
||||
content-hash = "6755748d710bead6ddf5f543c09fb33a09b340dcff15466f7e6759ce9c659004"
|
||||
python-versions = "^3.7"
|
||||
|
||||
[metadata.files]
|
||||
@@ -556,6 +633,10 @@ colorama = [
|
||||
{file = "colorama-0.4.3-py2.py3-none-any.whl", hash = "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff"},
|
||||
{file = "colorama-0.4.3.tar.gz", hash = "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1"},
|
||||
]
|
||||
cycler = [
|
||||
{file = "cycler-0.10.0-py2.py3-none-any.whl", hash = "sha256:1d8a5ae1ff6c5cf9b93e8811e581232ad8920aeec647c37316ceac982b08cb2d"},
|
||||
{file = "cycler-0.10.0.tar.gz", hash = "sha256:cd7b2d1018258d7247a71425e9f26463dfb444d411c39569972f4ce586b0c9d8"},
|
||||
]
|
||||
idna = [
|
||||
{file = "idna-2.9-py2.py3-none-any.whl", hash = "sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa"},
|
||||
{file = "idna-2.9.tar.gz", hash = "sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb"},
|
||||
@@ -568,6 +649,61 @@ jedi = [
|
||||
{file = "jedi-0.15.2-py2.py3-none-any.whl", hash = "sha256:1349c1e8c107095a55386628bb3b2a79422f3a2cab8381e34ce19909e0cf5064"},
|
||||
{file = "jedi-0.15.2.tar.gz", hash = "sha256:e909527104a903606dd63bea6e8e888833f0ef087057829b89a18364a856f807"},
|
||||
]
|
||||
kiwisolver = [
|
||||
{file = "kiwisolver-1.1.0-cp27-cp27m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:7f4dd50874177d2bb060d74769210f3bce1af87a8c7cf5b37d032ebf94f0aca3"},
|
||||
{file = "kiwisolver-1.1.0-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:fe51b79da0062f8e9d49ed0182a626a7dc7a0cbca0328f612c6ee5e4711c81e4"},
|
||||
{file = "kiwisolver-1.1.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:f790f8b3dff3d53453de6a7b7ddd173d2e020fb160baff578d578065b108a05f"},
|
||||
{file = "kiwisolver-1.1.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:f2b22153870ca5cf2ab9c940d7bc38e8e9089fa0f7e5856ea195e1cf4ff43d5a"},
|
||||
{file = "kiwisolver-1.1.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:e8bf074363ce2babeb4764d94f8e65efd22e6a7c74860a4f05a6947afc020ff2"},
|
||||
{file = "kiwisolver-1.1.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:05b5b061e09f60f56244adc885c4a7867da25ca387376b02c1efc29cc16bcd0f"},
|
||||
{file = "kiwisolver-1.1.0-cp27-none-win32.whl", hash = "sha256:47b8cb81a7d18dbaf4fed6a61c3cecdb5adec7b4ac292bddb0d016d57e8507d5"},
|
||||
{file = "kiwisolver-1.1.0-cp27-none-win_amd64.whl", hash = "sha256:b64916959e4ae0ac78af7c3e8cef4becee0c0e9694ad477b4c6b3a536de6a544"},
|
||||
{file = "kiwisolver-1.1.0-cp34-cp34m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:682e54f0ce8f45981878756d7203fd01e188cc6c8b2c5e2cf03675390b4534d5"},
|
||||
{file = "kiwisolver-1.1.0-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:d52e3b1868a4e8fd18b5cb15055c76820df514e26aa84cc02f593d99fef6707f"},
|
||||
{file = "kiwisolver-1.1.0-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:8aa7009437640beb2768bfd06da049bad0df85f47ff18426261acecd1cf00897"},
|
||||
{file = "kiwisolver-1.1.0-cp34-none-win32.whl", hash = "sha256:26f4fbd6f5e1dabff70a9ba0d2c4bd30761086454aa30dddc5b52764ee4852b7"},
|
||||
{file = "kiwisolver-1.1.0-cp34-none-win_amd64.whl", hash = "sha256:79bfb2f0bd7cbf9ea256612c9523367e5ec51d7cd616ae20ca2c90f575d839a2"},
|
||||
{file = "kiwisolver-1.1.0-cp35-cp35m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:3b2378ad387f49cbb328205bda569b9f87288d6bc1bf4cd683c34523a2341efe"},
|
||||
{file = "kiwisolver-1.1.0-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:aa716b9122307c50686356cfb47bfbc66541868078d0c801341df31dca1232a9"},
|
||||
{file = "kiwisolver-1.1.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:58e626e1f7dfbb620d08d457325a4cdac65d1809680009f46bf41eaf74ad0187"},
|
||||
{file = "kiwisolver-1.1.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:e3a21a720791712ed721c7b95d433e036134de6f18c77dbe96119eaf7aa08004"},
|
||||
{file = "kiwisolver-1.1.0-cp35-none-win32.whl", hash = "sha256:939f36f21a8c571686eb491acfffa9c7f1ac345087281b412d63ea39ca14ec4a"},
|
||||
{file = "kiwisolver-1.1.0-cp35-none-win_amd64.whl", hash = "sha256:9733b7f64bd9f807832d673355f79703f81f0b3e52bfce420fc00d8cb28c6a6c"},
|
||||
{file = "kiwisolver-1.1.0-cp36-cp36m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:acc4df99308111585121db217681f1ce0eecb48d3a828a2f9bbf9773f4937e9e"},
|
||||
{file = "kiwisolver-1.1.0-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:9105ce82dcc32c73eb53a04c869b6a4bc756b43e4385f76ea7943e827f529e4d"},
|
||||
{file = "kiwisolver-1.1.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:f16814a4a96dc04bf1da7d53ee8d5b1d6decfc1a92a63349bb15d37b6a263dd9"},
|
||||
{file = "kiwisolver-1.1.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:400599c0fe58d21522cae0e8b22318e09d9729451b17ee61ba8e1e7c0346565c"},
|
||||
{file = "kiwisolver-1.1.0-cp36-none-win32.whl", hash = "sha256:db1a5d3cc4ae943d674718d6c47d2d82488ddd94b93b9e12d24aabdbfe48caee"},
|
||||
{file = "kiwisolver-1.1.0-cp36-none-win_amd64.whl", hash = "sha256:5a52e1b006bfa5be04fe4debbcdd2688432a9af4b207a3f429c74ad625022641"},
|
||||
{file = "kiwisolver-1.1.0-cp37-cp37m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:a02f6c3e229d0b7220bd74600e9351e18bc0c361b05f29adae0d10599ae0e326"},
|
||||
{file = "kiwisolver-1.1.0-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:9491578147849b93e70d7c1d23cb1229458f71fc79c51d52dce0809b2ca44eea"},
|
||||
{file = "kiwisolver-1.1.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:5c7ca4e449ac9f99b3b9d4693debb1d6d237d1542dd6a56b3305fe8a9620f883"},
|
||||
{file = "kiwisolver-1.1.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:a0c0a9f06872330d0dd31b45607197caab3c22777600e88031bfe66799e70bb0"},
|
||||
{file = "kiwisolver-1.1.0-cp37-none-win32.whl", hash = "sha256:8944a16020c07b682df861207b7e0efcd2f46c7488619cb55f65882279119389"},
|
||||
{file = "kiwisolver-1.1.0-cp37-none-win_amd64.whl", hash = "sha256:d3fcf0819dc3fea58be1fd1ca390851bdb719a549850e708ed858503ff25d995"},
|
||||
{file = "kiwisolver-1.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:933df612c453928f1c6faa9236161a1d999a26cd40abf1dc5d7ebbc6dbfb8fca"},
|
||||
{file = "kiwisolver-1.1.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:d22702cadb86b6fcba0e6b907d9f84a312db9cd6934ee728144ce3018e715ee1"},
|
||||
{file = "kiwisolver-1.1.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:210d8c39d01758d76c2b9a693567e1657ec661229bc32eac30761fa79b2474b0"},
|
||||
{file = "kiwisolver-1.1.0-cp38-none-win32.whl", hash = "sha256:76275ee077772c8dde04fb6c5bc24b91af1bb3e7f4816fd1852f1495a64dad93"},
|
||||
{file = "kiwisolver-1.1.0-cp38-none-win_amd64.whl", hash = "sha256:3b15d56a9cd40c52d7ab763ff0bc700edbb4e1a298dc43715ecccd605002cf11"},
|
||||
{file = "kiwisolver-1.1.0.tar.gz", hash = "sha256:53eaed412477c836e1b9522c19858a8557d6e595077830146182225613b11a75"},
|
||||
]
|
||||
matplotlib = [
|
||||
{file = "matplotlib-3.2.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:0711b07920919951b2c508a773c433cbe07bdad952ea84ed9d18ca7853ccbe8b"},
|
||||
{file = "matplotlib-3.2.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:b93377c6720e7db9cbba57e856a21aae2ff707677a6ee6b3b9d485f22ed82697"},
|
||||
{file = "matplotlib-3.2.0-cp36-cp36m-win32.whl", hash = "sha256:8e931015769322ee6860cabb8f975f628788e851092fd5edbdb065b5a516e3af"},
|
||||
{file = "matplotlib-3.2.0-cp36-cp36m-win_amd64.whl", hash = "sha256:b21479a4478070c1c0f460e1bf1b65341e6a70ae0da905fcee836651450c66bb"},
|
||||
{file = "matplotlib-3.2.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0ab307e610302971012dc2387c97fc68e58c8eb00045a2c735da1b16353a3e3f"},
|
||||
{file = "matplotlib-3.2.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d75f5e952562f5e494ae92c1f917fc96c2ce09305a7c1bdc2e6502d3c61fbdc3"},
|
||||
{file = "matplotlib-3.2.0-cp37-cp37m-win32.whl", hash = "sha256:9d174cc9681184023a7d520079eb0c085208761c6562710c1de7263d08217ab6"},
|
||||
{file = "matplotlib-3.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d281862a68b0bfce8f9e02a8e5acaa5cfbec37f37320f59b52eaf54b6423ec13"},
|
||||
{file = "matplotlib-3.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ee8acb1d4ee204e5cfe361d8f00d7e52c68f81c099b6c6048a3c76bf2c6b46e6"},
|
||||
{file = "matplotlib-3.2.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:be937f34047bc09ed22d6a19d970fdc61d5d3191aa62f3262fc7f308e6d2e7f9"},
|
||||
{file = "matplotlib-3.2.0-cp38-cp38-win32.whl", hash = "sha256:97a03e73f9ab71db8e4084894550c3af420c8ab1989b5e1306261b17576bf61b"},
|
||||
{file = "matplotlib-3.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:d5287cfcabad6f0f71a2627c1bbb6fb0cddacb9844f6c91f210604faa508f562"},
|
||||
{file = "matplotlib-3.2.0-pp373-pypy36_pp73-win32.whl", hash = "sha256:fc84f7c7cf1c5a9dbceadb7546818228f019d3b113ce5e362120c895fbba2944"},
|
||||
{file = "matplotlib-3.2.0.tar.gz", hash = "sha256:651d76daf9168250370d4befb09f79875daa2224a9096d97dfc3ed764c842be4"},
|
||||
]
|
||||
mccabe = [
|
||||
{file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"},
|
||||
{file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"},
|
||||
@@ -576,6 +712,25 @@ more-itertools = [
|
||||
{file = "more-itertools-8.2.0.tar.gz", hash = "sha256:b1ddb932186d8a6ac451e1d95844b382f55e12686d51ca0c68b6f61f2ab7a507"},
|
||||
{file = "more_itertools-8.2.0-py3-none-any.whl", hash = "sha256:5dd8bcf33e5f9513ffa06d5ad33d78f31e1931ac9a18f33d37e77a180d393a7c"},
|
||||
]
|
||||
multidict = [
|
||||
{file = "multidict-4.7.5-cp35-cp35m-macosx_10_13_x86_64.whl", hash = "sha256:fc3b4adc2ee8474cb3cd2a155305d5f8eda0a9c91320f83e55748e1fcb68f8e3"},
|
||||
{file = "multidict-4.7.5-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:42f56542166040b4474c0c608ed051732033cd821126493cf25b6c276df7dd35"},
|
||||
{file = "multidict-4.7.5-cp35-cp35m-win32.whl", hash = "sha256:7774e9f6c9af3f12f296131453f7b81dabb7ebdb948483362f5afcaac8a826f1"},
|
||||
{file = "multidict-4.7.5-cp35-cp35m-win_amd64.whl", hash = "sha256:c2c37185fb0af79d5c117b8d2764f4321eeb12ba8c141a95d0aa8c2c1d0a11dd"},
|
||||
{file = "multidict-4.7.5-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:e439c9a10a95cb32abd708bb8be83b2134fa93790a4fb0535ca36db3dda94d20"},
|
||||
{file = "multidict-4.7.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:85cb26c38c96f76b7ff38b86c9d560dea10cf3459bb5f4caf72fc1bb932c7136"},
|
||||
{file = "multidict-4.7.5-cp36-cp36m-win32.whl", hash = "sha256:620b37c3fea181dab09267cd5a84b0f23fa043beb8bc50d8474dd9694de1fa6e"},
|
||||
{file = "multidict-4.7.5-cp36-cp36m-win_amd64.whl", hash = "sha256:6e6fef114741c4d7ca46da8449038ec8b1e880bbe68674c01ceeb1ac8a648e78"},
|
||||
{file = "multidict-4.7.5-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:a326f4240123a2ac66bb163eeba99578e9d63a8654a59f4688a79198f9aa10f8"},
|
||||
{file = "multidict-4.7.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:dc561313279f9d05a3d0ffa89cd15ae477528ea37aa9795c4654588a3287a9ab"},
|
||||
{file = "multidict-4.7.5-cp37-cp37m-win32.whl", hash = "sha256:4b7df040fb5fe826d689204f9b544af469593fb3ff3a069a6ad3409f742f5928"},
|
||||
{file = "multidict-4.7.5-cp37-cp37m-win_amd64.whl", hash = "sha256:317f96bc0950d249e96d8d29ab556d01dd38888fbe68324f46fd834b430169f1"},
|
||||
{file = "multidict-4.7.5-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:b51249fdd2923739cd3efc95a3d6c363b67bbf779208e9f37fd5e68540d1a4d4"},
|
||||
{file = "multidict-4.7.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:ae402f43604e3b2bc41e8ea8b8526c7fa7139ed76b0d64fc48e28125925275b2"},
|
||||
{file = "multidict-4.7.5-cp38-cp38-win32.whl", hash = "sha256:bb519becc46275c594410c6c28a8a0adc66fe24fef154a9addea54c1adb006f5"},
|
||||
{file = "multidict-4.7.5-cp38-cp38-win_amd64.whl", hash = "sha256:544fae9261232a97102e27a926019100a9db75bec7b37feedd74b3aa82f29969"},
|
||||
{file = "multidict-4.7.5.tar.gz", hash = "sha256:aee283c49601fa4c13adc64c09c978838a7e812f85377ae130a24d7198c0331e"},
|
||||
]
|
||||
musicbrainzngs = [
|
||||
{file = "musicbrainzngs-0.7.1-py2.py3-none-any.whl", hash = "sha256:e841a8f975104c0a72290b09f59326050194081a5ae62ee512f41915090e1a10"},
|
||||
{file = "musicbrainzngs-0.7.1.tar.gz", hash = "sha256:ab1c0100fd0b305852e65f2ed4113c6de12e68afd55186987b8ed97e0f98e627"},
|
||||
@@ -615,6 +770,30 @@ pathspec = [
|
||||
{file = "pathspec-0.7.0-py2.py3-none-any.whl", hash = "sha256:163b0632d4e31cef212976cf57b43d9fd6b0bac6e67c26015d611a647d5e7424"},
|
||||
{file = "pathspec-0.7.0.tar.gz", hash = "sha256:562aa70af2e0d434367d9790ad37aed893de47f1693e4201fd1d3dca15d19b96"},
|
||||
]
|
||||
pillow = [
|
||||
{file = "Pillow-7.0.0-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:5f3546ceb08089cedb9e8ff7e3f6a7042bb5b37c2a95d392fb027c3e53a2da00"},
|
||||
{file = "Pillow-7.0.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:9d2ba4ed13af381233e2d810ff3bab84ef9f18430a9b336ab69eaf3cd24299ff"},
|
||||
{file = "Pillow-7.0.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:ff3797f2f16bf9d17d53257612da84dd0758db33935777149b3334c01ff68865"},
|
||||
{file = "Pillow-7.0.0-cp35-cp35m-win32.whl", hash = "sha256:c18f70dc27cc5d236f10e7834236aff60aadc71346a5bc1f4f83a4b3abee6386"},
|
||||
{file = "Pillow-7.0.0-cp35-cp35m-win_amd64.whl", hash = "sha256:875358310ed7abd5320f21dd97351d62de4929b0426cdb1eaa904b64ac36b435"},
|
||||
{file = "Pillow-7.0.0-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:ab76e5580b0ed647a8d8d2d2daee170e8e9f8aad225ede314f684e297e3643c2"},
|
||||
{file = "Pillow-7.0.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a62ec5e13e227399be73303ff301f2865bf68657d15ea50b038d25fc41097317"},
|
||||
{file = "Pillow-7.0.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:8ac6ce7ff3892e5deaab7abaec763538ffd011f74dc1801d93d3c5fc541feee2"},
|
||||
{file = "Pillow-7.0.0-cp36-cp36m-win32.whl", hash = "sha256:91b710e3353aea6fc758cdb7136d9bbdcb26b53cefe43e2cba953ac3ee1d3313"},
|
||||
{file = "Pillow-7.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:bf598d2e37cf8edb1a2f26ed3fb255191f5232badea4003c16301cb94ac5bdd0"},
|
||||
{file = "Pillow-7.0.0-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:5bfef0b1cdde9f33881c913af14e43db69815c7e8df429ceda4c70a5e529210f"},
|
||||
{file = "Pillow-7.0.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:dc058b7833184970d1248135b8b0ab702e6daa833be14035179f2acb78ff5636"},
|
||||
{file = "Pillow-7.0.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:c5ed816632204a2fc9486d784d8e0d0ae754347aba99c811458d69fcdfd2a2f9"},
|
||||
{file = "Pillow-7.0.0-cp37-cp37m-win32.whl", hash = "sha256:54ebae163e8412aff0b9df1e88adab65788f5f5b58e625dc5c7f51eaf14a6837"},
|
||||
{file = "Pillow-7.0.0-cp37-cp37m-win_amd64.whl", hash = "sha256:87269cc6ce1e3dee11f23fa515e4249ae678dbbe2704598a51cee76c52e19cda"},
|
||||
{file = "Pillow-7.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0a628977ac2e01ca96aaae247ec2bd38e729631ddf2221b4b715446fd45505be"},
|
||||
{file = "Pillow-7.0.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:62a889aeb0a79e50ecf5af272e9e3c164148f4bd9636cc6bcfa182a52c8b0533"},
|
||||
{file = "Pillow-7.0.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:bf4003aa538af3f4205c5fac56eacaa67a6dd81e454ffd9e9f055fff9f1bc614"},
|
||||
{file = "Pillow-7.0.0-cp38-cp38-win32.whl", hash = "sha256:7406f5a9b2fd966e79e6abdaf700585a4522e98d6559ce37fc52e5c955fade0a"},
|
||||
{file = "Pillow-7.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:5f7ae9126d16194f114435ebb79cc536b5682002a4fa57fa7bb2cbcde65f2f4d"},
|
||||
{file = "Pillow-7.0.0-pp373-pypy36_pp73-win32.whl", hash = "sha256:8453f914f4e5a3d828281a6628cf517832abfa13ff50679a4848926dac7c0358"},
|
||||
{file = "Pillow-7.0.0.tar.gz", hash = "sha256:4d9ed9a64095e031435af120d3c910148067087541131e82b3e8db302f4c8946"},
|
||||
]
|
||||
pluggy = [
|
||||
{file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"},
|
||||
{file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"},
|
||||
@@ -657,6 +836,10 @@ pytest = [
|
||||
{file = "pytest-5.3.5-py3-none-any.whl", hash = "sha256:ff615c761e25eb25df19edddc0b970302d2a9091fbce0e7213298d85fb61fef6"},
|
||||
{file = "pytest-5.3.5.tar.gz", hash = "sha256:0d5fe9189a148acc3c3eb2ac8e1ac0742cb7618c084f3d228baaec0c254b318d"},
|
||||
]
|
||||
python-dateutil = [
|
||||
{file = "python-dateutil-2.8.1.tar.gz", hash = "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c"},
|
||||
{file = "python_dateutil-2.8.1-py2.py3-none-any.whl", hash = "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"},
|
||||
]
|
||||
python-jsonrpc-server = [
|
||||
{file = "python-jsonrpc-server-0.3.4.tar.gz", hash = "sha256:c73bf5495c9dd4d2f902755bedeb6da5afe778e0beee82f0e195c4655352fe37"},
|
||||
{file = "python_jsonrpc_server-0.3.4-py3-none-any.whl", hash = "sha256:1f85f75f37f923149cc0aa078474b6df55b708e82ed819ca8846a65d7d0ada7f"},
|
||||
@@ -747,6 +930,25 @@ wcwidth = [
|
||||
{file = "wcwidth-0.1.8-py2.py3-none-any.whl", hash = "sha256:8fd29383f539be45b20bd4df0dc29c20ba48654a41e661925e612311e9f3c603"},
|
||||
{file = "wcwidth-0.1.8.tar.gz", hash = "sha256:f28b3e8a6483e5d49e7f8949ac1a78314e740333ae305b4ba5defd3e74fb37a8"},
|
||||
]
|
||||
wordcloud = [
|
||||
{file = "wordcloud-1.6.0-cp27-cp27m-macosx_10_6_x86_64.whl", hash = "sha256:b99157f068826697d93d2e5e61b1acff35591d5e534818368ccd56945b9a5f29"},
|
||||
{file = "wordcloud-1.6.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:60c9178ea11d6537f19dad7eb5387f2516737796827710c9409ab9602d9493c7"},
|
||||
{file = "wordcloud-1.6.0-cp27-cp27m-win_amd64.whl", hash = "sha256:0baf47567bd426bf65963d53a1aaa69af35c2e096dc0ad9073efd5833cccd20a"},
|
||||
{file = "wordcloud-1.6.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:52d0772e385e38144be2bdb58a0d7817f2c80db0640e1efad699cff8ea86533d"},
|
||||
{file = "wordcloud-1.6.0-cp34-cp34m-macosx_10_6_x86_64.whl", hash = "sha256:61156874a21fffb46cdfb3518bbc9865fbfe9973ecc36eff20e86792687e439b"},
|
||||
{file = "wordcloud-1.6.0-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:358f4ead931bc8297de3dbd3a26ce8d1e3fe27c1027cce091c1b7037e4ba4904"},
|
||||
{file = "wordcloud-1.6.0-cp34-cp34m-win_amd64.whl", hash = "sha256:473b660baee64578dad272a18253b59245a337f5dfa3a186e32cf20b0eee4110"},
|
||||
{file = "wordcloud-1.6.0-cp35-cp35m-macosx_10_6_x86_64.whl", hash = "sha256:950882b89298c318e5f7cf10027f00b4e09402e18f719cb656aea5209a57e5a9"},
|
||||
{file = "wordcloud-1.6.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:a8d829e19431709c1310a505687fc7c0f869c48259f4a55b5bf387642ed6da46"},
|
||||
{file = "wordcloud-1.6.0-cp35-cp35m-win_amd64.whl", hash = "sha256:e9ae81e8dbb5953f8cf94083b990c760b179b4000dae2babd14827d61230fc69"},
|
||||
{file = "wordcloud-1.6.0-cp36-cp36m-macosx_10_6_x86_64.whl", hash = "sha256:ae6c0030a7fd09bd35713592ba005da9457f7d38f46dc807484c5e0a379d813c"},
|
||||
{file = "wordcloud-1.6.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:c59387b35af772626d4a87b986eb8ab29d3d7ffca6f94da95f4c3a0961407df3"},
|
||||
{file = "wordcloud-1.6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:b0256ca213eb52e5261307e64faaf242742ada1322bb9d5090ecdaa9b44540ee"},
|
||||
{file = "wordcloud-1.6.0-cp37-cp37m-macosx_10_6_x86_64.whl", hash = "sha256:fc3db0cc71e4d5666f732c5b4b3c04a0d58242579cb6c6e5146ffd2890cc5d57"},
|
||||
{file = "wordcloud-1.6.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d4b970d4d30bc9baec9e8b2d7e69fb9771576bb09d6b6f6ce6f22403ca58d6de"},
|
||||
{file = "wordcloud-1.6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:3971ca6042745169e9645b3bbce64b790f8c211ad7c7d265049992506e033212"},
|
||||
{file = "wordcloud-1.6.0.tar.gz", hash = "sha256:4335deb87b7cd9f8a6ce12de0257d15f14f98874f326e7a839f27b2c8ac792ca"},
|
||||
]
|
||||
yapf = [
|
||||
{file = "yapf-0.29.0-py2.py3-none-any.whl", hash = "sha256:cad8a272c6001b3401de3278238fdc54997b6c2e56baa751788915f879a52fca"},
|
||||
{file = "yapf-0.29.0.tar.gz", hash = "sha256:712e23c468506bf12cadd10169f852572ecc61b266258422d45aaf4ad7ef43de"},
|
||||
|
||||
@@ -12,6 +12,8 @@ addict = "^2.2.1"
|
||||
progress = "^1.5"
|
||||
numpy = "^1.18.1"
|
||||
beautifultable = "^0.8.0"
|
||||
wordcloud = "^1.6.0"
|
||||
multidict = "^4.7.5"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
pytest = "^5.2"
|
||||
|
||||
BIN
src/.DS_Store
vendored
Normal file
BIN
src/.DS_Store
vendored
Normal file
Binary file not shown.
BIN
src/musicbrainzapi/.DS_Store
vendored
Normal file
BIN
src/musicbrainzapi/.DS_Store
vendored
Normal file
Binary file not shown.
BIN
src/musicbrainzapi/api/.DS_Store
vendored
Normal file
BIN
src/musicbrainzapi/api/.DS_Store
vendored
Normal file
Binary file not shown.
@@ -1 +0,0 @@
|
||||
from . import lyrics
|
||||
@@ -1,110 +0,0 @@
|
||||
import musicbrainzngs
|
||||
from musicbrainzapi import __header__, __version__
|
||||
import addict
|
||||
|
||||
from pprint import pprint
|
||||
|
||||
|
||||
def main():
|
||||
musicbrainzngs.set_useragent(
|
||||
__header__.__header__, __version__.__version__
|
||||
)
|
||||
resp_0 = musicbrainzngs.browse_release_groups(
|
||||
# artist='1a425bbd-cca4-4b2c-aeb7-71cb176c828a',
|
||||
artist='0383dadf-2a4e-4d10-a46a-e9e041da8eb3',
|
||||
release_type=['album'],
|
||||
limit=100,
|
||||
)
|
||||
# pprint(resp_0)
|
||||
resp_0 = addict.Dict(resp_0)
|
||||
|
||||
release_group_ids = addict.Dict()
|
||||
album_info = addict.Dict()
|
||||
|
||||
release_group_ids = addict.Dict(
|
||||
(i.id, i.title)
|
||||
for i in resp_0['release-group-list']
|
||||
if i.type == 'Album'
|
||||
)
|
||||
|
||||
all_albums = list()
|
||||
|
||||
pprint(release_group_ids)
|
||||
|
||||
pprint(len(release_group_ids))
|
||||
|
||||
for id, alb in release_group_ids.items():
|
||||
# print(id, alb)
|
||||
|
||||
resp_1 = addict.Dict(
|
||||
musicbrainzngs.browse_releases(
|
||||
release_group=id,
|
||||
release_type=['album'],
|
||||
includes=['recordings'],
|
||||
limit=100,
|
||||
)
|
||||
)
|
||||
|
||||
album_track_count = [
|
||||
i['medium-list'][0]['track-count'] for i in resp_1['release-list']
|
||||
]
|
||||
|
||||
max_track_pos = album_track_count.index(max(album_track_count))
|
||||
|
||||
# print(max_track_pos)
|
||||
|
||||
# print(album_track_count)
|
||||
|
||||
album_tracks = resp_1['release-list'][max_track_pos]
|
||||
|
||||
album_year = resp_1['release-list'][max_track_pos].date.split('-')[0]
|
||||
|
||||
album_tracks = addict.Dict(
|
||||
(
|
||||
alb + f' [{album_year}]',
|
||||
[
|
||||
i.recording.title
|
||||
for i in resp_1['release-list'][max_track_pos][
|
||||
'medium-list'
|
||||
][0]['track-list']
|
||||
],
|
||||
)
|
||||
)
|
||||
|
||||
# pprint(resp_1['release-list'][3])
|
||||
# print(max_track_pos)
|
||||
# pprint(album_tracks)
|
||||
|
||||
all_albums.append(album_tracks)
|
||||
|
||||
pprint(all_albums)
|
||||
raise (SystemExit)
|
||||
|
||||
# pprint(album_info)
|
||||
|
||||
# resp_1 = addict.Dict(
|
||||
# musicbrainzngs.browse_releases(
|
||||
# release_group='1174aa3d-1c9e-4745-be8d-e21a61b1a22d',
|
||||
# release_type=['album'],
|
||||
# includes=['recordings'],
|
||||
# limit=100,
|
||||
# )
|
||||
# )
|
||||
|
||||
# resp_1 = addict.Dict(resp_1)
|
||||
|
||||
# pprint(resp_1)
|
||||
|
||||
# print(resp_1['release-list'][0]['medium-list'][0]['track-count'])
|
||||
|
||||
# album_track_count = [
|
||||
# i['medium-list'][0]['track-count'] for i in resp_1['release-list']
|
||||
# ]
|
||||
|
||||
# max_track_count = print(max(album_track_count))
|
||||
|
||||
# album = addict.Dict(())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
140
src/musicbrainzapi/api/lyrics/__init__.py
Normal file
140
src/musicbrainzapi/api/lyrics/__init__.py
Normal file
@@ -0,0 +1,140 @@
|
||||
from __future__ import annotations
|
||||
from typing import Union, Dict, List
|
||||
from dataclasses import dataclass
|
||||
import math
|
||||
|
||||
from beautifultable import BeautifulTable
|
||||
import click
|
||||
import numpy as np
|
||||
|
||||
|
||||
@dataclass
|
||||
class Lyrics:
|
||||
"""Lyrics object for an artist.
|
||||
"""
|
||||
|
||||
artist_id: str
|
||||
artist: str
|
||||
country: Union[str, None]
|
||||
all_albums_with_tracks: List[Dict[str, List[str]]]
|
||||
all_albums_with_lyrics: List[Dict[str, List[str]]]
|
||||
all_albums_lyrics_count: List[Dict[str, List[List[str, int]]]]
|
||||
all_albums_lyrics_sum: List[Dict[str, List[int, str]]]
|
||||
album_statistics: Dict[str, Dict[str, int]]
|
||||
year_statistics: Dict[str, Dict[str, int]]
|
||||
|
||||
_attributes = [
|
||||
'all_albums_with_tracks',
|
||||
'all_albums_with_lyrics',
|
||||
'all_albums_lyrics_count',
|
||||
'all_albums_lyrics_sum',
|
||||
'album_statistics',
|
||||
'year_statistics',
|
||||
]
|
||||
|
||||
def __init__(self) -> None:
|
||||
pass
|
||||
|
||||
def show_summary(self) -> None:
|
||||
"""Show the average word count for all lyrics
|
||||
|
||||
Returns
|
||||
-------
|
||||
None
|
||||
"""
|
||||
all_averages = []
|
||||
|
||||
for i in self.album_statistics.values():
|
||||
try:
|
||||
all_averages.append(i['avg'])
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
# print(all_averages)
|
||||
try:
|
||||
final_average = math.ceil(np.mean(all_averages))
|
||||
except ValueError:
|
||||
click.echo(
|
||||
'Oops! https://lyrics.ovh couldn\'t find any lyrics across any'
|
||||
' album. This is caused by inconsistent Artist names from'
|
||||
' Musicbrainz and lyrics.ovh. Try another artist.'
|
||||
)
|
||||
raise (SystemExit)
|
||||
output = BeautifulTable(max_width=200)
|
||||
output.set_style(BeautifulTable.STYLE_BOX_ROUNDED)
|
||||
output.column_headers = [
|
||||
'Average number of words in tracks across all albums\n'
|
||||
f'for {self.artist}'
|
||||
]
|
||||
output.append_row([final_average])
|
||||
click.echo(output)
|
||||
|
||||
return self
|
||||
|
||||
def show_summary_statistics(self, group_by: str) -> None:
|
||||
"""Summary
|
||||
|
||||
Parameters
|
||||
----------
|
||||
group_by : str
|
||||
Parameter to group statistics by. Valid options are album or year
|
||||
|
||||
Returns
|
||||
-------
|
||||
None
|
||||
"""
|
||||
stats_obj = getattr(self, f'{group_by}_statistics')
|
||||
stats = [
|
||||
'avg',
|
||||
'std',
|
||||
'min',
|
||||
'max',
|
||||
'median',
|
||||
'count',
|
||||
'p_10',
|
||||
'p_25',
|
||||
'p_75',
|
||||
'p_90',
|
||||
]
|
||||
output_0 = BeautifulTable(max_width=200)
|
||||
output_0.set_style(BeautifulTable.STYLE_BOX_ROUNDED)
|
||||
output_0.column_headers = [
|
||||
'Descriptive statistics for number of words in tracks across all'
|
||||
f' {group_by}s\nfor {self.artist}'
|
||||
]
|
||||
output_1 = BeautifulTable(max_width=200)
|
||||
output_1.set_style(BeautifulTable.STYLE_BOX_ROUNDED)
|
||||
output_1.column_headers = [
|
||||
group_by,
|
||||
stats[0],
|
||||
stats[1],
|
||||
stats[2],
|
||||
stats[3],
|
||||
stats[4],
|
||||
stats[5],
|
||||
stats[6],
|
||||
stats[7],
|
||||
stats[8],
|
||||
stats[9],
|
||||
]
|
||||
for group, s in stats_obj.items():
|
||||
try:
|
||||
output_1.append_row(
|
||||
[
|
||||
group,
|
||||
s.get(stats[0]),
|
||||
s.get(stats[1]),
|
||||
s.get(stats[2]),
|
||||
s.get(stats[3]),
|
||||
s.get(stats[4]),
|
||||
s.get(stats[5]),
|
||||
s.get(stats[6]),
|
||||
s.get(stats[7]),
|
||||
s.get(stats[8]),
|
||||
s.get(stats[9]),
|
||||
]
|
||||
)
|
||||
except AttributeError:
|
||||
continue
|
||||
output_0.append_row([output_1])
|
||||
click.echo(output_0)
|
||||
return self
|
||||
@@ -1,107 +1,49 @@
|
||||
from __future__ import annotations
|
||||
from abc import ABC, abstractmethod, abstractstaticmethod
|
||||
from dataclasses import dataclass
|
||||
|
||||
# from pprint import pprint
|
||||
from typing import Union, List, Dict
|
||||
from collections import Counter
|
||||
import html
|
||||
import json
|
||||
import math
|
||||
import os
|
||||
import string
|
||||
import math
|
||||
from typing import Union, Dict
|
||||
|
||||
from beautifultable import BeautifulTable
|
||||
import musicbrainzngs
|
||||
import click
|
||||
import addict
|
||||
import requests
|
||||
import click
|
||||
import musicbrainzngs
|
||||
import numpy as np
|
||||
import requests
|
||||
|
||||
from musicbrainzapi.api.lyrics.concrete_builder import LyricsConcreteBuilder
|
||||
from musicbrainzapi.api.lyrics import Lyrics
|
||||
from musicbrainzapi.api import authenticate
|
||||
|
||||
|
||||
class LyricsConcreteBuilder(ABC):
|
||||
"""docstring for Lyrics"""
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def product(self) -> None:
|
||||
pass
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def artist(self) -> str:
|
||||
pass
|
||||
|
||||
@artist.setter
|
||||
@abstractmethod
|
||||
def artist(self, artist: str) -> None:
|
||||
pass
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def country(self) -> Union[str, None]:
|
||||
pass
|
||||
|
||||
@country.setter
|
||||
@abstractmethod
|
||||
def country(self, country: Union[str, None]) -> None:
|
||||
pass
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def artist_id(self) -> str:
|
||||
pass
|
||||
|
||||
@artist_id.setter
|
||||
@abstractmethod
|
||||
def artist_id(self, artist_id: str) -> None:
|
||||
pass
|
||||
|
||||
@abstractstaticmethod
|
||||
def set_useragent():
|
||||
authenticate.set_useragent()
|
||||
|
||||
# @abstractstaticmethod
|
||||
# def browse_releases(self) -> dict:
|
||||
# pass
|
||||
|
||||
@abstractmethod
|
||||
def __init__(self) -> None:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def reset(self) -> None:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def find_artists(self) -> None:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def sort_artists(self) -> None:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_accuracy_scores(self) -> None:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_top_five_results(self) -> None:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def find_all_albums(self) -> None:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def find_all_tracks(self) -> None:
|
||||
pass
|
||||
|
||||
|
||||
class LyricsBuilder(LyricsConcreteBuilder):
|
||||
"""docstring for LyricsBuilder"""
|
||||
"""docstring for LyricsBuilder
|
||||
|
||||
Attributes
|
||||
----------
|
||||
album_statistics : addict.Dict
|
||||
Dictionary containing album statistics
|
||||
all_albums : list
|
||||
List of all albums + track titles
|
||||
all_albums_lyrics : list
|
||||
List of all albums + track lyrics
|
||||
all_albums_lyrics_count : list
|
||||
List of all albums + track lyrics counted by each word
|
||||
all_albums_lyrics_sum : list
|
||||
List of all albums + track lyrics counted and summed up.
|
||||
all_albums_lyrics_url : list
|
||||
List of all albums + link to lyrics api for each track.
|
||||
musicbrainz_artists : addict.Dict
|
||||
Dictionary of response from Musicbrainzapi
|
||||
release_group_ids : addict.Dict
|
||||
Dictionary of Musicbrainz release-group ids
|
||||
total_track_count : int
|
||||
Total number of tracks across all albums
|
||||
year_statistics : addict.Dict
|
||||
Dictionary containing album statistics
|
||||
"""
|
||||
|
||||
@property
|
||||
def product(self) -> Lyrics:
|
||||
@@ -152,25 +94,44 @@ class LyricsBuilder(LyricsConcreteBuilder):
|
||||
self.reset()
|
||||
|
||||
def reset(self) -> None:
|
||||
"""Reset the builder and create new product.
|
||||
"""
|
||||
self._product = Lyrics()
|
||||
|
||||
def find_artists(self) -> None:
|
||||
"""Find artists from the musicbrainz api
|
||||
|
||||
Returns
|
||||
-------
|
||||
None
|
||||
"""
|
||||
self.musicbrainz_artists = musicbrainzngs.search_artists(
|
||||
artist=self.artist, country=self.country
|
||||
)
|
||||
# pprint(self.musicbrainz_artists['artist-list'])
|
||||
return self
|
||||
|
||||
def sort_artists(self) -> None:
|
||||
"""Sort the artists from the Musicbrainzapi
|
||||
|
||||
Returns
|
||||
-------
|
||||
None
|
||||
"""
|
||||
self._sort_names = dict(
|
||||
(i.get('id'), f'{i.get("sort-name")} | {i.get("disambiguation")}')
|
||||
(i.get('id'), f'{i.get("name")} | {i.get("disambiguation")}')
|
||||
if i.get('disambiguation') is not None
|
||||
else (i.get('id'), f'{i.get("sort-name")}')
|
||||
else (i.get('id'), f'{i.get("name")}')
|
||||
for i in self.musicbrainz_artists['artist-list']
|
||||
)
|
||||
return self
|
||||
|
||||
def get_accuracy_scores(self) -> None:
|
||||
"""Get accuracy scores from the Musicbrainzapi
|
||||
|
||||
Returns
|
||||
-------
|
||||
None
|
||||
"""
|
||||
self._accuracy_scores = dict(
|
||||
(i.get('id'), int(i.get('ext:score', '0')))
|
||||
for i in self.musicbrainz_artists['artist-list']
|
||||
@@ -178,6 +139,12 @@ class LyricsBuilder(LyricsConcreteBuilder):
|
||||
return self
|
||||
|
||||
def get_top_five_results(self) -> None:
|
||||
"""Get the top five artists from the Musicbrainzapi
|
||||
|
||||
Returns
|
||||
-------
|
||||
None
|
||||
"""
|
||||
self._top_five_results = dict(
|
||||
(i, self._accuracy_scores.get(i))
|
||||
for i in sorted(
|
||||
@@ -189,6 +156,12 @@ class LyricsBuilder(LyricsConcreteBuilder):
|
||||
return self
|
||||
|
||||
def find_all_albums(self) -> None:
|
||||
"""Find all albums for the chosen artist
|
||||
|
||||
Returns
|
||||
-------
|
||||
None
|
||||
"""
|
||||
limit, offset, page = (100, 0, 1)
|
||||
|
||||
resp_0 = addict.Dict(
|
||||
@@ -245,6 +218,12 @@ class LyricsBuilder(LyricsConcreteBuilder):
|
||||
return self
|
||||
|
||||
def find_all_tracks(self) -> None:
|
||||
"""Find all tracks from all albums.
|
||||
|
||||
Returns
|
||||
-------
|
||||
None
|
||||
"""
|
||||
self.all_albums = list()
|
||||
total_albums = len(self.release_group_ids)
|
||||
self.total_track_count = 0
|
||||
@@ -309,6 +288,12 @@ class LyricsBuilder(LyricsConcreteBuilder):
|
||||
return self
|
||||
|
||||
def find_lyrics_urls(self) -> None:
|
||||
"""Construct the URL for the lyrics api.
|
||||
|
||||
Returns
|
||||
-------
|
||||
None
|
||||
"""
|
||||
self.all_albums_lyrics_url = list()
|
||||
for x in self.all_albums:
|
||||
for alb, tracks in x.items():
|
||||
@@ -326,8 +311,13 @@ class LyricsBuilder(LyricsConcreteBuilder):
|
||||
# pprint(self.all_albums_lyrics_url)
|
||||
return self
|
||||
|
||||
# change this for progressbar for i loop
|
||||
def find_all_lyrics(self) -> None:
|
||||
"""Get lyrics for each track from the lyrics api
|
||||
|
||||
Returns
|
||||
-------
|
||||
None
|
||||
"""
|
||||
self.all_albums_lyrics = list()
|
||||
|
||||
with click.progressbar(
|
||||
@@ -335,22 +325,28 @@ class LyricsBuilder(LyricsConcreteBuilder):
|
||||
label=f'Finding lyrics for {self.total_track_count}'
|
||||
f' tracks for {self.artist}. This may take some time! ☕️',
|
||||
) as bar:
|
||||
bar.update(5)
|
||||
for x in self.all_albums_lyrics_url:
|
||||
for alb, urls in x.items():
|
||||
bar.update(1)
|
||||
# bar.update(1)
|
||||
update = len(urls)
|
||||
lyrics = addict.Dict(
|
||||
(alb, [self.request_lyrics_from_url(i) for i in urls])
|
||||
)
|
||||
self.all_albums_lyrics.append(lyrics)
|
||||
bar.update(update - 1)
|
||||
bar.update(update)
|
||||
|
||||
with open(f'{os.getcwd()}/all_albums_lyrics.json', 'w') as f:
|
||||
json.dump(self.all_albums_lyrics, f, indent=2)
|
||||
return self
|
||||
|
||||
def count_words_in_lyrics(self) -> None:
|
||||
# remove punctuation, fix click bar
|
||||
"""Count all words in each track
|
||||
|
||||
Returns
|
||||
-------
|
||||
None
|
||||
"""
|
||||
self.all_albums_lyrics_count = list()
|
||||
# print(self.total_track_count)
|
||||
with click.progressbar(
|
||||
@@ -376,12 +372,18 @@ class LyricsBuilder(LyricsConcreteBuilder):
|
||||
click.echo(f'Processed lyrics for {self.total_track_count} tracks.')
|
||||
return self
|
||||
|
||||
# rename this
|
||||
def calculate_average_all_albums(self) -> None:
|
||||
"""Summary
|
||||
|
||||
Returns
|
||||
-------
|
||||
None
|
||||
Description
|
||||
"""
|
||||
self.all_albums_lyrics_sum = list()
|
||||
# album_lyrics = self.all_albums_lyrics_count
|
||||
with open(f'{os.getcwd()}/lyrics_count.json', 'r') as f:
|
||||
album_lyrics = json.load(f)
|
||||
album_lyrics = self.all_albums_lyrics_count
|
||||
# with open(f'{os.getcwd()}/lyrics_count.json', 'r') as f:
|
||||
# album_lyrics = json.load(f)
|
||||
count = 0
|
||||
for i in album_lyrics:
|
||||
count += len(i)
|
||||
@@ -406,15 +408,17 @@ class LyricsBuilder(LyricsConcreteBuilder):
|
||||
# print(d)
|
||||
self.all_albums_lyrics_sum.append(d)
|
||||
# print(count)
|
||||
with open(f'{os.getcwd()}/lyrics_sum_all_album.json', 'w+') as f:
|
||||
json.dump(self.all_albums_lyrics_sum, f)
|
||||
return self
|
||||
# with open(f'{os.getcwd()}/lyrics_sum_all_album.json', 'w+') as f:
|
||||
# json.dump(self.all_albums_lyrics_sum, f)
|
||||
# return self
|
||||
|
||||
def calculate_final_average_by_album(self) -> None:
|
||||
"""Calculates descriptive statistics by album.
|
||||
"""
|
||||
self.album_statistics = addict.Dict()
|
||||
# album_lyrics = self.all_albums_lyrics_sum
|
||||
with open(f'{os.getcwd()}/lyrics_sum_all_album.json', 'r') as f:
|
||||
album_lyrics = json.load(f)
|
||||
album_lyrics = self.all_albums_lyrics_sum
|
||||
# with open(f'{os.getcwd()}/lyrics_sum_all_album.json', 'r') as f:
|
||||
# album_lyrics = json.load(f)
|
||||
|
||||
for i in album_lyrics:
|
||||
for album, count in i.items():
|
||||
@@ -426,17 +430,18 @@ class LyricsBuilder(LyricsConcreteBuilder):
|
||||
self.album_statistics = addict.Dict(
|
||||
**self.album_statistics, **addict.Dict((album, _d))
|
||||
)
|
||||
with open(f'{os.getcwd()}/album_statistics.json', 'w') as f:
|
||||
json.dump(self.album_statistics, f, indent=2)
|
||||
# with open(f'{os.getcwd()}/album_statistics.json', 'w') as f:
|
||||
# json.dump(self.album_statistics, f, indent=2)
|
||||
# pprint(self.album_statistics)
|
||||
|
||||
# implement above in this
|
||||
def calculate_final_average_by_year(self) -> None:
|
||||
"""Calculates descriptive statistic by year.
|
||||
"""
|
||||
group_by_years = addict.Dict()
|
||||
self.year_statistics = addict.Dict()
|
||||
# album_lyrics = self.all_albums_lyrics_sum
|
||||
with open(f'{os.getcwd()}/lyrics_sum_all_album.json', 'r') as f:
|
||||
album_lyrics = json.load(f)
|
||||
album_lyrics = self.all_albums_lyrics_sum
|
||||
# with open(f'{os.getcwd()}/lyrics_sum_all_album.json', 'r') as f:
|
||||
# album_lyrics = json.load(f)
|
||||
|
||||
# Merge years together
|
||||
for i in album_lyrics:
|
||||
@@ -464,12 +469,38 @@ class LyricsBuilder(LyricsConcreteBuilder):
|
||||
|
||||
@staticmethod
|
||||
def construct_lyrics_url(artist: str, song: str) -> str:
|
||||
"""Builds the URL for the lyrics api.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
artist : str
|
||||
Artist
|
||||
song : str
|
||||
Track title
|
||||
|
||||
Returns
|
||||
-------
|
||||
str
|
||||
URL for lyrics from the lyrics api.
|
||||
"""
|
||||
lyrics_api_base = 'https://api.lyrics.ovh/v1'
|
||||
lyrics_api_url = html.escape(f'{lyrics_api_base}/{artist}/{song}')
|
||||
return lyrics_api_url
|
||||
|
||||
@staticmethod
|
||||
def request_lyrics_from_url(url: str) -> str:
|
||||
"""Gets lyrics from the lyrics api.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
url : str
|
||||
URL of the track for the lyrics api.
|
||||
|
||||
Returns
|
||||
-------
|
||||
str
|
||||
Lyrics of the trakc
|
||||
"""
|
||||
resp = requests.get(url)
|
||||
|
||||
# No lyrics for a song will return a key of 'error', we pass on this.
|
||||
@@ -481,11 +512,35 @@ class LyricsBuilder(LyricsConcreteBuilder):
|
||||
|
||||
@staticmethod
|
||||
def strip_punctuation(word: str) -> str:
|
||||
"""Removes punctuation from lyrics.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
word : str
|
||||
Word to remove punctuation from.
|
||||
|
||||
Returns
|
||||
-------
|
||||
str
|
||||
Same word without any punctuation.
|
||||
"""
|
||||
_strip = word.translate(str.maketrans('', '', string.punctuation))
|
||||
return _strip
|
||||
|
||||
@staticmethod
|
||||
def get_descriptive_statistics(nums: list) -> Dict[str, int]:
|
||||
"""Calculates descriptive statistics.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
nums : list
|
||||
A list containing total number of words from a track.
|
||||
|
||||
Returns
|
||||
-------
|
||||
Dict[str, int]
|
||||
Dictionary of statistic and value.
|
||||
"""
|
||||
if len(nums) == 0:
|
||||
return
|
||||
avg = math.ceil(np.mean(nums))
|
||||
@@ -511,232 +566,3 @@ class LyricsBuilder(LyricsConcreteBuilder):
|
||||
('count', count),
|
||||
)
|
||||
return _d
|
||||
|
||||
|
||||
class LyricsClickDirector:
|
||||
"""docstring for LyricsClickDirector"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._builder = None
|
||||
|
||||
@property
|
||||
def builder(self) -> LyricsBuilder:
|
||||
return self._builder
|
||||
|
||||
@builder.setter
|
||||
def builder(self, builder: LyricsBuilder) -> None:
|
||||
self._builder = builder
|
||||
|
||||
def _get_initial_artists(self, artist: str, country: str) -> None:
|
||||
self.builder.artist = artist
|
||||
self.builder.country = country
|
||||
self.builder.set_useragent()
|
||||
self.builder.find_artists()
|
||||
self.builder.sort_artists()
|
||||
self.builder.get_accuracy_scores()
|
||||
self.builder.get_top_five_results()
|
||||
return self
|
||||
|
||||
def _confirm_final_artist(self) -> None:
|
||||
artist_meta = None
|
||||
for i, j in self.builder._top_five_results.items():
|
||||
artist_meta = 'Multiple' if j <= 100 else None
|
||||
|
||||
if artist_meta == 'Multiple':
|
||||
_position = []
|
||||
click.echo(
|
||||
click.style(
|
||||
f'Musicbrainz found several results for '
|
||||
f'{self.builder.artist[0]}. Which artist/group do you want'
|
||||
'?',
|
||||
fg='green',
|
||||
)
|
||||
)
|
||||
for i, j in zip(self.builder._top_five_results, range(1, 6)):
|
||||
click.echo(
|
||||
f'[{j}] {self.builder._sort_names.get(i)}'
|
||||
f' ({self.builder._accuracy_scores.get(i)}% match)'
|
||||
)
|
||||
_position.append(i)
|
||||
chosen = int(
|
||||
click.prompt(
|
||||
click.style(f'Enter choice, default is', blink=True),
|
||||
default=1,
|
||||
type=click.IntRange(
|
||||
1, len(self.builder._top_five_results)
|
||||
),
|
||||
)
|
||||
)
|
||||
choice = _position[chosen - 1]
|
||||
click.echo(f'You chose {self.builder._sort_names.get(choice)}')
|
||||
self._artist = self.builder._sort_names.get(choice).split('|')[0]
|
||||
self._artist_id = choice
|
||||
|
||||
# Set artist and artistID on builder + product
|
||||
self.builder.artist_id = self._artist_id
|
||||
self.builder.artist = self._artist
|
||||
|
||||
elif artist_meta is None:
|
||||
click.echo(
|
||||
f'Musicbrainz did not find any results for '
|
||||
f'{self.builder.artist[0]}. Check the spelling or consider '
|
||||
'alternative names that the artist/group may go by.'
|
||||
)
|
||||
raise SystemExit()
|
||||
return self
|
||||
|
||||
def _query_for_data(self) -> None:
|
||||
self.builder.find_all_albums()
|
||||
self.builder.find_all_tracks()
|
||||
self.builder._product.all_albums_with_tracks = self.builder.all_albums
|
||||
return self
|
||||
|
||||
def _get_lyrics(self) -> None:
|
||||
self.builder.find_lyrics_urls()
|
||||
self.builder.find_all_lyrics()
|
||||
self.builder._product.all_albums_with_lyrics = (
|
||||
self.builder.all_albums_lyrics
|
||||
)
|
||||
self.builder.count_words_in_lyrics()
|
||||
with open(f'{os.getcwd()}/lyrics_count.json', 'w+') as file:
|
||||
json.dump(
|
||||
self.builder.all_albums_lyrics_count,
|
||||
file,
|
||||
indent=2,
|
||||
sort_keys=True,
|
||||
)
|
||||
self.builder._product.all_albums_lyrics_count = (
|
||||
self.builder.all_albums_lyrics_count
|
||||
)
|
||||
return self
|
||||
|
||||
def _calculate_basic_statistics(self) -> None:
|
||||
self.builder.calculate_average_all_albums()
|
||||
self.builder._product.all_albums_lyrics_sum = (
|
||||
self.builder.all_albums_lyrics_sum
|
||||
)
|
||||
return self
|
||||
|
||||
def _calculate_descriptive_statistics(self) -> None:
|
||||
self.builder.calculate_final_average_by_album()
|
||||
self.builder.calculate_final_average_by_year()
|
||||
self.builder._product.album_statistics = self.builder.album_statistics
|
||||
self.builder._product.year_statistics = self.builder.year_statistics
|
||||
return self
|
||||
|
||||
def _dev(self) -> None:
|
||||
self.builder.calculate_final_average_by_album()
|
||||
self.builder.calculate_final_average_by_year()
|
||||
self.builder._product.album_statistics = self.builder.album_statistics
|
||||
self.builder._product.year_statistics = self.builder.year_statistics
|
||||
self.builder._product.artist_id = None
|
||||
self.builder._product.artist = 'Katzenjammer'
|
||||
self.builder._product.show_summary()
|
||||
self.builder._product.show_summary_statistics(group_by='year')
|
||||
return self
|
||||
|
||||
@staticmethod
|
||||
def _get_product(builder_inst: LyricsBuilder) -> Lyrics:
|
||||
return builder_inst._product
|
||||
|
||||
|
||||
@dataclass
|
||||
class Lyrics:
|
||||
"""docstring for Lyrics"""
|
||||
|
||||
artist_id: str
|
||||
artist: str
|
||||
country: Union[str, None]
|
||||
all_albums_with_tracks: List[Dict[str, List[str]]]
|
||||
all_albums_with_lyrics: List[Dict[str, List[str]]]
|
||||
all_albums_lyrics_count: List[Dict[str, List[List[str, int]]]]
|
||||
all_albums_lyrics_sum: List[Dict[str, List[int, str]]]
|
||||
album_statistics: Dict[str, Dict[str, int]]
|
||||
year_statistics: Dict[str, Dict[str, int]]
|
||||
|
||||
def __init__(self) -> None:
|
||||
pass
|
||||
|
||||
def show_summary(self):
|
||||
all_averages = []
|
||||
|
||||
for i in self.album_statistics.values():
|
||||
try:
|
||||
all_averages.append(i['avg'])
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
print(all_averages)
|
||||
try:
|
||||
final_average = math.ceil(np.mean(all_averages))
|
||||
except ValueError:
|
||||
click.echo(
|
||||
'Oops! https://lyrics.ovh couldn\'t find any lyrics across all'
|
||||
' albums. This is caused by inconsistent Artist names from'
|
||||
' Musicbrainz and lyrics.ovh. Try another artist.'
|
||||
)
|
||||
raise(SystemExit)
|
||||
output = BeautifulTable(max_width=200)
|
||||
output.set_style(BeautifulTable.STYLE_BOX_ROUNDED)
|
||||
output.column_headers = [
|
||||
'Average number of words in tracks across all albums\n'
|
||||
f'for {self.artist}'
|
||||
]
|
||||
output.append_row([final_average])
|
||||
click.echo(output)
|
||||
|
||||
return self
|
||||
|
||||
def show_summary_statistics(self, group_by: str) -> None:
|
||||
stats_obj = getattr(self, f'{group_by}_statistics')
|
||||
stats = [
|
||||
'avg',
|
||||
'std',
|
||||
'min',
|
||||
'max',
|
||||
'median',
|
||||
'count',
|
||||
'p_10',
|
||||
'p_25',
|
||||
'p_75',
|
||||
'p_90',
|
||||
]
|
||||
output_0 = BeautifulTable(max_width=200)
|
||||
output_0.set_style(BeautifulTable.STYLE_BOX_ROUNDED)
|
||||
output_0.column_headers = [
|
||||
'Descriptive statistics for number of words in tracks across all'
|
||||
f' {group_by}s\nfor {self.artist}'
|
||||
]
|
||||
output_1 = BeautifulTable(max_width=200)
|
||||
output_1.set_style(BeautifulTable.STYLE_BOX_ROUNDED)
|
||||
output_1.column_headers = [
|
||||
group_by,
|
||||
stats[0],
|
||||
stats[1],
|
||||
stats[2],
|
||||
stats[3],
|
||||
stats[4],
|
||||
stats[5],
|
||||
stats[6],
|
||||
stats[7],
|
||||
stats[8],
|
||||
stats[9],
|
||||
]
|
||||
for group, s in stats_obj.items():
|
||||
output_1.append_row(
|
||||
[
|
||||
group,
|
||||
s.get(stats[0]),
|
||||
s.get(stats[1]),
|
||||
s.get(stats[2]),
|
||||
s.get(stats[3]),
|
||||
s.get(stats[4]),
|
||||
s.get(stats[5]),
|
||||
s.get(stats[6]),
|
||||
s.get(stats[7]),
|
||||
s.get(stats[8]),
|
||||
s.get(stats[9]),
|
||||
]
|
||||
)
|
||||
output_0.append_row([output_1])
|
||||
click.echo(output_0)
|
||||
return self
|
||||
81
src/musicbrainzapi/api/lyrics/concrete_builder.py
Normal file
81
src/musicbrainzapi/api/lyrics/concrete_builder.py
Normal file
@@ -0,0 +1,81 @@
|
||||
from __future__ import annotations
|
||||
from abc import ABC, abstractstaticmethod, abstractmethod
|
||||
from typing import Union
|
||||
|
||||
from musicbrainzapi.api import authenticate
|
||||
|
||||
|
||||
class LyricsConcreteBuilder(ABC):
|
||||
"""Abstract concrete builder for Lyrics
|
||||
"""
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def product(self) -> None:
|
||||
pass
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def artist(self) -> str:
|
||||
pass
|
||||
|
||||
@artist.setter
|
||||
@abstractmethod
|
||||
def artist(self, artist: str) -> None:
|
||||
pass
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def country(self) -> Union[str, None]:
|
||||
pass
|
||||
|
||||
@country.setter
|
||||
@abstractmethod
|
||||
def country(self, country: Union[str, None]) -> None:
|
||||
pass
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def artist_id(self) -> str:
|
||||
pass
|
||||
|
||||
@artist_id.setter
|
||||
@abstractmethod
|
||||
def artist_id(self, artist_id: str) -> None:
|
||||
pass
|
||||
|
||||
@abstractstaticmethod
|
||||
def set_useragent():
|
||||
authenticate.set_useragent()
|
||||
|
||||
@abstractmethod
|
||||
def __init__(self) -> None:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def reset(self) -> None:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def find_artists(self) -> None:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def sort_artists(self) -> None:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_accuracy_scores(self) -> None:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_top_five_results(self) -> None:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def find_all_albums(self) -> None:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def find_all_tracks(self) -> None:
|
||||
pass
|
||||
202
src/musicbrainzapi/api/lyrics/director.py
Normal file
202
src/musicbrainzapi/api/lyrics/director.py
Normal file
@@ -0,0 +1,202 @@
|
||||
from __future__ import annotations
|
||||
import json
|
||||
import os
|
||||
|
||||
import click
|
||||
|
||||
from musicbrainzapi.api.lyrics.builder import LyricsBuilder
|
||||
from musicbrainzapi.api.lyrics import Lyrics
|
||||
|
||||
|
||||
class LyricsClickDirector:
|
||||
"""Director for Lyrics builder.
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._builder = None
|
||||
|
||||
@property
|
||||
def builder(self) -> LyricsBuilder:
|
||||
return self._builder
|
||||
|
||||
@builder.setter
|
||||
def builder(self, builder: LyricsBuilder) -> None:
|
||||
self._builder = builder
|
||||
|
||||
def _get_initial_artists(self, artist: str, country: str) -> None:
|
||||
"""Search Musicbrainz api for an artist
|
||||
|
||||
Parameters
|
||||
----------
|
||||
artist : str
|
||||
Artist to search for
|
||||
country : str
|
||||
Country artist comes from.
|
||||
|
||||
Returns
|
||||
-------
|
||||
None
|
||||
"""
|
||||
self.builder.artist = artist
|
||||
self.builder.country = country
|
||||
self.builder.set_useragent()
|
||||
self.builder.find_artists()
|
||||
self.builder.sort_artists()
|
||||
self.builder.get_accuracy_scores()
|
||||
self.builder.get_top_five_results()
|
||||
return self
|
||||
|
||||
def _confirm_final_artist(self) -> None:
|
||||
"""Confirm the artist from the user.
|
||||
|
||||
Returns
|
||||
-------
|
||||
None
|
||||
|
||||
Raises
|
||||
------
|
||||
SystemExit
|
||||
If no artist is found will cleanly quit.
|
||||
"""
|
||||
artist_meta = None
|
||||
for i, j in self.builder._top_five_results.items():
|
||||
artist_meta = 'Multiple' if j <= 100 else None
|
||||
|
||||
if artist_meta == 'Multiple':
|
||||
_position = []
|
||||
click.echo(
|
||||
click.style(
|
||||
f'Musicbrainz found several results for '
|
||||
f'{self.builder.artist[0]}. Which artist/group do you want'
|
||||
'?',
|
||||
fg='green',
|
||||
)
|
||||
)
|
||||
for i, j in zip(self.builder._top_five_results, range(1, 6)):
|
||||
click.echo(
|
||||
f'[{j}] {self.builder._sort_names.get(i)}'
|
||||
f' ({self.builder._accuracy_scores.get(i)}% match)'
|
||||
)
|
||||
_position.append(i)
|
||||
chosen = int(
|
||||
click.prompt(
|
||||
click.style(f'Enter choice, default is', blink=True),
|
||||
default=1,
|
||||
type=click.IntRange(
|
||||
1, len(self.builder._top_five_results)
|
||||
),
|
||||
)
|
||||
)
|
||||
choice = _position[chosen - 1]
|
||||
click.echo(f'You chose {self.builder._sort_names.get(choice)}')
|
||||
self._artist = self.builder._sort_names.get(choice).split('|')[0]
|
||||
self._artist_id = choice
|
||||
|
||||
# Set artist and artistID on builder + product
|
||||
self.builder.artist_id = self._artist_id
|
||||
self.builder.artist = self._artist
|
||||
|
||||
elif artist_meta is None:
|
||||
click.echo(
|
||||
f'Musicbrainz did not find any results for '
|
||||
f'{self.builder.artist[0]}. Check the spelling or consider '
|
||||
'alternative names that the artist/group may go by.'
|
||||
)
|
||||
raise SystemExit()
|
||||
return self
|
||||
|
||||
def _query_for_data(self) -> None:
|
||||
"""Query Musicbrainz api for albums + track data.
|
||||
|
||||
Returns
|
||||
-------
|
||||
None
|
||||
"""
|
||||
self.builder.find_all_albums()
|
||||
self.builder.find_all_tracks()
|
||||
self.builder._product.all_albums_with_tracks = self.builder.all_albums
|
||||
return self
|
||||
|
||||
def _get_lyrics(self) -> None:
|
||||
"""Get Lyrics for each track
|
||||
|
||||
Returns
|
||||
-------
|
||||
None
|
||||
"""
|
||||
self.builder.find_lyrics_urls()
|
||||
self.builder.find_all_lyrics()
|
||||
self.builder._product.all_albums_with_lyrics = (
|
||||
self.builder.all_albums_lyrics
|
||||
)
|
||||
self.builder.count_words_in_lyrics()
|
||||
with open(f'{os.getcwd()}/lyrics_count.json', 'w+') as file:
|
||||
json.dump(
|
||||
self.builder.all_albums_lyrics_count,
|
||||
file,
|
||||
indent=2,
|
||||
sort_keys=True,
|
||||
)
|
||||
self.builder._product.all_albums_lyrics_count = (
|
||||
self.builder.all_albums_lyrics_count
|
||||
)
|
||||
return self
|
||||
|
||||
def _calculate_basic_statistics(self) -> None:
|
||||
"""Calculate a basic average for all tracks.
|
||||
|
||||
Returns
|
||||
-------
|
||||
None
|
||||
"""
|
||||
self.builder.calculate_average_all_albums()
|
||||
self.builder._product.all_albums_lyrics_sum = (
|
||||
self.builder.all_albums_lyrics_sum
|
||||
)
|
||||
return self
|
||||
|
||||
def _calculate_descriptive_statistics(self) -> None:
|
||||
"""Calculate descriptive statistics for album and/or year.
|
||||
|
||||
Returns
|
||||
-------
|
||||
None
|
||||
"""
|
||||
self.builder.calculate_final_average_by_album()
|
||||
self.builder.calculate_final_average_by_year()
|
||||
self.builder._product.album_statistics = self.builder.album_statistics
|
||||
self.builder._product.year_statistics = self.builder.year_statistics
|
||||
return self
|
||||
|
||||
def _dev(self) -> None:
|
||||
"""Dev function - used for testing
|
||||
|
||||
Returns
|
||||
-------
|
||||
None
|
||||
"""
|
||||
self.builder.calculate_final_average_by_album()
|
||||
self.builder.calculate_final_average_by_year()
|
||||
self.builder._product.album_statistics = self.builder.album_statistics
|
||||
self.builder._product.year_statistics = self.builder.year_statistics
|
||||
self.builder._product.artist_id = None
|
||||
self.builder._product.artist = 'Katzenjammer'
|
||||
self.builder._product.show_summary()
|
||||
self.builder._product.show_summary_statistics(group_by='year')
|
||||
return self
|
||||
|
||||
@staticmethod
|
||||
def _get_product(builder_inst: LyricsBuilder) -> Lyrics:
|
||||
"""Returns the constructed Lyrics object
|
||||
|
||||
Parameters
|
||||
----------
|
||||
builder_inst : LyricsBuilder
|
||||
Builder class for Lyrics object
|
||||
|
||||
Returns
|
||||
-------
|
||||
Lyrics
|
||||
Lyrics object
|
||||
"""
|
||||
return builder_inst._product
|
||||
@@ -1,5 +1,4 @@
|
||||
import os
|
||||
import sys
|
||||
from importlib import import_module
|
||||
|
||||
import click
|
||||
@@ -31,17 +30,16 @@ class ComplexCLI(click.MultiCommand):
|
||||
rv.sort()
|
||||
return rv
|
||||
|
||||
# def get_command(self, ctx, name):
|
||||
# try:
|
||||
# mod = import_module(f'musicbrainzapi.cli.commands.cmd_{name}')
|
||||
# except ImportError as e:
|
||||
# print(e)
|
||||
# return
|
||||
# return mod.cli
|
||||
|
||||
def get_command(self, ctx, name):
|
||||
try:
|
||||
if sys.version_info[0] == 2:
|
||||
name = name.encode('ascii', 'replace')
|
||||
mod = import_module(f'musicbrainzapi.cli.commands.cmd_{name}')
|
||||
# mod = __import__(
|
||||
# 'complex.commands.cmd_' + name, None, None, ['cli']
|
||||
# )
|
||||
except ImportError as e:
|
||||
print(e)
|
||||
return
|
||||
return mod.cli
|
||||
|
||||
|
||||
@@ -50,25 +48,21 @@ class ComplexCLI(click.MultiCommand):
|
||||
'-p',
|
||||
'--path',
|
||||
type=click.Path(
|
||||
exists=False, file_okay=False, resolve_path=True, writable=True
|
||||
exists=True, file_okay=False, resolve_path=True, writable=True
|
||||
),
|
||||
help='Path to save results.',
|
||||
default=os.path.expanduser('~/.musicbrainzapi')
|
||||
default=os.getcwd()
|
||||
)
|
||||
@click.option('-v', '--verbose', is_flag=True, help='Enables verbose mode.')
|
||||
# @click.option('-v', '--verbose', is_flag=True, help='Enables verbose mode.')
|
||||
@click.version_option(
|
||||
version=__version__,
|
||||
prog_name=__header__,
|
||||
message=f'{__header__} version {__version__} 🎤',
|
||||
)
|
||||
@pass_environment
|
||||
def cli(ctx, verbose, path):
|
||||
def cli(ctx, path):
|
||||
"""A complex command line interface."""
|
||||
ctx.verbose = verbose
|
||||
# ctx.verbose = verbose
|
||||
if path is not None:
|
||||
click.echo(f'Path set to {os.path.expanduser(path)}')
|
||||
ctx.path = os.path.expanduser(path)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
cli()
|
||||
|
||||
@@ -1,23 +1,32 @@
|
||||
import json
|
||||
from typing import Union
|
||||
|
||||
import click
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from musicbrainzapi.cli.cli import pass_environment
|
||||
from musicbrainzapi.api.command_builders import lyrics
|
||||
|
||||
import musicbrainzapi.wordcloud
|
||||
from musicbrainzapi.api.lyrics.builder import LyricsBuilder
|
||||
from musicbrainzapi.api.lyrics.director import LyricsClickDirector
|
||||
|
||||
|
||||
# @click.argument('path', required=False, type=click.Path(resolve_path=True))
|
||||
# @click.command(short_help='a test command')
|
||||
|
||||
|
||||
@click.option('--dev', is_flag=True)
|
||||
@click.option('--dev', is_flag=True, help='Development flag. Do not use.')
|
||||
@click.option(
|
||||
'--save-output',
|
||||
required=False,
|
||||
help='Save the output to json files locally. Will use the path parameter if'
|
||||
' provided else defaults to current working directory.',
|
||||
help='Save the output to json files locally. Will use the path parameter'
|
||||
' if provided else defaults to current working directory.',
|
||||
is_flag=True,
|
||||
default=False
|
||||
default=False,
|
||||
)
|
||||
@click.option(
|
||||
'--wordcloud',
|
||||
required=False,
|
||||
help='Generate a wordcloud from lyrics.',
|
||||
is_flag=True,
|
||||
default=False,
|
||||
)
|
||||
@click.option(
|
||||
'--show-summary',
|
||||
@@ -51,12 +60,30 @@ def cli(
|
||||
country: Union[str, None],
|
||||
dev: bool,
|
||||
show_summary: str,
|
||||
save_output: bool
|
||||
wordcloud: bool,
|
||||
save_output: bool,
|
||||
) -> None:
|
||||
"""Search for lyrics statistics of an Artist/Group."""
|
||||
# lyrics_obj = list()
|
||||
director = lyrics.LyricsClickDirector()
|
||||
builder = lyrics.LyricsBuilder()
|
||||
"""Search for lyrics statistics of an Artist/Group.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
ctx : musicbrainzapi.cli.cli.Environment
|
||||
click environment class
|
||||
artist : str
|
||||
artist
|
||||
country : Union[str, None]
|
||||
country
|
||||
dev : bool
|
||||
dev flag - not to be used
|
||||
show_summary : str
|
||||
summary flag - used to display descriptive statistics
|
||||
wordcloud : bool
|
||||
wordcloud flag - used to create a wordcloud from lyrics
|
||||
save_output : bool
|
||||
save output flag - used to save output locally to disk
|
||||
"""
|
||||
director = LyricsClickDirector()
|
||||
builder = LyricsBuilder()
|
||||
director.builder = builder
|
||||
if dev:
|
||||
director._dev()
|
||||
@@ -77,9 +104,37 @@ def cli(
|
||||
|
||||
# Show basic count
|
||||
lyrics_0.show_summary()
|
||||
|
||||
# Show summary statistics
|
||||
if show_summary == 'all':
|
||||
lyrics_0.show_summary_statistics(group_by='album')
|
||||
lyrics_0.show_summary_statistics(group_by='year')
|
||||
elif show_summary in ['album', 'year']:
|
||||
lyrics_0.show_summary_statistics(group_by=show_summary)
|
||||
|
||||
# Show wordcloud
|
||||
if wordcloud:
|
||||
click.echo('Generating wordcloud')
|
||||
cloud = musicbrainzapi.wordcloud.LyricsWordcloud.use_microphone(
|
||||
lyrics_0.all_albums_lyrics_count
|
||||
)
|
||||
cloud.create_word_cloud()
|
||||
show = click.confirm(
|
||||
'Wordcloud ready - press enter to show.', default=True
|
||||
)
|
||||
plt.imshow(
|
||||
cloud.wc.recolor(
|
||||
color_func=cloud.generate_grey_colours, random_state=3
|
||||
),
|
||||
interpolation='bilinear',
|
||||
)
|
||||
plt.axis('off')
|
||||
if show:
|
||||
plt.show()
|
||||
if save_output:
|
||||
click.echo(f'Saving output to {ctx.path}')
|
||||
path = ctx.path if ctx.path[-1] == '/' else ctx.path + '/'
|
||||
attr = lyrics_0._attributes
|
||||
for a in attr:
|
||||
with open(f'{path}{a}.json', 'w') as f:
|
||||
json.dump(getattr(lyrics_0, a), f, indent=2)
|
||||
|
||||
BIN
src/musicbrainzapi/wordcloud/.DS_Store
vendored
Normal file
BIN
src/musicbrainzapi/wordcloud/.DS_Store
vendored
Normal file
Binary file not shown.
172
src/musicbrainzapi/wordcloud/__init__.py
Normal file
172
src/musicbrainzapi/wordcloud/__init__.py
Normal file
@@ -0,0 +1,172 @@
|
||||
from __future__ import annotations
|
||||
import collections
|
||||
from importlib import resources
|
||||
import random
|
||||
import re
|
||||
import typing
|
||||
|
||||
from matplotlib import pyplot as plt
|
||||
from PIL import Image
|
||||
from wordcloud import STOPWORDS, WordCloud
|
||||
import numpy as np
|
||||
|
||||
from musicbrainzapi.api.lyrics import Lyrics
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
import PIL.PngImagePlugin.PngImageFile
|
||||
|
||||
|
||||
class LyricsWordcloud:
|
||||
"""Create a word cloud from Lyrics.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
all_albums_lyrics_count : list
|
||||
List of all albums + track lyrics counted by each word
|
||||
char_mask : np.array
|
||||
numpy array containing data for the word cloud image
|
||||
freq : collections.Counter
|
||||
Counter object containing counts for all words across all tracks
|
||||
lyrics_list : list
|
||||
List of all words from all lyrics across all tracks.
|
||||
pillow_img : PIL.PngImagePlugin.PngImageFile
|
||||
pillow image of the word cloud base
|
||||
wc : wordcloud.WordCloud
|
||||
WordCloud object
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
pillow_img: 'PIL.PngImagePlugin.PngImageFile',
|
||||
all_albums_lyrics_count: 'Lyrics.all_albums_lyrics_count',
|
||||
):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
pillow_img : PIL.PngImagePlugin.PngImageFile
|
||||
pillow image of the word cloud base
|
||||
all_albums_lyrics_count : Lyrics.all_albums_lyrics_count
|
||||
List of all albums + track lyrics counted by each word
|
||||
"""
|
||||
self.pillow_img = pillow_img
|
||||
self.all_albums_lyrics_count = all_albums_lyrics_count
|
||||
|
||||
@classmethod
|
||||
def use_microphone(
|
||||
cls, all_albums_lyrics_count: 'Lyrics.all_albums_lyrics_count',
|
||||
) -> LyricsWordcloud:
|
||||
"""Class method to instantiate with a microphone base image.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
all_albums_lyrics_count : Lyrics.all_albums_lyrics_count
|
||||
List of all albums + track lyrics counted by each word
|
||||
"""
|
||||
mic_resource = resources.path(
|
||||
'musicbrainzapi.wordcloud.resources', 'mic.png'
|
||||
)
|
||||
with mic_resource as m:
|
||||
mic_img = Image.open(m)
|
||||
|
||||
return cls(mic_img, all_albums_lyrics_count)
|
||||
|
||||
def _get_lyrics_list(self) -> None:
|
||||
"""Gets all words from lyrics in a single list + cleans them.
|
||||
|
||||
Returns
|
||||
-------
|
||||
None
|
||||
"""
|
||||
self.lyrics_list = list()
|
||||
for i in self.all_albums_lyrics_count:
|
||||
for album, lyric in i.items():
|
||||
for track in lyric:
|
||||
try:
|
||||
for word in track:
|
||||
for _ in range(1, word[1]):
|
||||
cleaned = word[0]
|
||||
cleaned = re.sub(
|
||||
r'[\(\[].*?[\)\]]', ' ', cleaned
|
||||
)
|
||||
cleaned = re.sub(
|
||||
r'[^a-zA-Z0-9\s]', '', cleaned
|
||||
)
|
||||
cleaned = cleaned.lower()
|
||||
if cleaned in STOPWORDS:
|
||||
continue
|
||||
self.lyrics_list.append(cleaned)
|
||||
except IndexError:
|
||||
pass
|
||||
return self
|
||||
|
||||
def _get_frequencies(self) -> None:
|
||||
"""Get frequencies of words from a list.
|
||||
"""
|
||||
self.freq = collections.Counter(self.lyrics_list)
|
||||
|
||||
def _get_char_mask(self) -> None:
|
||||
"""Gets a numpy array for the image file.
|
||||
"""
|
||||
self.char_mask = np.array(self.pillow_img)
|
||||
|
||||
@staticmethod
|
||||
def generate_grey_colours(
|
||||
word: str,
|
||||
font_size: str,
|
||||
random_state: typing.Union[None, bool] = None,
|
||||
*args,
|
||||
**kwargs,
|
||||
) -> str:
|
||||
colour = f'hsl(0, 0%, {random.randint(60, 100)}%)'
|
||||
return colour
|
||||
|
||||
def _generate_word_cloud(self) -> None:
|
||||
"""Generates a word cloud
|
||||
|
||||
Returns
|
||||
-------
|
||||
None
|
||||
"""
|
||||
self.wc = WordCloud(
|
||||
max_words=150,
|
||||
width=1500,
|
||||
height=1500,
|
||||
mask=self.char_mask,
|
||||
random_state=1,
|
||||
).generate_from_frequencies(self.freq)
|
||||
return self
|
||||
|
||||
def _generate_plot(self) -> None:
|
||||
"""Plots the wordcloud and sets matplotlib options.
|
||||
|
||||
Returns
|
||||
-------
|
||||
None
|
||||
"""
|
||||
plt.imshow(
|
||||
self.wc.recolor(
|
||||
color_func=self.generate_grey_colours, random_state=3
|
||||
),
|
||||
interpolation='bilinear',
|
||||
)
|
||||
plt.axis('off')
|
||||
return self
|
||||
|
||||
def show_word_cloud(self):
|
||||
"""Shows the word cloud.
|
||||
"""
|
||||
plt.show()
|
||||
|
||||
def create_word_cloud(self) -> None:
|
||||
"""Creates a word cloud
|
||||
|
||||
Returns
|
||||
-------
|
||||
None
|
||||
"""
|
||||
self._get_lyrics_list()
|
||||
self._get_frequencies()
|
||||
self._get_char_mask()
|
||||
self._generate_word_cloud()
|
||||
self._generate_plot()
|
||||
return self
|
||||
BIN
src/musicbrainzapi/wordcloud/resources/.DS_Store
vendored
Normal file
BIN
src/musicbrainzapi/wordcloud/resources/.DS_Store
vendored
Normal file
Binary file not shown.
0
src/musicbrainzapi/wordcloud/resources/__init__.py
Normal file
0
src/musicbrainzapi/wordcloud/resources/__init__.py
Normal file
BIN
src/musicbrainzapi/wordcloud/resources/mic.png
Normal file
BIN
src/musicbrainzapi/wordcloud/resources/mic.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 220 KiB |
Reference in New Issue
Block a user