Merge branch 'rework' into develop

This commit is contained in:
2020-03-07 20:37:51 +00:00
24 changed files with 1054 additions and 7291 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

View File

@@ -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
}

View File

@@ -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
]
}
]

View File

@@ -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"
]
}
]

View File

@@ -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"]}]

File diff suppressed because it is too large Load Diff

208
poetry.lock generated
View File

@@ -109,6 +109,17 @@ optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
version = "0.4.3" 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]] [[package]]
category = "main" category = "main"
description = "Internationalized Domain Names in Applications (IDNA)" description = "Internationalized Domain Names in Applications (IDNA)"
@@ -147,6 +158,32 @@ parso = ">=0.5.2"
[package.extras] [package.extras]
testing = ["colorama (0.4.1)", "docopt", "pytest (>=3.9.0,<5.0.0)"] 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]] [[package]]
category = "dev" category = "dev"
description = "McCabe checker, plugin for flake8" description = "McCabe checker, plugin for flake8"
@@ -163,6 +200,14 @@ optional = false
python-versions = ">=3.5" python-versions = ">=3.5"
version = "8.2.0" version = "8.2.0"
[[package]]
category = "main"
description = "multidict implementation"
name = "multidict"
optional = false
python-versions = ">=3.5"
version = "4.7.5"
[[package]] [[package]]
category = "main" category = "main"
description = "Python bindings for the MusicBrainz NGS and the Cover Art Archive webservices" 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.*" python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
version = "0.7.0" 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]] [[package]]
category = "dev" category = "dev"
description = "plugin and hook calling mechanisms for python" description = "plugin and hook calling mechanisms for python"
@@ -306,7 +359,7 @@ toml = "*"
dev = ["isort", "flake8", "pytest", "mypy"] dev = ["isort", "flake8", "pytest", "mypy"]
[[package]] [[package]]
category = "dev" category = "main"
description = "Python parsing module" description = "Python parsing module"
name = "pyparsing" name = "pyparsing"
optional = false optional = false
@@ -339,6 +392,17 @@ version = ">=0.12"
checkqa-mypy = ["mypy (v0.761)"] checkqa-mypy = ["mypy (v0.761)"]
testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] 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]] [[package]]
category = "dev" category = "dev"
description = "JSON RPC 2.0 server library" description = "JSON RPC 2.0 server library"
@@ -418,7 +482,7 @@ version = "0.16.0"
dev = ["pytest"] dev = ["pytest"]
[[package]] [[package]]
category = "dev" category = "main"
description = "Python 2 and 3 compatibility utilities" description = "Python 2 and 3 compatibility utilities"
name = "six" name = "six"
optional = false optional = false
@@ -487,6 +551,19 @@ optional = false
python-versions = "*" python-versions = "*"
version = "0.1.8" 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]] [[package]]
category = "dev" category = "dev"
description = "A formatter for Python code." 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"] testing = ["jaraco.itertools", "func-timeout"]
[metadata] [metadata]
content-hash = "b3e363ce109826fbe16ba73b6102a62254689ead837a371aecd2857fa5ed9f45" content-hash = "6755748d710bead6ddf5f543c09fb33a09b340dcff15466f7e6759ce9c659004"
python-versions = "^3.7" python-versions = "^3.7"
[metadata.files] [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-py2.py3-none-any.whl", hash = "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff"},
{file = "colorama-0.4.3.tar.gz", hash = "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1"}, {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 = [ idna = [
{file = "idna-2.9-py2.py3-none-any.whl", hash = "sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa"}, {file = "idna-2.9-py2.py3-none-any.whl", hash = "sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa"},
{file = "idna-2.9.tar.gz", hash = "sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb"}, {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-py2.py3-none-any.whl", hash = "sha256:1349c1e8c107095a55386628bb3b2a79422f3a2cab8381e34ce19909e0cf5064"},
{file = "jedi-0.15.2.tar.gz", hash = "sha256:e909527104a903606dd63bea6e8e888833f0ef087057829b89a18364a856f807"}, {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 = [ mccabe = [
{file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"},
{file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, {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.tar.gz", hash = "sha256:b1ddb932186d8a6ac451e1d95844b382f55e12686d51ca0c68b6f61f2ab7a507"},
{file = "more_itertools-8.2.0-py3-none-any.whl", hash = "sha256:5dd8bcf33e5f9513ffa06d5ad33d78f31e1931ac9a18f33d37e77a180d393a7c"}, {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 = [ musicbrainzngs = [
{file = "musicbrainzngs-0.7.1-py2.py3-none-any.whl", hash = "sha256:e841a8f975104c0a72290b09f59326050194081a5ae62ee512f41915090e1a10"}, {file = "musicbrainzngs-0.7.1-py2.py3-none-any.whl", hash = "sha256:e841a8f975104c0a72290b09f59326050194081a5ae62ee512f41915090e1a10"},
{file = "musicbrainzngs-0.7.1.tar.gz", hash = "sha256:ab1c0100fd0b305852e65f2ed4113c6de12e68afd55186987b8ed97e0f98e627"}, {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-py2.py3-none-any.whl", hash = "sha256:163b0632d4e31cef212976cf57b43d9fd6b0bac6e67c26015d611a647d5e7424"},
{file = "pathspec-0.7.0.tar.gz", hash = "sha256:562aa70af2e0d434367d9790ad37aed893de47f1693e4201fd1d3dca15d19b96"}, {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 = [ pluggy = [
{file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"},
{file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, {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-py3-none-any.whl", hash = "sha256:ff615c761e25eb25df19edddc0b970302d2a9091fbce0e7213298d85fb61fef6"},
{file = "pytest-5.3.5.tar.gz", hash = "sha256:0d5fe9189a148acc3c3eb2ac8e1ac0742cb7618c084f3d228baaec0c254b318d"}, {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 = [ python-jsonrpc-server = [
{file = "python-jsonrpc-server-0.3.4.tar.gz", hash = "sha256:c73bf5495c9dd4d2f902755bedeb6da5afe778e0beee82f0e195c4655352fe37"}, {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"}, {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-py2.py3-none-any.whl", hash = "sha256:8fd29383f539be45b20bd4df0dc29c20ba48654a41e661925e612311e9f3c603"},
{file = "wcwidth-0.1.8.tar.gz", hash = "sha256:f28b3e8a6483e5d49e7f8949ac1a78314e740333ae305b4ba5defd3e74fb37a8"}, {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 = [ yapf = [
{file = "yapf-0.29.0-py2.py3-none-any.whl", hash = "sha256:cad8a272c6001b3401de3278238fdc54997b6c2e56baa751788915f879a52fca"}, {file = "yapf-0.29.0-py2.py3-none-any.whl", hash = "sha256:cad8a272c6001b3401de3278238fdc54997b6c2e56baa751788915f879a52fca"},
{file = "yapf-0.29.0.tar.gz", hash = "sha256:712e23c468506bf12cadd10169f852572ecc61b266258422d45aaf4ad7ef43de"}, {file = "yapf-0.29.0.tar.gz", hash = "sha256:712e23c468506bf12cadd10169f852572ecc61b266258422d45aaf4ad7ef43de"},

View File

@@ -12,6 +12,8 @@ addict = "^2.2.1"
progress = "^1.5" progress = "^1.5"
numpy = "^1.18.1" numpy = "^1.18.1"
beautifultable = "^0.8.0" beautifultable = "^0.8.0"
wordcloud = "^1.6.0"
multidict = "^4.7.5"
[tool.poetry.dev-dependencies] [tool.poetry.dev-dependencies]
pytest = "^5.2" pytest = "^5.2"

BIN
src/.DS_Store vendored Normal file

Binary file not shown.

BIN
src/musicbrainzapi/.DS_Store vendored Normal file

Binary file not shown.

BIN
src/musicbrainzapi/api/.DS_Store vendored Normal file

Binary file not shown.

View File

@@ -1 +0,0 @@
from . import lyrics

View File

@@ -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()

View 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

View File

@@ -1,107 +1,49 @@
from __future__ import annotations 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 from collections import Counter
import html import html
import json import json
import math
import os import os
import string import string
import math from typing import Union, Dict
from beautifultable import BeautifulTable
import musicbrainzngs
import click
import addict import addict
import requests import click
import musicbrainzngs
import numpy as np 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 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): 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 @property
def product(self) -> Lyrics: def product(self) -> Lyrics:
@@ -152,25 +94,44 @@ class LyricsBuilder(LyricsConcreteBuilder):
self.reset() self.reset()
def reset(self) -> None: def reset(self) -> None:
"""Reset the builder and create new product.
"""
self._product = Lyrics() self._product = Lyrics()
def find_artists(self) -> None: def find_artists(self) -> None:
"""Find artists from the musicbrainz api
Returns
-------
None
"""
self.musicbrainz_artists = musicbrainzngs.search_artists( self.musicbrainz_artists = musicbrainzngs.search_artists(
artist=self.artist, country=self.country artist=self.artist, country=self.country
) )
# pprint(self.musicbrainz_artists['artist-list'])
return self return self
def sort_artists(self) -> None: def sort_artists(self) -> None:
"""Sort the artists from the Musicbrainzapi
Returns
-------
None
"""
self._sort_names = dict( 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 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'] for i in self.musicbrainz_artists['artist-list']
) )
return self return self
def get_accuracy_scores(self) -> None: def get_accuracy_scores(self) -> None:
"""Get accuracy scores from the Musicbrainzapi
Returns
-------
None
"""
self._accuracy_scores = dict( self._accuracy_scores = dict(
(i.get('id'), int(i.get('ext:score', '0'))) (i.get('id'), int(i.get('ext:score', '0')))
for i in self.musicbrainz_artists['artist-list'] for i in self.musicbrainz_artists['artist-list']
@@ -178,6 +139,12 @@ class LyricsBuilder(LyricsConcreteBuilder):
return self return self
def get_top_five_results(self) -> None: def get_top_five_results(self) -> None:
"""Get the top five artists from the Musicbrainzapi
Returns
-------
None
"""
self._top_five_results = dict( self._top_five_results = dict(
(i, self._accuracy_scores.get(i)) (i, self._accuracy_scores.get(i))
for i in sorted( for i in sorted(
@@ -189,6 +156,12 @@ class LyricsBuilder(LyricsConcreteBuilder):
return self return self
def find_all_albums(self) -> None: def find_all_albums(self) -> None:
"""Find all albums for the chosen artist
Returns
-------
None
"""
limit, offset, page = (100, 0, 1) limit, offset, page = (100, 0, 1)
resp_0 = addict.Dict( resp_0 = addict.Dict(
@@ -245,6 +218,12 @@ class LyricsBuilder(LyricsConcreteBuilder):
return self return self
def find_all_tracks(self) -> None: def find_all_tracks(self) -> None:
"""Find all tracks from all albums.
Returns
-------
None
"""
self.all_albums = list() self.all_albums = list()
total_albums = len(self.release_group_ids) total_albums = len(self.release_group_ids)
self.total_track_count = 0 self.total_track_count = 0
@@ -309,6 +288,12 @@ class LyricsBuilder(LyricsConcreteBuilder):
return self return self
def find_lyrics_urls(self) -> None: def find_lyrics_urls(self) -> None:
"""Construct the URL for the lyrics api.
Returns
-------
None
"""
self.all_albums_lyrics_url = list() self.all_albums_lyrics_url = list()
for x in self.all_albums: for x in self.all_albums:
for alb, tracks in x.items(): for alb, tracks in x.items():
@@ -326,8 +311,13 @@ class LyricsBuilder(LyricsConcreteBuilder):
# pprint(self.all_albums_lyrics_url) # pprint(self.all_albums_lyrics_url)
return self return self
# change this for progressbar for i loop
def find_all_lyrics(self) -> None: def find_all_lyrics(self) -> None:
"""Get lyrics for each track from the lyrics api
Returns
-------
None
"""
self.all_albums_lyrics = list() self.all_albums_lyrics = list()
with click.progressbar( with click.progressbar(
@@ -335,22 +325,28 @@ class LyricsBuilder(LyricsConcreteBuilder):
label=f'Finding lyrics for {self.total_track_count}' label=f'Finding lyrics for {self.total_track_count}'
f' tracks for {self.artist}. This may take some time! ☕️', f' tracks for {self.artist}. This may take some time! ☕️',
) as bar: ) as bar:
bar.update(5)
for x in self.all_albums_lyrics_url: for x in self.all_albums_lyrics_url:
for alb, urls in x.items(): for alb, urls in x.items():
bar.update(1) # bar.update(1)
update = len(urls) update = len(urls)
lyrics = addict.Dict( lyrics = addict.Dict(
(alb, [self.request_lyrics_from_url(i) for i in urls]) (alb, [self.request_lyrics_from_url(i) for i in urls])
) )
self.all_albums_lyrics.append(lyrics) 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: with open(f'{os.getcwd()}/all_albums_lyrics.json', 'w') as f:
json.dump(self.all_albums_lyrics, f, indent=2) json.dump(self.all_albums_lyrics, f, indent=2)
return self return self
def count_words_in_lyrics(self) -> None: 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() self.all_albums_lyrics_count = list()
# print(self.total_track_count) # print(self.total_track_count)
with click.progressbar( with click.progressbar(
@@ -376,12 +372,18 @@ class LyricsBuilder(LyricsConcreteBuilder):
click.echo(f'Processed lyrics for {self.total_track_count} tracks.') click.echo(f'Processed lyrics for {self.total_track_count} tracks.')
return self return self
# rename this
def calculate_average_all_albums(self) -> None: def calculate_average_all_albums(self) -> None:
"""Summary
Returns
-------
None
Description
"""
self.all_albums_lyrics_sum = list() self.all_albums_lyrics_sum = list()
# album_lyrics = self.all_albums_lyrics_count album_lyrics = self.all_albums_lyrics_count
with open(f'{os.getcwd()}/lyrics_count.json', 'r') as f: # with open(f'{os.getcwd()}/lyrics_count.json', 'r') as f:
album_lyrics = json.load(f) # album_lyrics = json.load(f)
count = 0 count = 0
for i in album_lyrics: for i in album_lyrics:
count += len(i) count += len(i)
@@ -406,15 +408,17 @@ class LyricsBuilder(LyricsConcreteBuilder):
# print(d) # print(d)
self.all_albums_lyrics_sum.append(d) self.all_albums_lyrics_sum.append(d)
# print(count) # print(count)
with open(f'{os.getcwd()}/lyrics_sum_all_album.json', 'w+') as f: # with open(f'{os.getcwd()}/lyrics_sum_all_album.json', 'w+') as f:
json.dump(self.all_albums_lyrics_sum, f) # json.dump(self.all_albums_lyrics_sum, f)
return self # return self
def calculate_final_average_by_album(self) -> None: def calculate_final_average_by_album(self) -> None:
"""Calculates descriptive statistics by album.
"""
self.album_statistics = addict.Dict() self.album_statistics = addict.Dict()
# album_lyrics = self.all_albums_lyrics_sum album_lyrics = self.all_albums_lyrics_sum
with open(f'{os.getcwd()}/lyrics_sum_all_album.json', 'r') as f: # with open(f'{os.getcwd()}/lyrics_sum_all_album.json', 'r') as f:
album_lyrics = json.load(f) # album_lyrics = json.load(f)
for i in album_lyrics: for i in album_lyrics:
for album, count in i.items(): for album, count in i.items():
@@ -426,17 +430,18 @@ class LyricsBuilder(LyricsConcreteBuilder):
self.album_statistics = addict.Dict( self.album_statistics = addict.Dict(
**self.album_statistics, **addict.Dict((album, _d)) **self.album_statistics, **addict.Dict((album, _d))
) )
with open(f'{os.getcwd()}/album_statistics.json', 'w') as f: # with open(f'{os.getcwd()}/album_statistics.json', 'w') as f:
json.dump(self.album_statistics, f, indent=2) # json.dump(self.album_statistics, f, indent=2)
# pprint(self.album_statistics) # pprint(self.album_statistics)
# implement above in this
def calculate_final_average_by_year(self) -> None: def calculate_final_average_by_year(self) -> None:
"""Calculates descriptive statistic by year.
"""
group_by_years = addict.Dict() group_by_years = addict.Dict()
self.year_statistics = addict.Dict() self.year_statistics = addict.Dict()
# album_lyrics = self.all_albums_lyrics_sum album_lyrics = self.all_albums_lyrics_sum
with open(f'{os.getcwd()}/lyrics_sum_all_album.json', 'r') as f: # with open(f'{os.getcwd()}/lyrics_sum_all_album.json', 'r') as f:
album_lyrics = json.load(f) # album_lyrics = json.load(f)
# Merge years together # Merge years together
for i in album_lyrics: for i in album_lyrics:
@@ -464,12 +469,38 @@ class LyricsBuilder(LyricsConcreteBuilder):
@staticmethod @staticmethod
def construct_lyrics_url(artist: str, song: str) -> str: 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_base = 'https://api.lyrics.ovh/v1'
lyrics_api_url = html.escape(f'{lyrics_api_base}/{artist}/{song}') lyrics_api_url = html.escape(f'{lyrics_api_base}/{artist}/{song}')
return lyrics_api_url return lyrics_api_url
@staticmethod @staticmethod
def request_lyrics_from_url(url: str) -> str: 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) resp = requests.get(url)
# No lyrics for a song will return a key of 'error', we pass on this. # No lyrics for a song will return a key of 'error', we pass on this.
@@ -481,11 +512,35 @@ class LyricsBuilder(LyricsConcreteBuilder):
@staticmethod @staticmethod
def strip_punctuation(word: str) -> str: 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)) _strip = word.translate(str.maketrans('', '', string.punctuation))
return _strip return _strip
@staticmethod @staticmethod
def get_descriptive_statistics(nums: list) -> Dict[str, int]: 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: if len(nums) == 0:
return return
avg = math.ceil(np.mean(nums)) avg = math.ceil(np.mean(nums))
@@ -511,232 +566,3 @@ class LyricsBuilder(LyricsConcreteBuilder):
('count', count), ('count', count),
) )
return _d 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

View 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

View 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

View File

@@ -1,5 +1,4 @@
import os import os
import sys
from importlib import import_module from importlib import import_module
import click import click
@@ -31,17 +30,16 @@ class ComplexCLI(click.MultiCommand):
rv.sort() rv.sort()
return rv 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): def get_command(self, ctx, name):
try: mod = import_module(f'musicbrainzapi.cli.commands.cmd_{name}')
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 return mod.cli
@@ -50,25 +48,21 @@ class ComplexCLI(click.MultiCommand):
'-p', '-p',
'--path', '--path',
type=click.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.', 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( @click.version_option(
version=__version__, version=__version__,
prog_name=__header__, prog_name=__header__,
message=f'{__header__} version {__version__} 🎤', message=f'{__header__} version {__version__} 🎤',
) )
@pass_environment @pass_environment
def cli(ctx, verbose, path): def cli(ctx, path):
"""A complex command line interface.""" """A complex command line interface."""
ctx.verbose = verbose # ctx.verbose = verbose
if path is not None: if path is not None:
click.echo(f'Path set to {os.path.expanduser(path)}') click.echo(f'Path set to {os.path.expanduser(path)}')
ctx.path = os.path.expanduser(path) ctx.path = os.path.expanduser(path)
if __name__ == '__main__':
cli()

View File

@@ -1,23 +1,32 @@
import json
from typing import Union from typing import Union
import click import click
import matplotlib.pyplot as plt
from musicbrainzapi.cli.cli import pass_environment 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.option('--dev', is_flag=True, help='Development flag. Do not use.')
# @click.command(short_help='a test command')
@click.option('--dev', is_flag=True)
@click.option( @click.option(
'--save-output', '--save-output',
required=False, required=False,
help='Save the output to json files locally. Will use the path parameter if' help='Save the output to json files locally. Will use the path parameter'
' provided else defaults to current working directory.', ' if provided else defaults to current working directory.',
is_flag=True, 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( @click.option(
'--show-summary', '--show-summary',
@@ -51,12 +60,30 @@ def cli(
country: Union[str, None], country: Union[str, None],
dev: bool, dev: bool,
show_summary: str, show_summary: str,
save_output: bool wordcloud: bool,
save_output: bool,
) -> None: ) -> None:
"""Search for lyrics statistics of an Artist/Group.""" """Search for lyrics statistics of an Artist/Group.
# lyrics_obj = list()
director = lyrics.LyricsClickDirector() Parameters
builder = lyrics.LyricsBuilder() ----------
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 director.builder = builder
if dev: if dev:
director._dev() director._dev()
@@ -70,16 +97,44 @@ def cli(
director._calculate_basic_statistics() director._calculate_basic_statistics()
if show_summary is not None: if show_summary is not None:
director._calculate_descriptive_statistics() director._calculate_descriptive_statistics()
# Get the Lyrics object # Get the Lyrics object
lyrics_0 = director.builder.product lyrics_0 = director.builder.product
# lyrics_obj.append(lyrics_0) # lyrics_obj.append(lyrics_0)
# Show basic count # Show basic count
lyrics_0.show_summary() lyrics_0.show_summary()
# Show summary statistics # Show summary statistics
if show_summary == 'all': if show_summary == 'all':
lyrics_0.show_summary_statistics(group_by='album') lyrics_0.show_summary_statistics(group_by='album')
lyrics_0.show_summary_statistics(group_by='year') lyrics_0.show_summary_statistics(group_by='year')
elif show_summary in ['album', 'year']: elif show_summary in ['album', 'year']:
lyrics_0.show_summary_statistics(group_by=show_summary) 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

Binary file not shown.

View 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

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 KiB