Merge branch '218-python3.8-flatpak' into master
25
.builds/build-flatpak.yml
Normal file
@@ -0,0 +1,25 @@
|
||||
image: archlinux
|
||||
packages:
|
||||
- dbus
|
||||
- flatpak
|
||||
- flatpak-builder
|
||||
- gobject-introspection
|
||||
- gtk3
|
||||
- mpv
|
||||
- python-cairo
|
||||
- python-gobject
|
||||
- python-pip
|
||||
- python-poetry
|
||||
- xorg-server-xvfb
|
||||
sources:
|
||||
- https://git.sr.ht/~sumner/sublime-music
|
||||
environment:
|
||||
REPO_NAME: sublime-music
|
||||
# triggers:
|
||||
# - action: email
|
||||
# condition: failure
|
||||
# to: ~sumner/sublime-music-devel@lists.sr.ht
|
||||
tasks:
|
||||
- build-flatpak: |
|
||||
cd ${REPO_NAME}/flatpak
|
||||
sudo REPO=repo ./flatpak_build.sh
|
48
.builds/build.yml
Normal file
@@ -0,0 +1,48 @@
|
||||
image: archlinux
|
||||
packages:
|
||||
- dbus
|
||||
- gobject-introspection
|
||||
- gtk3
|
||||
- mpv
|
||||
- python-cairo
|
||||
- python-gobject
|
||||
- python-poetry
|
||||
- xorg-server-xvfb
|
||||
sources:
|
||||
- https://git.sr.ht/~sumner/sublime-music
|
||||
environment:
|
||||
REPO_NAME: sublime-music
|
||||
triggers:
|
||||
- action: email
|
||||
condition: failure
|
||||
to: ~sumner/sublime-music-devel@lists.sr.ht
|
||||
tasks:
|
||||
- setup: |
|
||||
cd ${REPO_NAME}
|
||||
poetry install
|
||||
echo "cd ${REPO_NAME}" >> ~/.buildenv
|
||||
echo "source $(poetry env info -p)/bin/activate" >> ~/.buildenv
|
||||
|
||||
- lint: |
|
||||
python setup.py check -mrs
|
||||
black --check .
|
||||
flake8
|
||||
mypy sublime_music tests/**/*.py
|
||||
cicd/custom_style_check.py
|
||||
|
||||
- test: |
|
||||
Xvfb :119 -screen 0 1024x768x16 &
|
||||
export DISPLAY=:119
|
||||
pytest
|
||||
|
||||
- build: |
|
||||
python setup.py sdist
|
||||
|
||||
- deploy-pypi: |
|
||||
./cicd/run_if_tagged_with_version \
|
||||
"twine upload -r testpypi dist/*" \
|
||||
"twine upload dist/*"
|
||||
|
||||
- verify-pypi: |
|
||||
./cicd/run_if_tagged_with_version \
|
||||
"pip install ${REPO_NAME}"
|
33
.builds/readme.yml
Normal file
@@ -0,0 +1,33 @@
|
||||
image: alpine/edge
|
||||
packages:
|
||||
- curl
|
||||
- git
|
||||
- openssh
|
||||
- py3-docutils
|
||||
sources:
|
||||
- https://git.sr.ht/~sumner/sublime-music
|
||||
secrets:
|
||||
# README Personal Access Token
|
||||
- 2fb5fd72-fa96-46c6-ab90-6b7cabebba16
|
||||
environment:
|
||||
REPO_NAME: sublime-music
|
||||
triggers:
|
||||
- action: email
|
||||
condition: failure
|
||||
to: ~sumner/sublime-music-devel@lists.sr.ht
|
||||
tasks:
|
||||
- setup: |
|
||||
echo "cd ${REPO_NAME}" >> ~/.buildenv
|
||||
|
||||
# If we are on the master branch, compile the README.rst to HTML and set it
|
||||
# as the README for the repo.
|
||||
- readme: |
|
||||
set +x
|
||||
git branch --contains | grep master &&
|
||||
rst2html5 --no-doc-title README.rst | \
|
||||
curl -H "Content-Type: text/html" \
|
||||
-H "Authorization: Bearer $(cat ~/.readme-token)" \
|
||||
-XPUT \
|
||||
--data-binary @- \
|
||||
"https://git.sr.ht/api/repos/${REPO_NAME}/readme" &&
|
||||
echo "README set" || echo "Skipping README set because not on master"
|
7
.envrc
Normal file
@@ -0,0 +1,7 @@
|
||||
# Run poetry install and activate the virtualenv
|
||||
poetry install
|
||||
source .venv/bin/activate
|
||||
|
||||
watch_file pyproject.toml
|
||||
watch_file poetry.lock
|
||||
watch_file setup.py
|
1
.gitignore
vendored
@@ -1,5 +1,6 @@
|
||||
flatpak/flatpak_build_dir/
|
||||
flatpak/sublime-music.flatpak
|
||||
flatpak/repo
|
||||
|
||||
# Created by https://www.gitignore.io/api/python
|
||||
# Edit at https://www.gitignore.io/?templates=python
|
||||
|
@@ -22,11 +22,11 @@ lint:
|
||||
before_script:
|
||||
- ./cicd/install-project-deps.sh
|
||||
script:
|
||||
- pipenv run python setup.py check -mrs
|
||||
- pipenv run black --check .
|
||||
- pipenv run flake8
|
||||
- pipenv run mypy sublime tests/**/*.py
|
||||
- pipenv run cicd/custom_style_check.py
|
||||
- poetry run python setup.py check -mrs
|
||||
- poetry run black --check .
|
||||
- poetry run flake8
|
||||
- poetry run mypy sublime_music tests/**/*.py
|
||||
- poetry run cicd/custom_style_check.py
|
||||
|
||||
test:
|
||||
stage: test
|
||||
@@ -37,7 +37,7 @@ test:
|
||||
- Xvfb :119 -screen 0 1024x768x16 &
|
||||
- export DISPLAY=:119
|
||||
script:
|
||||
- pipenv run ./cicd/pytest.sh
|
||||
- poetry run ./cicd/pytest.sh
|
||||
artifacts:
|
||||
paths:
|
||||
- htmlcov
|
||||
@@ -47,7 +47,7 @@ build:
|
||||
before_script:
|
||||
- ./cicd/install-project-deps.sh
|
||||
script:
|
||||
- pipenv run python setup.py sdist
|
||||
- poetry run python setup.py sdist
|
||||
artifacts:
|
||||
paths:
|
||||
- dist/*
|
||||
@@ -55,7 +55,6 @@ build:
|
||||
|
||||
build_flatpak:
|
||||
image: registry.gitlab.com/sublime-music/sublime-music/flatpak-build:latest
|
||||
allow_failure: true
|
||||
stage: build
|
||||
script:
|
||||
- cd flatpak
|
||||
@@ -116,7 +115,7 @@ publish_release:
|
||||
before_script:
|
||||
- ./cicd/install-project-deps.sh
|
||||
script:
|
||||
- pipenv run ./cicd/publish-gitlab-release.sh
|
||||
- poetry run ./cicd/publish-gitlab-release.sh
|
||||
|
||||
# Scheduled Jobs
|
||||
# =============================================================================
|
||||
|
@@ -1,10 +1,23 @@
|
||||
v0.11.9
|
||||
=======
|
||||
|
||||
**The wait is over!** Thanks to help from jlanda_, the **Flatpak** is back! The
|
||||
Flatpak requires ``org.gnome.SDK//3.38`` and ``org.gnome.Platform//3.38``.
|
||||
|
||||
.. _jlanda: https://gitlab.com/jlanda
|
||||
|
||||
**Bug Fixes**
|
||||
|
||||
* Fixed regressions with Chromecast playback.
|
||||
|
||||
**Infrastructure**
|
||||
|
||||
* Switched from Pipenv to Poetry because Poetry is so much faster.
|
||||
* Added a ``.envrc`` file for direnv users.
|
||||
* Started migrating from GitLab to sr.ht due to usability regressions in GitLab.
|
||||
* **Package name change:** The package name is now ``sublime_music`` instead of
|
||||
``sublime``.
|
||||
|
||||
v0.11.8
|
||||
=======
|
||||
|
||||
|
@@ -83,8 +83,8 @@ Building the flatpak
|
||||
--------------------
|
||||
|
||||
- A flatpak-builder environment must be setup on the build machine to do a
|
||||
flatpak build. This includes ``org.gnome.SDK//3.34`` and
|
||||
``org.gnome.Platform//3.34``.
|
||||
flatpak build. This includes ``org.gnome.SDK//3.36`` and
|
||||
``org.gnome.Platform//3.36``.
|
||||
- The ``flatpak`` folder contains the required files to build a flatpak package.
|
||||
- The script ``flatpak_build.sh`` will run the required commands to grab the
|
||||
remaining dependencies and build the flatpak.
|
||||
@@ -117,6 +117,7 @@ before knowing if your code is the correct style.
|
||||
* ``flake8-importorder`` (with the ``edited`` import style): enforce ordering
|
||||
of import statements.
|
||||
* ``flake8-pep3101``: no ``%`` string formatting.
|
||||
* ``flake8-print``: to prevent using the ``print`` function.
|
||||
|
||||
* `mypy`_ is used for type checking. All type errors must be resolved.
|
||||
|
||||
|
35
Pipfile
@@ -1,35 +0,0 @@
|
||||
[[source]]
|
||||
name = "pypi"
|
||||
url = "https://pypi.org/simple"
|
||||
verify_ssl = true
|
||||
|
||||
[dev-packages]
|
||||
black = "*"
|
||||
docutils = "*"
|
||||
flake8 = "*"
|
||||
flake8-annotations = "*"
|
||||
flake8-bugbear = "*"
|
||||
flake8-comprehensions = "*"
|
||||
flake8-import-order = "*"
|
||||
flake8-pep3101 = "*"
|
||||
flake8-print = "*"
|
||||
graphviz = "*"
|
||||
lxml = "*"
|
||||
mypy = "*"
|
||||
pycodestyle = "*"
|
||||
pytest = "*"
|
||||
pytest-cov = "*"
|
||||
rope = "*"
|
||||
rst2html5 = "*"
|
||||
sphinx = "*"
|
||||
sphinx-rtd-theme = "*"
|
||||
termcolor = "*"
|
||||
|
||||
[packages]
|
||||
sublime-music = {editable = true, extras = ["chromecast", "keyring", "server"], path = "."}
|
||||
|
||||
[requires]
|
||||
python_version = "3.8"
|
||||
|
||||
[pipenv]
|
||||
allow_prereleases = true
|
980
Pipfile.lock
generated
@@ -1,980 +0,0 @@
|
||||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "f018bc2d21d6dc296af872daca484440360496dc7fd3746880da2fe2996ed0ce"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
"python_version": "3.8"
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"name": "pypi",
|
||||
"url": "https://pypi.org/simple",
|
||||
"verify_ssl": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"default": {
|
||||
"bleach": {
|
||||
"hashes": [
|
||||
"sha256:52b5919b81842b1854196eaae5ca29679a2f2e378905c346d3ca8227c2c66080",
|
||||
"sha256:9f8ccbeb6183c6e6cddea37592dfb0167485c1e3b13b3363bc325aa8bda3adbd"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
|
||||
"version": "==3.2.1"
|
||||
},
|
||||
"bottle": {
|
||||
"hashes": [
|
||||
"sha256:0819b74b145a7def225c0e83b16a4d5711fde751cd92bae467a69efce720f69e",
|
||||
"sha256:43157254e88f32c6be16f8d9eb1f1d1472396a4e174ebd2bf62544854ecf37e7"
|
||||
],
|
||||
"version": "==0.12.18"
|
||||
},
|
||||
"casttube": {
|
||||
"hashes": [
|
||||
"sha256:36f118007f9eead3959cf30de03c1640b53a263569ff2a3971c0521826c835b2",
|
||||
"sha256:54d2af8c7949aa9c5db87fb11ef0a478a5d3e7ac6d2d2ac8dd1711e3a516fc82"
|
||||
],
|
||||
"version": "==0.2.1"
|
||||
},
|
||||
"certifi": {
|
||||
"hashes": [
|
||||
"sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3",
|
||||
"sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41"
|
||||
],
|
||||
"version": "==2020.6.20"
|
||||
},
|
||||
"cffi": {
|
||||
"hashes": [
|
||||
"sha256:0da50dcbccd7cb7e6c741ab7912b2eff48e85af217d72b57f80ebc616257125e",
|
||||
"sha256:12a453e03124069b6896107ee133ae3ab04c624bb10683e1ed1c1663df17c13c",
|
||||
"sha256:15419020b0e812b40d96ec9d369b2bc8109cc3295eac6e013d3261343580cc7e",
|
||||
"sha256:15a5f59a4808f82d8ec7364cbace851df591c2d43bc76bcbe5c4543a7ddd1bf1",
|
||||
"sha256:23e44937d7695c27c66a54d793dd4b45889a81b35c0751ba91040fe825ec59c4",
|
||||
"sha256:29c4688ace466a365b85a51dcc5e3c853c1d283f293dfcc12f7a77e498f160d2",
|
||||
"sha256:57214fa5430399dffd54f4be37b56fe22cedb2b98862550d43cc085fb698dc2c",
|
||||
"sha256:577791f948d34d569acb2d1add5831731c59d5a0c50a6d9f629ae1cefd9ca4a0",
|
||||
"sha256:6539314d84c4d36f28d73adc1b45e9f4ee2a89cdc7e5d2b0a6dbacba31906798",
|
||||
"sha256:65867d63f0fd1b500fa343d7798fa64e9e681b594e0a07dc934c13e76ee28fb1",
|
||||
"sha256:672b539db20fef6b03d6f7a14b5825d57c98e4026401fce838849f8de73fe4d4",
|
||||
"sha256:6843db0343e12e3f52cc58430ad559d850a53684f5b352540ca3f1bc56df0731",
|
||||
"sha256:7057613efefd36cacabbdbcef010e0a9c20a88fc07eb3e616019ea1692fa5df4",
|
||||
"sha256:76ada88d62eb24de7051c5157a1a78fd853cca9b91c0713c2e973e4196271d0c",
|
||||
"sha256:837398c2ec00228679513802e3744d1e8e3cb1204aa6ad408b6aff081e99a487",
|
||||
"sha256:8662aabfeab00cea149a3d1c2999b0731e70c6b5bac596d95d13f643e76d3d4e",
|
||||
"sha256:95e9094162fa712f18b4f60896e34b621df99147c2cee216cfa8f022294e8e9f",
|
||||
"sha256:99cc66b33c418cd579c0f03b77b94263c305c389cb0c6972dac420f24b3bf123",
|
||||
"sha256:9b219511d8b64d3fa14261963933be34028ea0e57455baf6781fe399c2c3206c",
|
||||
"sha256:ae8f34d50af2c2154035984b8b5fc5d9ed63f32fe615646ab435b05b132ca91b",
|
||||
"sha256:b9aa9d8818c2e917fa2c105ad538e222a5bce59777133840b93134022a7ce650",
|
||||
"sha256:bf44a9a0141a082e89c90e8d785b212a872db793a0080c20f6ae6e2a0ebf82ad",
|
||||
"sha256:c0b48b98d79cf795b0916c57bebbc6d16bb43b9fc9b8c9f57f4cf05881904c75",
|
||||
"sha256:da9d3c506f43e220336433dffe643fbfa40096d408cb9b7f2477892f369d5f82",
|
||||
"sha256:e4082d832e36e7f9b2278bc774886ca8207346b99f278e54c9de4834f17232f7",
|
||||
"sha256:e4b9b7af398c32e408c00eb4e0d33ced2f9121fd9fb978e6c1b57edd014a7d15",
|
||||
"sha256:e613514a82539fc48291d01933951a13ae93b6b444a88782480be32245ed4afa",
|
||||
"sha256:f5033952def24172e60493b68717792e3aebb387a8d186c43c020d9363ee7281"
|
||||
],
|
||||
"version": "==1.14.2"
|
||||
},
|
||||
"chardet": {
|
||||
"hashes": [
|
||||
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
|
||||
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
|
||||
],
|
||||
"version": "==3.0.4"
|
||||
},
|
||||
"cryptography": {
|
||||
"hashes": [
|
||||
"sha256:10c9775a3f31610cf6b694d1fe598f2183441de81cedcf1814451ae53d71b13a",
|
||||
"sha256:180c9f855a8ea280e72a5d61cf05681b230c2dce804c48e9b2983f491ecc44ed",
|
||||
"sha256:247df238bc05c7d2e934a761243bfdc67db03f339948b1e2e80c75d41fc7cc36",
|
||||
"sha256:26409a473cc6278e4c90f782cd5968ebad04d3911ed1c402fc86908c17633e08",
|
||||
"sha256:2a27615c965173c4c88f2961cf18115c08fedfb8bdc121347f26e8458dc6d237",
|
||||
"sha256:2e26223ac636ca216e855748e7d435a1bf846809ed12ed898179587d0cf74618",
|
||||
"sha256:321761d55fb7cb256b771ee4ed78e69486a7336be9143b90c52be59d7657f50f",
|
||||
"sha256:4005b38cd86fc51c955db40b0f0e52ff65340874495af72efabb1bb8ca881695",
|
||||
"sha256:4b9e96543d0784acebb70991ebc2dbd99aa287f6217546bb993df22dd361d41c",
|
||||
"sha256:548b0818e88792318dc137d8b1ec82a0ab0af96c7f0603a00bb94f896fbf5e10",
|
||||
"sha256:725875681afe50b41aee7fdd629cedbc4720bab350142b12c55c0a4d17c7416c",
|
||||
"sha256:7a63e97355f3cd77c94bd98c59cb85fe0efd76ea7ef904c9b0316b5bbfde6ed1",
|
||||
"sha256:94191501e4b4009642be21dde2a78bd3c2701a81ee57d3d3d02f1d99f8b64a9e",
|
||||
"sha256:969ae512a250f869c1738ca63be843488ff5cc031987d302c1f59c7dbe1b225f",
|
||||
"sha256:9f734423eb9c2ea85000aa2476e0d7a58e021bc34f0a373ac52a5454cd52f791",
|
||||
"sha256:b45ab1c6ece7c471f01c56f5d19818ca797c34541f0b2351635a5c9fe09ac2e0",
|
||||
"sha256:cc6096c86ec0de26e2263c228fb25ee01c3ff1346d3cfc219d67d49f303585af",
|
||||
"sha256:dc3f437ca6353979aace181f1b790f0fc79e446235b14306241633ab7d61b8f8",
|
||||
"sha256:e7563eb7bc5c7e75a213281715155248cceba88b11cb4b22957ad45b85903761",
|
||||
"sha256:e7dad66a9e5684a40f270bd4aee1906878193ae50a4831922e454a2a457f1716",
|
||||
"sha256:eb80a288e3cfc08f679f95da72d2ef90cb74f6d8a8ba69d2f215c5e110b2ca32",
|
||||
"sha256:fa7fbcc40e2210aca26c7ac8a39467eae444d90a2c346cbcffd9133a166bcc67"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
|
||||
"version": "==3.1"
|
||||
},
|
||||
"dataclasses-json": {
|
||||
"hashes": [
|
||||
"sha256:56ec931959ede74b5dedf65cf20772e6a79764d20c404794cce0111c88c085ff",
|
||||
"sha256:b746c48d9d8e884e2a0ffa59c6220a1b21f94d4f9f12c839da0a8a0efd36dc19"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==0.5.2"
|
||||
},
|
||||
"deepdiff": {
|
||||
"hashes": [
|
||||
"sha256:273b18d32bb9b956548290b2e3ddf79c515def2dd5738965f4348ae813e710c5",
|
||||
"sha256:e2b74af4da0ef9cd338bb6e8c97242c1ec9d81fcb28298d7bb24acdc19ea79d7"
|
||||
],
|
||||
"markers": "python_version >= '3.5'",
|
||||
"version": "==5.0.2"
|
||||
},
|
||||
"fuzzywuzzy": {
|
||||
"hashes": [
|
||||
"sha256:45016e92264780e58972dca1b3d939ac864b78437422beecebb3095f8efd00e8",
|
||||
"sha256:928244b28db720d1e0ee7587acf660ea49d7e4c632569cad4f1cd7e68a5f0993"
|
||||
],
|
||||
"version": "==0.18.0"
|
||||
},
|
||||
"idna": {
|
||||
"hashes": [
|
||||
"sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6",
|
||||
"sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==2.10"
|
||||
},
|
||||
"ifaddr": {
|
||||
"hashes": [
|
||||
"sha256:1f9e8a6ca6f16db5a37d3356f07b6e52344f6f9f7e806d618537731669eb1a94",
|
||||
"sha256:d1f603952f0a71c9ab4e705754511e4e03b02565bc4cec7188ad6415ff534cd3"
|
||||
],
|
||||
"version": "==0.1.7"
|
||||
},
|
||||
"jeepney": {
|
||||
"hashes": [
|
||||
"sha256:3479b861cc2b6407de5188695fa1a8d57e5072d7059322469b62628869b8e36e",
|
||||
"sha256:d6c6b49683446d2407d2fe3acb7a368a77ff063f9182fe427da15d622adc24cf"
|
||||
],
|
||||
"markers": "sys_platform == 'linux'",
|
||||
"version": "==0.4.3"
|
||||
},
|
||||
"keyring": {
|
||||
"hashes": [
|
||||
"sha256:4e34ea2fdec90c1c43d6610b5a5fafa1b9097db1802948e90caf5763974b8f8d",
|
||||
"sha256:9aeadd006a852b78f4b4ef7c7556c2774d2432bbef8ee538a3e9089ac8b11466"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==21.4.0"
|
||||
},
|
||||
"marshmallow": {
|
||||
"hashes": [
|
||||
"sha256:2272273505f1644580fbc66c6b220cc78f893eb31f1ecde2af98ad28011e9811",
|
||||
"sha256:47911dd7c641a27160f0df5fd0fe94667160ffe97f70a42c3cc18388d86098cc"
|
||||
],
|
||||
"markers": "python_version >= '3.5'",
|
||||
"version": "==3.8.0"
|
||||
},
|
||||
"marshmallow-enum": {
|
||||
"hashes": [
|
||||
"sha256:38e697e11f45a8e64b4a1e664000897c659b60aa57bfa18d44e226a9920b6e58",
|
||||
"sha256:57161ab3dbfde4f57adeb12090f39592e992b9c86d206d02f6bd03ebec60f072"
|
||||
],
|
||||
"version": "==1.5.1"
|
||||
},
|
||||
"mypy-extensions": {
|
||||
"hashes": [
|
||||
"sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d",
|
||||
"sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"
|
||||
],
|
||||
"version": "==0.4.3"
|
||||
},
|
||||
"ordered-set": {
|
||||
"hashes": [
|
||||
"sha256:ba93b2df055bca202116ec44b9bead3df33ea63a7d5827ff8e16738b97f33a95"
|
||||
],
|
||||
"markers": "python_version >= '3.5'",
|
||||
"version": "==4.0.2"
|
||||
},
|
||||
"packaging": {
|
||||
"hashes": [
|
||||
"sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8",
|
||||
"sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==20.4"
|
||||
},
|
||||
"peewee": {
|
||||
"hashes": [
|
||||
"sha256:1269a9736865512bd4056298003aab190957afe07d2616cf22eaf56cb6398369"
|
||||
],
|
||||
"version": "==3.13.3"
|
||||
},
|
||||
"protobuf": {
|
||||
"hashes": [
|
||||
"sha256:0bba42f439bf45c0f600c3c5993666fcb88e8441d011fad80a11df6f324eef33",
|
||||
"sha256:1e834076dfef9e585815757a2c7e4560c7ccc5962b9d09f831214c693a91b463",
|
||||
"sha256:339c3a003e3c797bc84499fa32e0aac83c768e67b3de4a5d7a5a9aa3b0da634c",
|
||||
"sha256:361acd76f0ad38c6e38f14d08775514fbd241316cce08deb2ce914c7dfa1184a",
|
||||
"sha256:3dee442884a18c16d023e52e32dd34a8930a889e511af493f6dc7d4d9bf12e4f",
|
||||
"sha256:4d1174c9ed303070ad59553f435846a2f877598f59f9afc1b89757bdf846f2a7",
|
||||
"sha256:5db9d3e12b6ede5e601b8d8684a7f9d90581882925c96acf8495957b4f1b204b",
|
||||
"sha256:6a82e0c8bb2bf58f606040cc5814e07715b2094caeba281e2e7d0b0e2e397db5",
|
||||
"sha256:8c35bcbed1c0d29b127c886790e9d37e845ffc2725cc1db4bd06d70f4e8359f4",
|
||||
"sha256:91c2d897da84c62816e2f473ece60ebfeab024a16c1751aaf31100127ccd93ec",
|
||||
"sha256:9c2e63c1743cba12737169c447374fab3dfeb18111a460a8c1a000e35836b18c",
|
||||
"sha256:9edfdc679a3669988ec55a989ff62449f670dfa7018df6ad7f04e8dbacb10630",
|
||||
"sha256:c0c5ab9c4b1eac0a9b838f1e46038c3175a95b0f2d944385884af72876bd6bc7",
|
||||
"sha256:c8abd7605185836f6f11f97b21200f8a864f9cb078a193fe3c9e235711d3ff1e",
|
||||
"sha256:d69697acac76d9f250ab745b46c725edf3e98ac24763990b24d58c16c642947a",
|
||||
"sha256:df3932e1834a64b46ebc262e951cd82c3cf0fa936a154f0a42231140d8237060",
|
||||
"sha256:e7662437ca1e0c51b93cadb988f9b353fa6b8013c0385d63a70c8a77d84da5f9",
|
||||
"sha256:f68eb9d03c7d84bd01c790948320b768de8559761897763731294e3bc316decb"
|
||||
],
|
||||
"version": "==3.13.0"
|
||||
},
|
||||
"pycairo": {
|
||||
"hashes": [
|
||||
"sha256:2c143183280feb67f5beb4e543fd49990c28e7df427301ede04fc550d3562e84"
|
||||
],
|
||||
"markers": "python_version >= '3.5' and python_version < '4'",
|
||||
"version": "==1.19.1"
|
||||
},
|
||||
"pychromecast": {
|
||||
"hashes": [
|
||||
"sha256:182ee414f7de227f2e55d6444bd59d31282bac4d1d63f0f3ca1f19725c5ba4bf",
|
||||
"sha256:f594231efb34b86eeb463611662bca21a6962793885d3ad68195286940f7473d"
|
||||
],
|
||||
"version": "==7.3.0"
|
||||
},
|
||||
"pycparser": {
|
||||
"hashes": [
|
||||
"sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0",
|
||||
"sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==2.20"
|
||||
},
|
||||
"pygobject": {
|
||||
"hashes": [
|
||||
"sha256:051b950f509f2e9f125add96c1493bde987c527f7a0c15a1f7b69d6d1c3cd8e6"
|
||||
],
|
||||
"markers": "python_version >= '3.5' and python_version < '4'",
|
||||
"version": "==3.38.0"
|
||||
},
|
||||
"pyparsing": {
|
||||
"hashes": [
|
||||
"sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1",
|
||||
"sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"
|
||||
],
|
||||
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==2.4.7"
|
||||
},
|
||||
"python-dateutil": {
|
||||
"hashes": [
|
||||
"sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c",
|
||||
"sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==2.8.1"
|
||||
},
|
||||
"python-levenshtein": {
|
||||
"hashes": [
|
||||
"sha256:033a11de5e3d19ea25c9302d11224e1a1898fe5abd23c61c7c360c25195e3eb1"
|
||||
],
|
||||
"version": "==0.12.0"
|
||||
},
|
||||
"python-mpv": {
|
||||
"hashes": [
|
||||
"sha256:10c7ae61eff441602c7188595108391cdede153c15454772d8811c2bcb9e6823"
|
||||
],
|
||||
"version": "==0.5.2"
|
||||
},
|
||||
"requests": {
|
||||
"hashes": [
|
||||
"sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b",
|
||||
"sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
|
||||
"version": "==2.24.0"
|
||||
},
|
||||
"secretstorage": {
|
||||
"hashes": [
|
||||
"sha256:15da8a989b65498e29be338b3b279965f1b8f09b9668bd8010da183024c8bff6",
|
||||
"sha256:b5ec909dde94d4ae2fa26af7c089036997030f0cf0a5cb372b4cccabd81c143b"
|
||||
],
|
||||
"markers": "sys_platform == 'linux'",
|
||||
"version": "==3.1.2"
|
||||
},
|
||||
"semver": {
|
||||
"hashes": [
|
||||
"sha256:21e80ca738975ed513cba859db0a0d2faca2380aef1962f48272ebf9a8a44bd4",
|
||||
"sha256:c0a4a9d1e45557297a722ee9bac3de2ec2ea79016b6ffcaca609b0bc62cf4276"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==2.10.2"
|
||||
},
|
||||
"six": {
|
||||
"hashes": [
|
||||
"sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259",
|
||||
"sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==1.15.0"
|
||||
},
|
||||
"stringcase": {
|
||||
"hashes": [
|
||||
"sha256:48a06980661908efe8d9d34eab2b6c13aefa2163b3ced26972902e3bdfd87008"
|
||||
],
|
||||
"version": "==1.2.0"
|
||||
},
|
||||
"sublime-music": {
|
||||
"editable": true,
|
||||
"extras": [
|
||||
"chromecast",
|
||||
"keyring",
|
||||
"server"
|
||||
],
|
||||
"path": "."
|
||||
},
|
||||
"typing-extensions": {
|
||||
"hashes": [
|
||||
"sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918",
|
||||
"sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c",
|
||||
"sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"
|
||||
],
|
||||
"version": "==3.7.4.3"
|
||||
},
|
||||
"typing-inspect": {
|
||||
"hashes": [
|
||||
"sha256:3b98390df4d999a28cf5b35d8b333425af5da2ece8a4ea9e98f71e7591347b4f",
|
||||
"sha256:8f1b1dd25908dbfd81d3bebc218011531e7ab614ba6e5bf7826d887c834afab7",
|
||||
"sha256:de08f50a22955ddec353876df7b2545994d6df08a2f45d54ac8c05e530372ca0"
|
||||
],
|
||||
"version": "==0.6.0"
|
||||
},
|
||||
"urllib3": {
|
||||
"editable": true,
|
||||
"extras": [
|
||||
"chromecast",
|
||||
"keyring",
|
||||
"server"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
|
||||
"path": "."
|
||||
},
|
||||
"webencodings": {
|
||||
"hashes": [
|
||||
"sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78",
|
||||
"sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"
|
||||
],
|
||||
"version": "==0.5.1"
|
||||
},
|
||||
"zeroconf": {
|
||||
"hashes": [
|
||||
"sha256:c08dbb90c116626cb6c5f19ebd14cd4846cffe7151f338c19215e6938d334980",
|
||||
"sha256:f69cc7f9cc3b2a85c7f2cdafe2bb7fb00cf01604bc3df392f7ed5e3c303d7705"
|
||||
],
|
||||
"version": "==0.28.5"
|
||||
}
|
||||
},
|
||||
"develop": {
|
||||
"alabaster": {
|
||||
"hashes": [
|
||||
"sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359",
|
||||
"sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"
|
||||
],
|
||||
"version": "==0.7.12"
|
||||
},
|
||||
"appdirs": {
|
||||
"hashes": [
|
||||
"sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41",
|
||||
"sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"
|
||||
],
|
||||
"version": "==1.4.4"
|
||||
},
|
||||
"attrs": {
|
||||
"hashes": [
|
||||
"sha256:26b54ddbbb9ee1d34d5d3668dd37d6cf74990ab23c828c2888dccdceee395594",
|
||||
"sha256:fce7fc47dfc976152e82d53ff92fa0407700c21acd20886a13777a0d20e655dc"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==20.2.0"
|
||||
},
|
||||
"babel": {
|
||||
"hashes": [
|
||||
"sha256:1aac2ae2d0d8ea368fa90906567f5c08463d98ade155c0c4bfedd6a0f7160e38",
|
||||
"sha256:d670ea0b10f8b723672d3a6abeb87b565b244da220d76b4dba1b66269ec152d4"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==2.8.0"
|
||||
},
|
||||
"black": {
|
||||
"hashes": [
|
||||
"sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==20.8b1"
|
||||
},
|
||||
"certifi": {
|
||||
"hashes": [
|
||||
"sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3",
|
||||
"sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41"
|
||||
],
|
||||
"version": "==2020.6.20"
|
||||
},
|
||||
"chardet": {
|
||||
"hashes": [
|
||||
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
|
||||
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
|
||||
],
|
||||
"version": "==3.0.4"
|
||||
},
|
||||
"click": {
|
||||
"hashes": [
|
||||
"sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a",
|
||||
"sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
|
||||
"version": "==7.1.2"
|
||||
},
|
||||
"coverage": {
|
||||
"hashes": [
|
||||
"sha256:0203acd33d2298e19b57451ebb0bed0ab0c602e5cf5a818591b4918b1f97d516",
|
||||
"sha256:0f313707cdecd5cd3e217fc68c78a960b616604b559e9ea60cc16795c4304259",
|
||||
"sha256:1c6703094c81fa55b816f5ae542c6ffc625fec769f22b053adb42ad712d086c9",
|
||||
"sha256:1d44bb3a652fed01f1f2c10d5477956116e9b391320c94d36c6bf13b088a1097",
|
||||
"sha256:280baa8ec489c4f542f8940f9c4c2181f0306a8ee1a54eceba071a449fb870a0",
|
||||
"sha256:29a6272fec10623fcbe158fdf9abc7a5fa032048ac1d8631f14b50fbfc10d17f",
|
||||
"sha256:2b31f46bf7b31e6aa690d4c7a3d51bb262438c6dcb0d528adde446531d0d3bb7",
|
||||
"sha256:2d43af2be93ffbad25dd959899b5b809618a496926146ce98ee0b23683f8c51c",
|
||||
"sha256:381ead10b9b9af5f64646cd27107fb27b614ee7040bb1226f9c07ba96625cbb5",
|
||||
"sha256:47a11bdbd8ada9b7ee628596f9d97fbd3851bd9999d398e9436bd67376dbece7",
|
||||
"sha256:4d6a42744139a7fa5b46a264874a781e8694bb32f1d76d8137b68138686f1729",
|
||||
"sha256:50691e744714856f03a86df3e2bff847c2acede4c191f9a1da38f088df342978",
|
||||
"sha256:530cc8aaf11cc2ac7430f3614b04645662ef20c348dce4167c22d99bec3480e9",
|
||||
"sha256:582ddfbe712025448206a5bc45855d16c2e491c2dd102ee9a2841418ac1c629f",
|
||||
"sha256:63808c30b41f3bbf65e29f7280bf793c79f54fb807057de7e5238ffc7cc4d7b9",
|
||||
"sha256:71b69bd716698fa62cd97137d6f2fdf49f534decb23a2c6fc80813e8b7be6822",
|
||||
"sha256:7858847f2d84bf6e64c7f66498e851c54de8ea06a6f96a32a1d192d846734418",
|
||||
"sha256:78e93cc3571fd928a39c0b26767c986188a4118edc67bc0695bc7a284da22e82",
|
||||
"sha256:7f43286f13d91a34fadf61ae252a51a130223c52bfefb50310d5b2deb062cf0f",
|
||||
"sha256:86e9f8cd4b0cdd57b4ae71a9c186717daa4c5a99f3238a8723f416256e0b064d",
|
||||
"sha256:8f264ba2701b8c9f815b272ad568d555ef98dfe1576802ab3149c3629a9f2221",
|
||||
"sha256:9342dd70a1e151684727c9c91ea003b2fb33523bf19385d4554f7897ca0141d4",
|
||||
"sha256:9361de40701666b034c59ad9e317bae95c973b9ff92513dd0eced11c6adf2e21",
|
||||
"sha256:9669179786254a2e7e57f0ecf224e978471491d660aaca833f845b72a2df3709",
|
||||
"sha256:aac1ba0a253e17889550ddb1b60a2063f7474155465577caa2a3b131224cfd54",
|
||||
"sha256:aef72eae10b5e3116bac6957de1df4d75909fc76d1499a53fb6387434b6bcd8d",
|
||||
"sha256:bd3166bb3b111e76a4f8e2980fa1addf2920a4ca9b2b8ca36a3bc3dedc618270",
|
||||
"sha256:c1b78fb9700fc961f53386ad2fd86d87091e06ede5d118b8a50dea285a071c24",
|
||||
"sha256:c3888a051226e676e383de03bf49eb633cd39fc829516e5334e69b8d81aae751",
|
||||
"sha256:c5f17ad25d2c1286436761b462e22b5020d83316f8e8fcb5deb2b3151f8f1d3a",
|
||||
"sha256:c851b35fc078389bc16b915a0a7c1d5923e12e2c5aeec58c52f4aa8085ac8237",
|
||||
"sha256:cb7df71de0af56000115eafd000b867d1261f786b5eebd88a0ca6360cccfaca7",
|
||||
"sha256:cedb2f9e1f990918ea061f28a0f0077a07702e3819602d3507e2ff98c8d20636",
|
||||
"sha256:e8caf961e1b1a945db76f1b5fa9c91498d15f545ac0ababbe575cfab185d3bd8"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
|
||||
"version": "==5.3"
|
||||
},
|
||||
"docutils": {
|
||||
"hashes": [
|
||||
"sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af",
|
||||
"sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.16"
|
||||
},
|
||||
"flake8": {
|
||||
"hashes": [
|
||||
"sha256:15e351d19611c887e482fb960eae4d44845013cc142d42896e9862f775d8cf5c",
|
||||
"sha256:f04b9fcbac03b0a3e58c0ab3a0ecc462e023a9faf046d57794184028123aa208"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.8.3"
|
||||
},
|
||||
"flake8-annotations": {
|
||||
"hashes": [
|
||||
"sha256:09fe1aa3f40cb8fef632a0ab3614050a7584bb884b6134e70cf1fc9eeee642fa",
|
||||
"sha256:5bda552f074fd6e34276c7761756fa07d824ffac91ce9c0a8555eb2bc5b92d7a"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.4.0"
|
||||
},
|
||||
"flake8-bugbear": {
|
||||
"hashes": [
|
||||
"sha256:a3ddc03ec28ba2296fc6f89444d1c946a6b76460f859795b35b77d4920a51b63",
|
||||
"sha256:bd02e4b009fb153fe6072c31c52aeab5b133d508095befb2ffcf3b41c4823162"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==20.1.4"
|
||||
},
|
||||
"flake8-comprehensions": {
|
||||
"hashes": [
|
||||
"sha256:44eaae9894aa15f86e0c86df1e218e7917494fab6f96d28f96a029c460f17d92",
|
||||
"sha256:d5751acc0f7364794c71d06f113f4686d6e2e26146a50fa93130b9f200fe160d"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.2.3"
|
||||
},
|
||||
"flake8-import-order": {
|
||||
"hashes": [
|
||||
"sha256:90a80e46886259b9c396b578d75c749801a41ee969a235e163cfe1be7afd2543",
|
||||
"sha256:a28dc39545ea4606c1ac3c24e9d05c849c6e5444a50fb7e9cdd430fc94de6e92"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.18.1"
|
||||
},
|
||||
"flake8-pep3101": {
|
||||
"hashes": [
|
||||
"sha256:86e3eb4e42de8326dcd98ebdeaf9a3c6854203a48f34aeb3e7e8ed948107f512",
|
||||
"sha256:a5dae1caca1243b2b40108dce926d97cf5a9f52515c4a4cbb1ffe1ca0c54e343"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.3.0"
|
||||
},
|
||||
"flake8-print": {
|
||||
"hashes": [
|
||||
"sha256:324f9e59a522518daa2461bacd7f82da3c34eb26a4314c2a54bd493f8b394a68"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.1.4"
|
||||
},
|
||||
"genshi": {
|
||||
"hashes": [
|
||||
"sha256:5e92e278ca1ea395349a451d54fc81dc3c1b543c48939a15bd36b7b3335e1560",
|
||||
"sha256:7933c95151d7dd2124a2b4c8dd85bb6aec881ca17c0556da0b40e56434b313a0"
|
||||
],
|
||||
"version": "==0.7.3"
|
||||
},
|
||||
"graphviz": {
|
||||
"hashes": [
|
||||
"sha256:088562ef6e3dad5e8dde9389caf4a84f768e65dcaa08238cfcdf36d2b30ccf61",
|
||||
"sha256:f5aad52a652c06825dcc5ee018d920fca26aef339386866094597fb3f2f222ce"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.14.1"
|
||||
},
|
||||
"idna": {
|
||||
"hashes": [
|
||||
"sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6",
|
||||
"sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==2.10"
|
||||
},
|
||||
"imagesize": {
|
||||
"hashes": [
|
||||
"sha256:6965f19a6a2039c7d48bca7dba2473069ff854c36ae6f19d2cde309d998228a1",
|
||||
"sha256:b1f6b5a4eab1f73479a50fb79fcf729514a900c341d8503d62a62dbc4127a2b1"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==1.2.0"
|
||||
},
|
||||
"iniconfig": {
|
||||
"hashes": [
|
||||
"sha256:80cf40c597eb564e86346103f609d74efce0f6b4d4f30ec8ce9e2c26411ba437",
|
||||
"sha256:e5f92f89355a67de0595932a6c6c02ab4afddc6fcdc0bfc5becd0d60884d3f69"
|
||||
],
|
||||
"version": "==1.0.1"
|
||||
},
|
||||
"jinja2": {
|
||||
"hashes": [
|
||||
"sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0",
|
||||
"sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
|
||||
"version": "==2.11.2"
|
||||
},
|
||||
"lxml": {
|
||||
"hashes": [
|
||||
"sha256:05a444b207901a68a6526948c7cc8f9fe6d6f24c70781488e32fd74ff5996e3f",
|
||||
"sha256:08fc93257dcfe9542c0a6883a25ba4971d78297f63d7a5a26ffa34861ca78730",
|
||||
"sha256:107781b213cf7201ec3806555657ccda67b1fccc4261fb889ef7fc56976db81f",
|
||||
"sha256:121b665b04083a1e85ff1f5243d4a93aa1aaba281bc12ea334d5a187278ceaf1",
|
||||
"sha256:1fa21263c3aba2b76fd7c45713d4428dbcc7644d73dcf0650e9d344e433741b3",
|
||||
"sha256:2b30aa2bcff8e958cd85d907d5109820b01ac511eae5b460803430a7404e34d7",
|
||||
"sha256:4b4a111bcf4b9c948e020fd207f915c24a6de3f1adc7682a2d92660eb4e84f1a",
|
||||
"sha256:5591c4164755778e29e69b86e425880f852464a21c7bb53c7ea453bbe2633bbe",
|
||||
"sha256:59daa84aef650b11bccd18f99f64bfe44b9f14a08a28259959d33676554065a1",
|
||||
"sha256:5a9c8d11aa2c8f8b6043d845927a51eb9102eb558e3f936df494e96393f5fd3e",
|
||||
"sha256:5dd20538a60c4cc9a077d3b715bb42307239fcd25ef1ca7286775f95e9e9a46d",
|
||||
"sha256:74f48ec98430e06c1fa8949b49ebdd8d27ceb9df8d3d1c92e1fdc2773f003f20",
|
||||
"sha256:786aad2aa20de3dbff21aab86b2fb6a7be68064cbbc0219bde414d3a30aa47ae",
|
||||
"sha256:7ad7906e098ccd30d8f7068030a0b16668ab8aa5cda6fcd5146d8d20cbaa71b5",
|
||||
"sha256:80a38b188d20c0524fe8959c8ce770a8fdf0e617c6912d23fc97c68301bb9aba",
|
||||
"sha256:8f0ec6b9b3832e0bd1d57af41f9238ea7709bbd7271f639024f2fc9d3bb01293",
|
||||
"sha256:92282c83547a9add85ad658143c76a64a8d339028926d7dc1998ca029c88ea6a",
|
||||
"sha256:94150231f1e90c9595ccc80d7d2006c61f90a5995db82bccbca7944fd457f0f6",
|
||||
"sha256:9dc9006dcc47e00a8a6a029eb035c8f696ad38e40a27d073a003d7d1443f5d88",
|
||||
"sha256:a76979f728dd845655026ab991df25d26379a1a8fc1e9e68e25c7eda43004bed",
|
||||
"sha256:aa8eba3db3d8761db161003e2d0586608092e217151d7458206e243be5a43843",
|
||||
"sha256:bea760a63ce9bba566c23f726d72b3c0250e2fa2569909e2d83cda1534c79443",
|
||||
"sha256:c3f511a3c58676147c277eff0224c061dd5a6a8e1373572ac817ac6324f1b1e0",
|
||||
"sha256:c9d317efde4bafbc1561509bfa8a23c5cab66c44d49ab5b63ff690f5159b2304",
|
||||
"sha256:cc411ad324a4486b142c41d9b2b6a722c534096963688d879ea6fa8a35028258",
|
||||
"sha256:cdc13a1682b2a6241080745b1953719e7fe0850b40a5c71ca574f090a1391df6",
|
||||
"sha256:cfd7c5dd3c35c19cec59c63df9571c67c6d6e5c92e0fe63517920e97f61106d1",
|
||||
"sha256:e1cacf4796b20865789083252186ce9dc6cc59eca0c2e79cca332bdff24ac481",
|
||||
"sha256:e70d4e467e243455492f5de463b72151cc400710ac03a0678206a5f27e79ddef",
|
||||
"sha256:ecc930ae559ea8a43377e8b60ca6f8d61ac532fc57efb915d899de4a67928efd",
|
||||
"sha256:f161af26f596131b63b236372e4ce40f3167c1b5b5d459b29d2514bd8c9dc9ee"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==4.5.2"
|
||||
},
|
||||
"markupsafe": {
|
||||
"hashes": [
|
||||
"sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473",
|
||||
"sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161",
|
||||
"sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235",
|
||||
"sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5",
|
||||
"sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42",
|
||||
"sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff",
|
||||
"sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b",
|
||||
"sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1",
|
||||
"sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e",
|
||||
"sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183",
|
||||
"sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66",
|
||||
"sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b",
|
||||
"sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1",
|
||||
"sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15",
|
||||
"sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1",
|
||||
"sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e",
|
||||
"sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b",
|
||||
"sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905",
|
||||
"sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735",
|
||||
"sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d",
|
||||
"sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e",
|
||||
"sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d",
|
||||
"sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c",
|
||||
"sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21",
|
||||
"sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2",
|
||||
"sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5",
|
||||
"sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b",
|
||||
"sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6",
|
||||
"sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f",
|
||||
"sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f",
|
||||
"sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2",
|
||||
"sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7",
|
||||
"sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==1.1.1"
|
||||
},
|
||||
"mccabe": {
|
||||
"hashes": [
|
||||
"sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42",
|
||||
"sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"
|
||||
],
|
||||
"version": "==0.6.1"
|
||||
},
|
||||
"more-itertools": {
|
||||
"hashes": [
|
||||
"sha256:6f83822ae94818eae2612063a5101a7311e68ae8002005b5e05f03fd74a86a20",
|
||||
"sha256:9b30f12df9393f0d28af9210ff8efe48d10c94f73e5daf886f10c4b0b0b4f03c"
|
||||
],
|
||||
"markers": "python_version >= '3.5'",
|
||||
"version": "==8.5.0"
|
||||
},
|
||||
"mypy": {
|
||||
"hashes": [
|
||||
"sha256:2c6cde8aa3426c1682d35190b59b71f661237d74b053822ea3d748e2c9578a7c",
|
||||
"sha256:3fdda71c067d3ddfb21da4b80e2686b71e9e5c72cca65fa216d207a358827f86",
|
||||
"sha256:5dd13ff1f2a97f94540fd37a49e5d255950ebcdf446fb597463a40d0df3fac8b",
|
||||
"sha256:6731603dfe0ce4352c555c6284c6db0dc935b685e9ce2e4cf220abe1e14386fd",
|
||||
"sha256:6bb93479caa6619d21d6e7160c552c1193f6952f0668cdda2f851156e85186fc",
|
||||
"sha256:81c7908b94239c4010e16642c9102bfc958ab14e36048fa77d0be3289dda76ea",
|
||||
"sha256:9c7a9a7ceb2871ba4bac1cf7217a7dd9ccd44c27c2950edbc6dc08530f32ad4e",
|
||||
"sha256:a4a2cbcfc4cbf45cd126f531dedda8485671545b43107ded25ce952aac6fb308",
|
||||
"sha256:b7fbfabdbcc78c4f6fc4712544b9b0d6bf171069c6e0e3cb82440dd10ced3406",
|
||||
"sha256:c05b9e4fb1d8a41d41dec8786c94f3b95d3c5f528298d769eb8e73d293abc48d",
|
||||
"sha256:d7df6eddb6054d21ca4d3c6249cae5578cb4602951fd2b6ee2f5510ffb098707",
|
||||
"sha256:e0b61738ab504e656d1fe4ff0c0601387a5489ca122d55390ade31f9ca0e252d",
|
||||
"sha256:eff7d4a85e9eea55afa34888dfeaccde99e7520b51f867ac28a48492c0b1130c",
|
||||
"sha256:f05644db6779387ccdb468cc47a44b4356fc2ffa9287135d05b70a98dc83b89a"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.782"
|
||||
},
|
||||
"mypy-extensions": {
|
||||
"hashes": [
|
||||
"sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d",
|
||||
"sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"
|
||||
],
|
||||
"version": "==0.4.3"
|
||||
},
|
||||
"packaging": {
|
||||
"hashes": [
|
||||
"sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8",
|
||||
"sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==20.4"
|
||||
},
|
||||
"pathspec": {
|
||||
"hashes": [
|
||||
"sha256:7d91249d21749788d07a2d0f94147accd8f845507400749ea19c1ec9054a12b0",
|
||||
"sha256:da45173eb3a6f2a5a487efba21f050af2b41948be6ab52b6a1e3ff22bb8b7061"
|
||||
],
|
||||
"version": "==0.8.0"
|
||||
},
|
||||
"pluggy": {
|
||||
"hashes": [
|
||||
"sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0",
|
||||
"sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==0.13.1"
|
||||
},
|
||||
"py": {
|
||||
"hashes": [
|
||||
"sha256:366389d1db726cd2fcfc79732e75410e5fe4d31db13692115529d34069a043c2",
|
||||
"sha256:9ca6883ce56b4e8da7e79ac18787889fa5206c79dcc67fb065376cd2fe03f342"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==1.9.0"
|
||||
},
|
||||
"pycodestyle": {
|
||||
"hashes": [
|
||||
"sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367",
|
||||
"sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.6.0"
|
||||
},
|
||||
"pyflakes": {
|
||||
"hashes": [
|
||||
"sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92",
|
||||
"sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==2.2.0"
|
||||
},
|
||||
"pygments": {
|
||||
"hashes": [
|
||||
"sha256:307543fe65c0947b126e83dd5a61bd8acbd84abec11f43caebaf5534cbc17998",
|
||||
"sha256:926c3f319eda178d1bd90851e4317e6d8cdb5e292a3386aac9bd75eca29cf9c7"
|
||||
],
|
||||
"markers": "python_version >= '3.5'",
|
||||
"version": "==2.7.1"
|
||||
},
|
||||
"pyparsing": {
|
||||
"hashes": [
|
||||
"sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1",
|
||||
"sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"
|
||||
],
|
||||
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==2.4.7"
|
||||
},
|
||||
"pytest": {
|
||||
"hashes": [
|
||||
"sha256:0e37f61339c4578776e090c3b8f6b16ce4db333889d65d0efb305243ec544b40",
|
||||
"sha256:c8f57c2a30983f469bf03e68cdfa74dc474ce56b8f280ddcb080dfd91df01043"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==6.0.2"
|
||||
},
|
||||
"pytest-cov": {
|
||||
"hashes": [
|
||||
"sha256:45ec2d5182f89a81fc3eb29e3d1ed3113b9e9a873bcddb2a71faaab066110191",
|
||||
"sha256:47bd0ce14056fdd79f93e1713f88fad7bdcc583dcd7783da86ef2f085a0bb88e"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.10.1"
|
||||
},
|
||||
"pytz": {
|
||||
"hashes": [
|
||||
"sha256:a494d53b6d39c3c6e44c3bec237336e14305e4f29bbf800b599253057fbb79ed",
|
||||
"sha256:c35965d010ce31b23eeb663ed3cc8c906275d6be1a34393a1d73a41febf4a048"
|
||||
],
|
||||
"version": "==2020.1"
|
||||
},
|
||||
"regex": {
|
||||
"hashes": [
|
||||
"sha256:0dc64ee3f33cd7899f79a8d788abfbec168410be356ed9bd30bbd3f0a23a7204",
|
||||
"sha256:1269fef3167bb52631ad4fa7dd27bf635d5a0790b8e6222065d42e91bede4162",
|
||||
"sha256:14a53646369157baa0499513f96091eb70382eb50b2c82393d17d7ec81b7b85f",
|
||||
"sha256:3a3af27a8d23143c49a3420efe5b3f8cf1a48c6fc8bc6856b03f638abc1833bb",
|
||||
"sha256:46bac5ca10fb748d6c55843a931855e2727a7a22584f302dd9bb1506e69f83f6",
|
||||
"sha256:4c037fd14c5f4e308b8370b447b469ca10e69427966527edcab07f52d88388f7",
|
||||
"sha256:51178c738d559a2d1071ce0b0f56e57eb315bcf8f7d4cf127674b533e3101f88",
|
||||
"sha256:5ea81ea3dbd6767873c611687141ec7b06ed8bab43f68fad5b7be184a920dc99",
|
||||
"sha256:6961548bba529cac7c07af2fd4d527c5b91bb8fe18995fed6044ac22b3d14644",
|
||||
"sha256:75aaa27aa521a182824d89e5ab0a1d16ca207318a6b65042b046053cfc8ed07a",
|
||||
"sha256:7a2dd66d2d4df34fa82c9dc85657c5e019b87932019947faece7983f2089a840",
|
||||
"sha256:8a51f2c6d1f884e98846a0a9021ff6861bdb98457879f412fdc2b42d14494067",
|
||||
"sha256:9c568495e35599625f7b999774e29e8d6b01a6fb684d77dee1f56d41b11b40cd",
|
||||
"sha256:9eddaafb3c48e0900690c1727fba226c4804b8e6127ea409689c3bb492d06de4",
|
||||
"sha256:bbb332d45b32df41200380fff14712cb6093b61bd142272a10b16778c418e98e",
|
||||
"sha256:bc3d98f621898b4a9bc7fecc00513eec8f40b5b83913d74ccb445f037d58cd89",
|
||||
"sha256:c11d6033115dc4887c456565303f540c44197f4fc1a2bfb192224a301534888e",
|
||||
"sha256:c50a724d136ec10d920661f1442e4a8b010a4fe5aebd65e0c2241ea41dbe93dc",
|
||||
"sha256:d0a5095d52b90ff38592bbdc2644f17c6d495762edf47d876049cfd2968fbccf",
|
||||
"sha256:d6cff2276e502b86a25fd10c2a96973fdb45c7a977dca2138d661417f3728341",
|
||||
"sha256:e46d13f38cfcbb79bfdb2964b0fe12561fe633caf964a77a5f8d4e45fe5d2ef7"
|
||||
],
|
||||
"version": "==2020.7.14"
|
||||
},
|
||||
"requests": {
|
||||
"hashes": [
|
||||
"sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b",
|
||||
"sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
|
||||
"version": "==2.24.0"
|
||||
},
|
||||
"rope": {
|
||||
"hashes": [
|
||||
"sha256:658ad6705f43dcf3d6df379da9486529cf30e02d9ea14c5682aa80eb33b649e1"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.17.0"
|
||||
},
|
||||
"rst2html5": {
|
||||
"hashes": [
|
||||
"sha256:8e64e80c52d0383aa04f2701a7627c2f2a09d59c61a3dc3555d845333bc31648"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.10.6"
|
||||
},
|
||||
"six": {
|
||||
"hashes": [
|
||||
"sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259",
|
||||
"sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==1.15.0"
|
||||
},
|
||||
"snowballstemmer": {
|
||||
"hashes": [
|
||||
"sha256:209f257d7533fdb3cb73bdbd24f436239ca3b2fa67d56f6ff88e86be08cc5ef0",
|
||||
"sha256:df3bac3df4c2c01363f3dd2cfa78cce2840a79b9f1c2d2de9ce8d31683992f52"
|
||||
],
|
||||
"version": "==2.0.0"
|
||||
},
|
||||
"sphinx": {
|
||||
"hashes": [
|
||||
"sha256:321d6d9b16fa381a5306e5a0b76cd48ffbc588e6340059a729c6fdd66087e0e8",
|
||||
"sha256:ce6fd7ff5b215af39e2fcd44d4a321f6694b4530b6f2b2109b64d120773faea0"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.2.1"
|
||||
},
|
||||
"sphinx-rtd-theme": {
|
||||
"hashes": [
|
||||
"sha256:22c795ba2832a169ca301cd0a083f7a434e09c538c70beb42782c073651b707d",
|
||||
"sha256:373413d0f82425aaa28fb288009bf0d0964711d347763af2f1b65cafcb028c82"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.5.0"
|
||||
},
|
||||
"sphinxcontrib-applehelp": {
|
||||
"hashes": [
|
||||
"sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a",
|
||||
"sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"
|
||||
],
|
||||
"markers": "python_version >= '3.5'",
|
||||
"version": "==1.0.2"
|
||||
},
|
||||
"sphinxcontrib-devhelp": {
|
||||
"hashes": [
|
||||
"sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e",
|
||||
"sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"
|
||||
],
|
||||
"markers": "python_version >= '3.5'",
|
||||
"version": "==1.0.2"
|
||||
},
|
||||
"sphinxcontrib-htmlhelp": {
|
||||
"hashes": [
|
||||
"sha256:3c0bc24a2c41e340ac37c85ced6dafc879ab485c095b1d65d2461ac2f7cca86f",
|
||||
"sha256:e8f5bb7e31b2dbb25b9cc435c8ab7a79787ebf7f906155729338f3156d93659b"
|
||||
],
|
||||
"markers": "python_version >= '3.5'",
|
||||
"version": "==1.0.3"
|
||||
},
|
||||
"sphinxcontrib-jsmath": {
|
||||
"hashes": [
|
||||
"sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178",
|
||||
"sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"
|
||||
],
|
||||
"markers": "python_version >= '3.5'",
|
||||
"version": "==1.0.1"
|
||||
},
|
||||
"sphinxcontrib-qthelp": {
|
||||
"hashes": [
|
||||
"sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72",
|
||||
"sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"
|
||||
],
|
||||
"markers": "python_version >= '3.5'",
|
||||
"version": "==1.0.3"
|
||||
},
|
||||
"sphinxcontrib-serializinghtml": {
|
||||
"hashes": [
|
||||
"sha256:eaa0eccc86e982a9b939b2b82d12cc5d013385ba5eadcc7e4fed23f4405f77bc",
|
||||
"sha256:f242a81d423f59617a8e5cf16f5d4d74e28ee9a66f9e5b637a18082991db5a9a"
|
||||
],
|
||||
"markers": "python_version >= '3.5'",
|
||||
"version": "==1.1.4"
|
||||
},
|
||||
"termcolor": {
|
||||
"hashes": [
|
||||
"sha256:1d6d69ce66211143803fbc56652b41d73b4a400a2891d7bf7a1cdf4c02de613b"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.1.0"
|
||||
},
|
||||
"toml": {
|
||||
"hashes": [
|
||||
"sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f",
|
||||
"sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88"
|
||||
],
|
||||
"version": "==0.10.1"
|
||||
},
|
||||
"typed-ast": {
|
||||
"hashes": [
|
||||
"sha256:0666aa36131496aed8f7be0410ff974562ab7eeac11ef351def9ea6fa28f6355",
|
||||
"sha256:0c2c07682d61a629b68433afb159376e24e5b2fd4641d35424e462169c0a7919",
|
||||
"sha256:249862707802d40f7f29f6e1aad8d84b5aa9e44552d2cc17384b209f091276aa",
|
||||
"sha256:24995c843eb0ad11a4527b026b4dde3da70e1f2d8806c99b7b4a7cf491612652",
|
||||
"sha256:269151951236b0f9a6f04015a9004084a5ab0d5f19b57de779f908621e7d8b75",
|
||||
"sha256:4083861b0aa07990b619bd7ddc365eb7fa4b817e99cf5f8d9cf21a42780f6e01",
|
||||
"sha256:498b0f36cc7054c1fead3d7fc59d2150f4d5c6c56ba7fb150c013fbc683a8d2d",
|
||||
"sha256:4e3e5da80ccbebfff202a67bf900d081906c358ccc3d5e3c8aea42fdfdfd51c1",
|
||||
"sha256:6daac9731f172c2a22ade6ed0c00197ee7cc1221aa84cfdf9c31defeb059a907",
|
||||
"sha256:715ff2f2df46121071622063fc7543d9b1fd19ebfc4f5c8895af64a77a8c852c",
|
||||
"sha256:73d785a950fc82dd2a25897d525d003f6378d1cb23ab305578394694202a58c3",
|
||||
"sha256:8c8aaad94455178e3187ab22c8b01a3837f8ee50e09cf31f1ba129eb293ec30b",
|
||||
"sha256:8ce678dbaf790dbdb3eba24056d5364fb45944f33553dd5869b7580cdbb83614",
|
||||
"sha256:aaee9905aee35ba5905cfb3c62f3e83b3bec7b39413f0a7f19be4e547ea01ebb",
|
||||
"sha256:bcd3b13b56ea479b3650b82cabd6b5343a625b0ced5429e4ccad28a8973f301b",
|
||||
"sha256:c9e348e02e4d2b4a8b2eedb48210430658df6951fa484e59de33ff773fbd4b41",
|
||||
"sha256:d205b1b46085271b4e15f670058ce182bd1199e56b317bf2ec004b6a44f911f6",
|
||||
"sha256:d43943ef777f9a1c42bf4e552ba23ac77a6351de620aa9acf64ad54933ad4d34",
|
||||
"sha256:d5d33e9e7af3b34a40dc05f498939f0ebf187f07c385fd58d591c533ad8562fe",
|
||||
"sha256:fc0fea399acb12edbf8a628ba8d2312f583bdbdb3335635db062fa98cf71fca4",
|
||||
"sha256:fe460b922ec15dd205595c9b5b99e2f056fd98ae8f9f56b888e7a17dc2b757e7"
|
||||
],
|
||||
"version": "==1.4.1"
|
||||
},
|
||||
"typing-extensions": {
|
||||
"hashes": [
|
||||
"sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918",
|
||||
"sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c",
|
||||
"sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"
|
||||
],
|
||||
"version": "==3.7.4.3"
|
||||
},
|
||||
"urllib3": {
|
||||
"editable": true,
|
||||
"extras": [
|
||||
"chromecast",
|
||||
"keyring",
|
||||
"server"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
|
||||
"path": "."
|
||||
}
|
||||
}
|
||||
}
|
@@ -47,7 +47,7 @@ def check_file(path: Path) -> bool:
|
||||
|
||||
|
||||
valid = True
|
||||
for path in Path("sublime").glob("**/*.py"):
|
||||
for path in Path("sublime_music").glob("**/*.py"):
|
||||
valid &= check_file(path)
|
||||
|
||||
for path in Path("tests").glob("**/*.py"):
|
||||
@@ -56,7 +56,7 @@ for path in Path("tests").glob("**/*.py"):
|
||||
"""
|
||||
Checks that the version in the CHANGELOG is the same as the version in ``__init__.py``.
|
||||
"""
|
||||
with open(Path("sublime/__init__.py")) as f:
|
||||
with open(Path("sublime_music/__init__.py")) as f:
|
||||
for line in f:
|
||||
if line.startswith("__version__"):
|
||||
version = eval(line.split()[-1])
|
||||
|
@@ -11,5 +11,7 @@ RUN dnf -y update && \
|
||||
|
||||
RUN flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
|
||||
|
||||
RUN flatpak install -y org.gnome.Platform//3.34 && \
|
||||
flatpak install -y org.gnome.Sdk//3.34
|
||||
RUN flatpak install -y org.gnome.Platform//3.38 && \
|
||||
flatpak install -y org.gnome.Sdk//3.38
|
||||
|
||||
RUN pip3 install requirements-parser
|
||||
|
@@ -3,5 +3,10 @@ export PYENV_ROOT="${HOME}/.pyenv"
|
||||
export PATH="${PYENV_ROOT}/bin:$PATH"
|
||||
eval "$(pyenv init -)"
|
||||
|
||||
export PIPENV_VENV_IN_PROJECT=1
|
||||
pipenv install --dev
|
||||
apt-get install -y python3-venv
|
||||
pip3 install poetry
|
||||
|
||||
mkdir -p ~/.config/pypoetry/
|
||||
echo "[virtualenvs]" > ~/.config/pypoetry/config.toml
|
||||
echo "in-project = true" >> ~/.config/pypoetry/config.toml
|
||||
poetry install
|
||||
|
@@ -3,7 +3,7 @@
|
||||
set -xe
|
||||
|
||||
pushd docs
|
||||
pipenv run make html
|
||||
poetry run make html
|
||||
popd
|
||||
|
||||
mv docs/_build/html public
|
||||
|
@@ -40,7 +40,7 @@ RUN apt update && \
|
||||
RUN cd /usr/local/src && \
|
||||
curl -L https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-installer | bash
|
||||
|
||||
RUN pip3 install pipenv
|
||||
RUN pip3 install poetry
|
||||
|
||||
# Install the correct Python version with pyenv
|
||||
COPY install-python.sh /tmp/install-python.sh
|
||||
|
32
cicd/run_if_tagged_with_version
Executable file
@@ -0,0 +1,32 @@
|
||||
#! /usr/bin/env python3
|
||||
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
version_tag_re = re.compile(r"v\d+\.\d+\.\d+")
|
||||
|
||||
|
||||
tags = (
|
||||
subprocess.run(["git", "tag", "--contains", "HEAD"], capture_output=True)
|
||||
.stdout.decode()
|
||||
.strip()
|
||||
.split()
|
||||
)
|
||||
|
||||
# If one of the tags is a version tag, then run the commands specified in the
|
||||
# parameters.
|
||||
for tag in tags:
|
||||
if match := version_tag_re.match(tag):
|
||||
print(f"VERSION TAG {tag} FOUND")
|
||||
|
||||
# Execute the associated commands, raising an exception if the command
|
||||
# returns a non-zero value.
|
||||
for arg in sys.argv[1:]:
|
||||
print(f"+ {' '.join(arg.split())}")
|
||||
subprocess.run(arg.split()).check_returncode()
|
||||
|
||||
# Don't run the else statement of the for loop.
|
||||
break
|
||||
else:
|
||||
print("No version tag found for this commit. Skipping")
|
@@ -11,7 +11,7 @@ BUILDDIR = _build
|
||||
SPHINXDOCBUILD = sphinx-apidoc
|
||||
SPHINXDOCOPTS = -f -e -T
|
||||
DOCSSOURCE = ./api/
|
||||
PROJECTDIR = ../sublime/
|
||||
PROJECTDIR = ../sublime_music/
|
||||
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
help:
|
||||
|
@@ -8,8 +8,8 @@ server, data on the local filesystem, or even an entirely different service.
|
||||
|
||||
This document is designed to help you understand the Adapter API so that you can
|
||||
create your own custom adapters. This document is best read in conjunction with
|
||||
the :class:`sublime.adapters.Adapter` documentation. This document is meant as a
|
||||
guide to tell you a general order in which to implement things.
|
||||
the :class:`sublime_music.adapters.Adapter` documentation. This document is
|
||||
meant as a guide to tell you a general order in which to implement things.
|
||||
|
||||
Terms
|
||||
=====
|
||||
@@ -35,8 +35,8 @@ Creating Your Adapter Class
|
||||
An adapter is composed of a single Python module. The adapter module can have
|
||||
arbitrary code, and as many files/classes/functions/etc. as necessary, however
|
||||
there must be one and only one class in the module which inherits from the
|
||||
:class:`sublime.adapters.Adapter` class. Normally, a single file with a single
|
||||
class should be enough to implement the entire adapter.
|
||||
:class:`sublime_music.adapters.Adapter` class. Normally, a single file with a
|
||||
single class should be enough to implement the entire adapter.
|
||||
|
||||
.. warning::
|
||||
|
||||
@@ -49,16 +49,16 @@ class should be enough to implement the entire adapter.
|
||||
After you've created the class, you will want to implement the following
|
||||
functions and properties first:
|
||||
|
||||
* ``get_ui_info``: Returns a :class:`sublime.adapters.UIInfo` with the info for
|
||||
the adapter.
|
||||
* ``get_ui_info``: Returns a :class:`sublime_music.adapters.UIInfo` with the
|
||||
info for the adapter.
|
||||
* ``__init__``: Used to initialize your adapter. See the
|
||||
:class:`sublime.adapters.Adapter.__init__` documentation for the function
|
||||
signature of the ``__init__`` function.
|
||||
:class:`sublime_music.adapters.Adapter.__init__` documentation for the
|
||||
function signature of the ``__init__`` function.
|
||||
* ``ping_status``: Assuming that your adapter requires connection to the
|
||||
internet, this property needs to be implemented. (If your adapter doesn't
|
||||
require connection to the internet, set
|
||||
:class:`sublime.adapters.Adapter.is_networked` to ``False`` and ignore the
|
||||
rest of this bullet point.)
|
||||
:class:`sublime_music.adapters.Adapter.is_networked` to ``False`` and ignore
|
||||
the rest of this bullet point.)
|
||||
|
||||
This property will tell the UI whether or not the underlying server can be
|
||||
pinged.
|
||||
@@ -80,12 +80,12 @@ functions and properties first:
|
||||
|
||||
If you don't want to implement all of the GTK logic yourself, and just want a
|
||||
simple form, then you can use the
|
||||
:class:`sublime.adapters.ConfigureServerForm` class to generate a form in a
|
||||
declarative manner.
|
||||
:class:`sublime_music.adapters.ConfigureServerForm` class to generate a form
|
||||
in a declarative manner.
|
||||
|
||||
.. note::
|
||||
|
||||
The :class:`sublime.adapters.Adapter` class is an `Abstract Base Class
|
||||
The :class:`sublime_music.adapters.Adapter` class is an `Abstract Base Class
|
||||
<abc_>`_ and all required functions are annotated with the
|
||||
``@abstractmethod`` decorator. This means that your adapter will fail to
|
||||
instantiate if the abstract methods are not implemented.
|
||||
@@ -102,21 +102,21 @@ must do the following:
|
||||
name must be unique within your adapter.
|
||||
|
||||
2. Add a new entry to the return value of your
|
||||
:class:`sublime.adapters.Adapter.get_config_parameters` function with the key
|
||||
being the name from (1), and the value being a
|
||||
:class:`sublime.adapters.ConfigParamDescriptor`. The order of the keys in the
|
||||
dictionary matters, since the UI uses that to determine the order in which
|
||||
the configuration parameters will be shown in the UI.
|
||||
:class:`sublime_music.adapters.Adapter.get_config_parameters` function with
|
||||
the key being the name from (1), and the value being a
|
||||
:class:`sublime_music.adapters.ConfigParamDescriptor`. The order of the keys
|
||||
in the dictionary matters, since the UI uses that to determine the order in
|
||||
which the configuration parameters will be shown in the UI.
|
||||
|
||||
3. Add any verifications that are necessary for your configuration parameter in
|
||||
your :class:`sublime.adapters.Adapter.verify_configuration` function. If you
|
||||
parameter descriptor has ``required = True``, then that parameter is
|
||||
your :class:`sublime_music.adapters.Adapter.verify_configuration` function.
|
||||
If you parameter descriptor has ``required = True``, then that parameter is
|
||||
guaranteed to appear in the configuration.
|
||||
|
||||
4. The configuration parameter will be passed into your
|
||||
:class:`sublime.adapters.Adapter.init` function. It is guaranteed that the
|
||||
``verify_configuration`` will have been called first, so there is no need to
|
||||
re-verify the config that is passed.
|
||||
:class:`sublime_music.adapters.Adapter.init` function. It is guaranteed that
|
||||
the ``verify_configuration`` will have been called first, so there is no need
|
||||
to re-verify the config that is passed.
|
||||
|
||||
Implementing Data Retrieval Methods
|
||||
-----------------------------------
|
||||
|
10
docs/conf.py
@@ -26,13 +26,13 @@ author = "Sumner Evans"
|
||||
gitlab_url = "https://gitlab.com/sublime-music/sublime-music/"
|
||||
|
||||
# Get the version from the package.
|
||||
module_name = "sublime"
|
||||
init_file = Path(__file__).parent.parent.joinpath("sublime/__init__.py").resolve()
|
||||
module_name = "sublime_music"
|
||||
init_file = Path(__file__).parent.parent.joinpath("sublime_music/__init__.py").resolve()
|
||||
spec = importlib.util.spec_from_file_location(module_name, str(init_file))
|
||||
sublime = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(sublime)
|
||||
sublime_music = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(sublime_music)
|
||||
|
||||
version = release = f"v{sublime.__version__}"
|
||||
version = release = f"v{sublime_music.__version__}"
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"app-id": "app.sublimemusic.SublimeMusic",
|
||||
"runtime": "org.gnome.Platform",
|
||||
"runtime-version": "3.34",
|
||||
"runtime-version": "3.38",
|
||||
"sdk": "org.gnome.Sdk",
|
||||
"command": "sublime-music",
|
||||
"rename-icon": "sublime-music",
|
||||
@@ -20,11 +20,12 @@
|
||||
"--socket=wayland",
|
||||
"--share=network",
|
||||
"--socket=pulseaudio",
|
||||
"--socket=session-bus",
|
||||
"--talk-name=org.freedesktop.Notifications",
|
||||
"--own-name=org.mpris.MediaPlayer2.SublimeMusic",
|
||||
"--filesystem=xdg-config/sublime-music",
|
||||
"--filesystem=xdg-cache/sublime-music",
|
||||
"--filesystem=xdg-data/sublime-music"
|
||||
"--filesystem=xdg-data/sublime-music",
|
||||
"--env=TMPDIR=/tmp"
|
||||
],
|
||||
"modules": [
|
||||
{
|
||||
@@ -66,6 +67,9 @@
|
||||
"build-commands": [
|
||||
"pip3 install --prefix=/app ."
|
||||
],
|
||||
"build-options": {
|
||||
"build-args": [ "--share=network" ]
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"type": "dir",
|
||||
@@ -101,51 +105,6 @@
|
||||
}
|
||||
],
|
||||
"modules": [
|
||||
{
|
||||
"name": "python-3.8",
|
||||
"sources": [
|
||||
{
|
||||
"type": "archive",
|
||||
"url": "https://www.python.org/ftp/python/3.8.3/Python-3.8.3.tar.xz",
|
||||
"sha256": "dfab5ec723c218082fe3d5d7ae17ecbdebffa9a1aea4d64aa3a2ecdd2e795864"
|
||||
}
|
||||
],
|
||||
"config-opts": [
|
||||
"--enable-shared",
|
||||
"--with-ensurepip=yes",
|
||||
"--with-system-expat",
|
||||
"--with-system-ffi",
|
||||
"--enable-loadable-sqlite-extensions",
|
||||
"--with-dbmliborder=gdbm",
|
||||
"--enable-unicode=ucs4"
|
||||
],
|
||||
"post-install": [
|
||||
"chmod 644 $FLATPAK_DEST/lib/libpython3.8.so.1.0"
|
||||
],
|
||||
"cleanup": [
|
||||
"/bin/2to3*",
|
||||
"/bin/easy_install*",
|
||||
"/bin/idle*",
|
||||
"/bin/pydoc*",
|
||||
"/bin/python*-config",
|
||||
"/bin/pyvenv*",
|
||||
"/include",
|
||||
"/lib/pkgconfig",
|
||||
"/lib/python*/config",
|
||||
"/share",
|
||||
"/lib/python*/test",
|
||||
"/lib/python*/*/test",
|
||||
"/lib/python*/*/tests",
|
||||
"/lib/python*/lib-tk/test",
|
||||
"/lib/python*/lib-dynload/_*_test.*.so",
|
||||
"/lib/python*/lib-dynload/_test*.*.so",
|
||||
"/lib/python*/idlelib",
|
||||
"/lib/python*/tkinter*",
|
||||
"/lib/python*/turtle*",
|
||||
"/lib/python*/lib2to3*",
|
||||
"/lib/python3.8/config/libpython3.8.a"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "luajit",
|
||||
"no-autogen": true,
|
||||
|
@@ -1,25 +1,32 @@
|
||||
bleach==3.2.1
|
||||
bottle==0.12.18
|
||||
casttube==0.2.1
|
||||
certifi==2020.4.5.1
|
||||
certifi==2020.6.20
|
||||
chardet==3.0.4
|
||||
dataclasses-json==0.4.4
|
||||
deepdiff==4.3.2
|
||||
dataclasses-json==0.5.
|
||||
deepdiff==5.0.2
|
||||
fuzzywuzzy==0.18.0
|
||||
idna==2.9
|
||||
ifaddr==0.1.6
|
||||
idna==2.10
|
||||
ifaddr==0.1.7
|
||||
jeepney==0.4.3
|
||||
marshmallow-enum==1.5.1
|
||||
marshmallow==3.6.0
|
||||
marshmallow==3.8.0
|
||||
mypy-extensions==0.4.3
|
||||
ordered-set==4.0.1
|
||||
ordered-set==4.0.2
|
||||
packaging==20.4
|
||||
peewee==3.13.3
|
||||
protobuf==3.12.2
|
||||
pychromecast==5.3.0
|
||||
protobuf==3.13.0
|
||||
pycairo==1.19.1
|
||||
pychromecast==7.3.0
|
||||
pycparser==2.20
|
||||
pyparsing==2.4.7
|
||||
python-dateutil==2.8.1
|
||||
python-levenshtein==0.12.0
|
||||
python-mpv==0.4.6
|
||||
requests==2.23.0
|
||||
python-mpv==0.5.2
|
||||
requests==2.24.0
|
||||
semver==2.10.2
|
||||
six==1.15.0
|
||||
stringcase==1.2.0
|
||||
typing-extensions==3.7.4.2
|
||||
typing-inspect==0.6.0
|
||||
urllib3==1.25.9
|
||||
urllib3==1.25.10
|
||||
zeroconf==0.28.5
|
||||
|
@@ -5,6 +5,11 @@ set -xe
|
||||
REPO=${REPO:-/repo}
|
||||
APPID=app.sublimemusic.SublimeMusic
|
||||
|
||||
# TODO move these to the Docker container
|
||||
pip3 install requirements-parser
|
||||
flatpak install -y org.gnome.Platform//3.38
|
||||
flatpak install -y org.gnome.Sdk//3.38
|
||||
|
||||
rm -rf flatpak-builder-tools
|
||||
git clone https://github.com/flatpak/flatpak-builder-tools.git
|
||||
|
||||
|
1214
poetry.lock
generated
Normal file
@@ -1,3 +1,73 @@
|
||||
[tool.poetry]
|
||||
name = "sublime_music"
|
||||
version = "0.11.9"
|
||||
description = "A native GTK *sonic client."
|
||||
license = "GPL-3.0-or-later"
|
||||
authors = ["Sumner Evans <inquiries@sumnerevans.com>"]
|
||||
readme = "README.rst"
|
||||
homepage = "https://sublimemusic.app"
|
||||
repository = "https://sr.ht/~sumner/sublime-music"
|
||||
documentation = "https://sublime-music.gitlab.io/sublime-music/"
|
||||
keywords = ["airsonic", "music", "chromecast", "subsonic"]
|
||||
classifiers = [
|
||||
# 3 - Alpha
|
||||
# 4 - Beta
|
||||
# 5 - Production/Stable
|
||||
"Development Status :: 3 - Alpha",
|
||||
"Intended Audience :: End Users/Desktop",
|
||||
"Operating System :: POSIX",
|
||||
]
|
||||
|
||||
# TODO
|
||||
exclude = ["tests"]
|
||||
|
||||
[tool.poetry.urls]
|
||||
"Bug Tracker" = "https://todo.sr.ht/~sumner/sublime-music"
|
||||
|
||||
[tool.poetry.scripts]
|
||||
sublime-music = 'sublime_music.__main__:main'
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.8"
|
||||
bleach = "^3.2.1"
|
||||
dataclasses-json = "^0.5.2"
|
||||
deepdiff = "^5.0.2"
|
||||
fuzzywuzzy = "^0.18.0"
|
||||
peewee = "^3.13.3"
|
||||
PyGObject = "^3.38.0"
|
||||
python-dateutil = "^2.8.1"
|
||||
python-Levenshtein = "^0.12.0"
|
||||
python-mpv = "^0.5.2"
|
||||
requests = "^2.24.0"
|
||||
semver = "^2.10.2"
|
||||
bottle = {version = "^0.12.18", optional = true}
|
||||
keyring = {version = "^21.4.0", optional = true}
|
||||
pychromecast = {version = "^7.3.0", optional = true}
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
black = "^20.8b1"
|
||||
docutils = "^0.16"
|
||||
flake8 = "^3.8.3"
|
||||
flake8-annotations = "^2.4.0"
|
||||
flake8-bugbear = "^20.1.4"
|
||||
flake8-comprehensions = "^3.2.3"
|
||||
flake8-import-order = "^0.18.1"
|
||||
flake8-pep3101 = "^1.3.0"
|
||||
flake8-print = "^3.1.4"
|
||||
mypy = "^0.782"
|
||||
pytest-cov = "^2.10.1"
|
||||
termcolor = "^1.1.0"
|
||||
requirements-parser = "^0.2.0"
|
||||
|
||||
[tool.poetry.extras]
|
||||
chromecast = ["pychromecast"]
|
||||
keyring = ["keyring"]
|
||||
server = ["bottle"]
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry>=0.12"]
|
||||
build-backend = "poetry.masonry.api"
|
||||
|
||||
[tool.black]
|
||||
exclude = '''
|
||||
(
|
||||
|
@@ -4,7 +4,7 @@ exclude = .git,__pycache__,build,dist,flatpak,.venv
|
||||
max-line-length = 88
|
||||
suppress-none-returning = True
|
||||
suppress-dummy-args = True
|
||||
application-import-names = sublime
|
||||
application-import-names = sublime_music
|
||||
import-order-style = edited
|
||||
|
||||
[mypy-bottle]
|
||||
@@ -58,7 +58,7 @@ addopts =
|
||||
--doctest-modules
|
||||
--ignore-glob='flatpak'
|
||||
--ignore-glob='cicd'
|
||||
--cov=sublime
|
||||
--cov=sublime_music
|
||||
--cov-report html
|
||||
--cov-report term
|
||||
--no-cov-on-fail
|
||||
|
20
setup.py
@@ -8,19 +8,19 @@ with open(here.joinpath("README.rst"), encoding="utf-8") as f:
|
||||
long_description = f.read()
|
||||
|
||||
# Find the version
|
||||
with open(here.joinpath("sublime", "__init__.py")) as f:
|
||||
with open(here.joinpath("sublime_music", "__init__.py")) as f:
|
||||
for line in f:
|
||||
if line.startswith("__version__"):
|
||||
version = eval(line.split()[-1])
|
||||
break
|
||||
|
||||
package_data_dirs = [
|
||||
here.joinpath("sublime", "adapters", "icons"),
|
||||
here.joinpath("sublime", "adapters", "images"),
|
||||
here.joinpath("sublime", "adapters", "subsonic", "icons"),
|
||||
here.joinpath("sublime", "dbus", "mpris_specs"),
|
||||
here.joinpath("sublime", "ui", "icons"),
|
||||
here.joinpath("sublime", "ui", "images"),
|
||||
here.joinpath("sublime_music", "adapters", "icons"),
|
||||
here.joinpath("sublime_music", "adapters", "images"),
|
||||
here.joinpath("sublime_music", "adapters", "subsonic", "icons"),
|
||||
here.joinpath("sublime_music", "dbus", "mpris_specs"),
|
||||
here.joinpath("sublime_music", "ui", "icons"),
|
||||
here.joinpath("sublime_music", "ui", "images"),
|
||||
]
|
||||
package_data_files = []
|
||||
for data_dir in package_data_dirs:
|
||||
@@ -28,7 +28,7 @@ for data_dir in package_data_dirs:
|
||||
package_data_files.append(str(file))
|
||||
|
||||
setup(
|
||||
name="sublime-music",
|
||||
name="sublime_music",
|
||||
version=version,
|
||||
url="https://gitlab.com/sublime-music/sublime-music",
|
||||
description="A native GTK *sonic client.",
|
||||
@@ -51,7 +51,7 @@ setup(
|
||||
],
|
||||
keywords="airsonic subsonic libresonic gonic music",
|
||||
packages=find_packages(exclude=["tests"]),
|
||||
package_data={"sublime": ["ui/app_styles.css", *package_data_files]},
|
||||
package_data={"sublime_music": ["ui/app_styles.css", *package_data_files]},
|
||||
install_requires=[
|
||||
"bleach",
|
||||
"dataclasses-json",
|
||||
@@ -75,5 +75,5 @@ setup(
|
||||
# "scripts" keyword. Entry points provide cross-platform support and
|
||||
# allow pip to create the appropriate form of executable for the target
|
||||
# platform.
|
||||
entry_points={"console_scripts": ["sublime-music=sublime.__main__:main"]},
|
||||
entry_points={"console_scripts": ["sublime-music=sublime_music.__main__:main"]},
|
||||
)
|
||||
|
@@ -89,7 +89,8 @@
|
||||
<update_contact>me_AT_sumnerevans.com</update_contact>
|
||||
|
||||
<releases>
|
||||
<release version="0.10.2" date="2020-06-07"></release>
|
||||
<release version="0.11.9" date="2020-09-22"></release>
|
||||
<release version="0.10.3" date="2020-06-07"></release>
|
||||
<release version="0.10.2" date="2020-06-07"></release>
|
||||
</releases>
|
||||
</component>
|
||||
|
@@ -9,8 +9,9 @@ import gi
|
||||
gi.require_version("Gtk", "3.0")
|
||||
from gi.repository import Gtk # noqa: F401
|
||||
|
||||
import sublime
|
||||
from sublime.app import SublimeMusicApp
|
||||
import sublime_music
|
||||
|
||||
from .app import SublimeMusicApp
|
||||
|
||||
|
||||
def main():
|
||||
@@ -33,7 +34,7 @@ def main():
|
||||
|
||||
args, unknown_args = parser.parse_known_args()
|
||||
if args.version:
|
||||
print(f"Sublime Music v{sublime.__version__}") # noqa: T001
|
||||
print(f"Sublime Music v{sublime_music.__version__}") # noqa: T001
|
||||
return
|
||||
|
||||
min_log_level = getattr(logging, args.loglevel.upper(), None)
|
@@ -569,33 +569,34 @@ class Adapter(abc.ABC):
|
||||
"""
|
||||
Get a list of all of the playlists known by the adapter.
|
||||
|
||||
:returns: A list of all of the :class:`sublime.adapter.api_objects.Playlist`
|
||||
objects known to the adapter.
|
||||
:returns: A list of all of the
|
||||
:class:`sublime_music.adapter.api_objects.Playlist` objects known to the
|
||||
adapter.
|
||||
"""
|
||||
raise self._check_can_error("get_playlists")
|
||||
|
||||
def get_playlist_details(self, playlist_id: str,) -> Playlist:
|
||||
def get_playlist_details(self, playlist_id: str) -> Playlist:
|
||||
"""
|
||||
Get the details for the given ``playlist_id``. If the playlist_id does not
|
||||
exist, then this function should throw an exception.
|
||||
|
||||
:param playlist_id: The ID of the playlist to retrieve.
|
||||
:returns: A :class:`sublime.adapter.api_objects.Play` object for the given
|
||||
:returns: A :class:`sublime_music.adapter.api_objects.Play` object for the given
|
||||
playlist.
|
||||
"""
|
||||
raise self._check_can_error("get_playlist_details")
|
||||
|
||||
def create_playlist(
|
||||
self, name: str, songs: Sequence[Song] = None,
|
||||
self, name: str, songs: Sequence[Song] = None
|
||||
) -> Optional[Playlist]:
|
||||
"""
|
||||
Creates a playlist of the given name with the given songs.
|
||||
|
||||
:param name: The human-readable name of the playlist.
|
||||
:param songs: A list of songs that should be included in the playlist.
|
||||
:returns: A :class:`sublime.adapter.api_objects.Playlist` object for the created
|
||||
playlist. If getting this information will incurr network overhead, then
|
||||
just return ``None``.
|
||||
:returns: A :class:`sublime_music.adapter.api_objects.Playlist` object for the
|
||||
created playlist. If getting this information will incurr network overhead,
|
||||
then just return ``None``.
|
||||
"""
|
||||
raise self._check_can_error("create_playlist")
|
||||
|
||||
@@ -618,8 +619,8 @@ class Adapter(abc.ABC):
|
||||
shared/public vs. not shared/private playlists concept, setting this to
|
||||
``True`` will make the playlist shared/public.
|
||||
:param song_ids: A list of song IDs that should be included in the playlist.
|
||||
:returns: A :class:`sublime.adapter.api_objects.Playlist` object for the updated
|
||||
playlist.
|
||||
:returns: A :class:`sublime_music.adapter.api_objects.Playlist` object for the
|
||||
updated playlist.
|
||||
"""
|
||||
raise self._check_can_error("update_playlist")
|
||||
|
||||
@@ -671,7 +672,7 @@ class Adapter(abc.ABC):
|
||||
Get the details for a given song ID.
|
||||
|
||||
:param song_id: The ID of the song to get the details for.
|
||||
:returns: The :class:`sublime.adapters.api_objects.Song`.
|
||||
:returns: The :class:`sublime_music.adapters.api_objects.Song`.
|
||||
"""
|
||||
raise self._check_can_error("get_song_details")
|
||||
|
||||
@@ -679,7 +680,7 @@ class Adapter(abc.ABC):
|
||||
"""
|
||||
Scrobble the given song.
|
||||
|
||||
:params song: The :class:`sublime.adapters.api_objects.Song` to scrobble.
|
||||
:params song: The :class:`sublime_music.adapters.api_objects.Song` to scrobble.
|
||||
"""
|
||||
raise self._check_can_error("scrobble_song")
|
||||
|
||||
@@ -687,7 +688,7 @@ class Adapter(abc.ABC):
|
||||
"""
|
||||
Get a list of all of the artists known to the adapter.
|
||||
|
||||
:returns: A list of all of the :class:`sublime.adapter.api_objects.Artist`
|
||||
:returns: A list of all of the :class:`sublime_music.adapter.api_objects.Artist`
|
||||
objects known to the adapter.
|
||||
"""
|
||||
raise self._check_can_error("get_artists")
|
||||
@@ -697,7 +698,7 @@ class Adapter(abc.ABC):
|
||||
Get the details for the given artist ID.
|
||||
|
||||
:param artist_id: The ID of the artist to get the details for.
|
||||
:returns: The :classs`sublime.adapters.api_objects.Artist`
|
||||
:returns: The :classs`sublime_music.adapters.api_objects.Artist`
|
||||
"""
|
||||
raise self._check_can_error("get_artist")
|
||||
|
||||
@@ -723,7 +724,7 @@ class Adapter(abc.ABC):
|
||||
|
||||
:param query: An :class:`AlbumSearchQuery` object representing the types of
|
||||
albums to return.
|
||||
:returns: A list of all of the :class:`sublime.adapter.api_objects.Album`
|
||||
:returns: A list of all of the :class:`sublime_music.adapter.api_objects.Album`
|
||||
objects known to the adapter that match the query.
|
||||
"""
|
||||
raise self._check_can_error("get_albums")
|
||||
@@ -733,7 +734,7 @@ class Adapter(abc.ABC):
|
||||
Get the details for the given album ID.
|
||||
|
||||
:param album_id: The ID of the album to get the details for.
|
||||
:returns: The :classs`sublime.adapters.api_objects.Album`
|
||||
:returns: The :classs`sublime_music.adapters.api_objects.Album`
|
||||
"""
|
||||
raise self._check_can_error("get_album")
|
||||
|
||||
@@ -747,8 +748,9 @@ class Adapter(abc.ABC):
|
||||
:param directory_id: The directory to retrieve. If the special value ``"root"``
|
||||
is given, the adapter should list all of the directories at the root of the
|
||||
filesystem tree.
|
||||
:returns: A list of the :class:`sublime.adapter.api_objects.Directory` and
|
||||
:class:`sublime.adapter.api_objects.Song` objects in the given directory.
|
||||
:returns: A list of the :class:`sublime_music.adapter.api_objects.Directory` and
|
||||
:class:`sublime_music.adapter.api_objects.Song` objects in the given
|
||||
directory.
|
||||
"""
|
||||
raise self._check_can_error("get_directory")
|
||||
|
||||
@@ -756,7 +758,7 @@ class Adapter(abc.ABC):
|
||||
"""
|
||||
Get a list of the genres known to the adapter.
|
||||
|
||||
:returns: A list of all of the :classs`sublime.adapter.api_objects.Genre`
|
||||
:returns: A list of all of the :classs`sublime_music.adapter.api_objects.Genre`
|
||||
objects known to the adapter.
|
||||
"""
|
||||
raise self._check_can_error("get_genres")
|
||||
@@ -767,7 +769,7 @@ class Adapter(abc.ABC):
|
||||
the play queue from the cloud.
|
||||
|
||||
:returns: The cloud-saved play queue as a
|
||||
:class:`sublime.adapter.api_objects.PlayQueue` object.
|
||||
:class:`sublime_music.adapter.api_objects.PlayQueue` object.
|
||||
"""
|
||||
raise self._check_can_error("get_play_queue")
|
||||
|
||||
@@ -791,7 +793,7 @@ class Adapter(abc.ABC):
|
||||
Return search results fro the given query.
|
||||
|
||||
:param query: The query string.
|
||||
:returns: A :class:`sublime.adapters.api_objects.SearchResult` object
|
||||
:returns: A :class:`sublime_music.adapters.api_objects.SearchResult` object
|
||||
representing the results of the search.
|
||||
"""
|
||||
raise self._check_can_error("search")
|
@@ -178,7 +178,9 @@ class SearchResult:
|
||||
_S = TypeVar("_S")
|
||||
|
||||
def _to_result(
|
||||
self, it: Dict[str, _S], transform: Callable[[_S], Tuple[Optional[str], ...]],
|
||||
self,
|
||||
it: Dict[str, _S],
|
||||
transform: Callable[[_S], Tuple[Optional[str], ...]],
|
||||
) -> List[_S]:
|
||||
assert self.query
|
||||
all_results = []
|
@@ -111,7 +111,10 @@ class ConfigureServerForm(Gtk.Box):
|
||||
self.is_networked = is_networked
|
||||
|
||||
content_grid = Gtk.Grid(
|
||||
column_spacing=10, row_spacing=5, margin_left=10, margin_right=10,
|
||||
column_spacing=10,
|
||||
row_spacing=5,
|
||||
margin_left=10,
|
||||
margin_right=10,
|
||||
)
|
||||
advanced_grid = Gtk.Grid(column_spacing=10, row_spacing=10)
|
||||
|
||||
@@ -175,7 +178,8 @@ class ConfigureServerForm(Gtk.Box):
|
||||
|
||||
if cpd.helptext:
|
||||
help_icon = Gtk.Image.new_from_icon_name(
|
||||
"help-about", Gtk.IconSize.BUTTON,
|
||||
"help-about",
|
||||
Gtk.IconSize.BUTTON,
|
||||
)
|
||||
help_icon.get_style_context().add_class("configure-form-help-icon")
|
||||
help_icon.set_tooltip_markup(cpd.helptext)
|
||||
@@ -248,7 +252,7 @@ class ConfigureServerForm(Gtk.Box):
|
||||
def _set_verification_status(
|
||||
self, verifying: bool, is_valid: bool = False, error_text: str = None
|
||||
):
|
||||
from sublime.ui import util
|
||||
from sublime_music.ui import util
|
||||
|
||||
if verifying:
|
||||
if not self.verifying_in_progress:
|
||||
@@ -300,7 +304,7 @@ class ConfigureServerForm(Gtk.Box):
|
||||
def _verify_config(self, ratchet: int):
|
||||
self.emit("config-valid-changed", False)
|
||||
|
||||
from sublime.adapters import Result
|
||||
from sublime_music.adapters import Result
|
||||
|
||||
if self.required_config_parameter_keys.issubset(set(self.config_store.keys())):
|
||||
if self._verification_status_ratchet != ratchet:
|
@@ -9,7 +9,7 @@ from typing import Any, cast, Dict, Iterable, Optional, Sequence, Set, Tuple
|
||||
from gi.repository import Gtk
|
||||
from peewee import fn, prefetch
|
||||
|
||||
from sublime.adapters import api_objects as API
|
||||
from sublime_music.adapters import api_objects as API
|
||||
|
||||
from . import models
|
||||
from .. import (
|
||||
@@ -60,9 +60,7 @@ class FilesystemAdapter(CachingAdapter):
|
||||
def migrate_configuration(config_store: ConfigurationStore):
|
||||
pass
|
||||
|
||||
def __init__(
|
||||
self, config: dict, data_directory: Path, is_cache: bool = False,
|
||||
):
|
||||
def __init__(self, config: dict, data_directory: Path, is_cache: bool = False):
|
||||
self.data_directory = data_directory
|
||||
self.cover_art_dir = self.data_directory.joinpath("cover_art")
|
||||
self.music_dir = self.data_directory.joinpath("music")
|
||||
@@ -311,7 +309,9 @@ class FilesystemAdapter(CachingAdapter):
|
||||
|
||||
def get_song_details(self, song_id: str) -> models.Song:
|
||||
return self._get_object_details(
|
||||
models.Song, song_id, CachingAdapter.CachedDataKey.SONG,
|
||||
models.Song,
|
||||
song_id,
|
||||
CachingAdapter.CachedDataKey.SONG,
|
||||
)
|
||||
|
||||
def get_artists(self, ignore_cache_miss: bool = False) -> Sequence[API.Artist]:
|
||||
@@ -429,7 +429,8 @@ class FilesystemAdapter(CachingAdapter):
|
||||
),
|
||||
)
|
||||
search_result.add_results(
|
||||
"playlists", self.get_playlists(ignore_cache_miss=True),
|
||||
"playlists",
|
||||
self.get_playlists(ignore_cache_miss=True),
|
||||
)
|
||||
return search_result
|
||||
|
||||
@@ -439,7 +440,10 @@ class FilesystemAdapter(CachingAdapter):
|
||||
return hashlib.sha1(bytes(string, "utf8")).hexdigest()
|
||||
|
||||
def ingest_new_data(
|
||||
self, data_key: CachingAdapter.CachedDataKey, param: Optional[str], data: Any,
|
||||
self,
|
||||
data_key: CachingAdapter.CachedDataKey,
|
||||
param: Optional[str],
|
||||
data: Any,
|
||||
):
|
||||
assert self.is_cache, "FilesystemAdapter is not in cache mode!"
|
||||
|
||||
@@ -809,7 +813,9 @@ class FilesystemAdapter(CachingAdapter):
|
||||
)
|
||||
song_data["_cover_art"] = (
|
||||
self._do_ingest_new_data(
|
||||
KEYS.COVER_ART_FILE, api_song.cover_art, data=None,
|
||||
KEYS.COVER_ART_FILE,
|
||||
api_song.cover_art,
|
||||
data=None,
|
||||
)
|
||||
if api_song.cover_art
|
||||
else None
|
||||
@@ -863,7 +869,9 @@ class FilesystemAdapter(CachingAdapter):
|
||||
return return_val if return_val is not None else cache_info
|
||||
|
||||
def _do_invalidate_data(
|
||||
self, data_key: CachingAdapter.CachedDataKey, param: Optional[str],
|
||||
self,
|
||||
data_key: CachingAdapter.CachedDataKey,
|
||||
param: Optional[str],
|
||||
):
|
||||
logging.debug(f"_do_invalidate_data param={param} data_key={data_key}")
|
||||
models.CacheInfo.update({"valid": False}).where(
|
||||
@@ -899,7 +907,8 @@ class FilesystemAdapter(CachingAdapter):
|
||||
):
|
||||
logging.debug(f"_do_delete_data param={param} data_key={data_key}")
|
||||
cache_info = models.CacheInfo.get_or_none(
|
||||
models.CacheInfo.cache_key == data_key, models.CacheInfo.parameter == param,
|
||||
models.CacheInfo.cache_key == data_key,
|
||||
models.CacheInfo.parameter == param,
|
||||
)
|
||||
|
||||
if data_key == KEYS.COVER_ART_FILE:
|
@@ -14,7 +14,7 @@ from peewee import (
|
||||
TextField,
|
||||
)
|
||||
|
||||
from sublime.adapters.adapter_base import CachingAdapter
|
||||
from sublime_music.adapters.adapter_base import CachingAdapter
|
||||
|
||||
|
||||
# Custom Fields
|
||||
@@ -70,7 +70,10 @@ class SortedManyToManyQuery(ManyToManyQuery):
|
||||
|
||||
class SortedManyToManyFieldAccessor(ManyToManyFieldAccessor):
|
||||
def __get__(
|
||||
self, instance: Model, instance_type: Any = None, force_query: bool = False,
|
||||
self,
|
||||
instance: Model,
|
||||
instance_type: Any = None,
|
||||
force_query: bool = False,
|
||||
):
|
||||
if instance is not None:
|
||||
if not force_query and self.src_fk.backref != "+":
|
Before Width: | Height: | Size: 348 B After Width: | Height: | Size: 348 B |
Before Width: | Height: | Size: 299 B After Width: | Height: | Size: 299 B |
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
@@ -30,7 +30,7 @@ from typing import (
|
||||
|
||||
import requests
|
||||
|
||||
from sublime.config import ProviderConfiguration
|
||||
from sublime_music.config import ProviderConfiguration
|
||||
|
||||
from .adapter_base import (
|
||||
Adapter,
|
||||
@@ -273,7 +273,7 @@ class AdapterManager:
|
||||
config: Any,
|
||||
on_song_download_progress: Callable[[Any, str, DownloadProgress], None],
|
||||
):
|
||||
from sublime.config import AppConfiguration
|
||||
from sublime_music.config import AppConfiguration
|
||||
|
||||
assert isinstance(config, AppConfiguration)
|
||||
|
||||
@@ -493,7 +493,8 @@ class AdapterManager:
|
||||
# Everything succeeded.
|
||||
if expected_size_exists:
|
||||
AdapterManager._instance.song_download_progress(
|
||||
id, DownloadProgress(DownloadProgress.Type.DONE),
|
||||
id,
|
||||
DownloadProgress(DownloadProgress.Type.DONE),
|
||||
)
|
||||
except Exception as e:
|
||||
if expected_size_exists and not download_cancelled:
|
||||
@@ -872,7 +873,9 @@ class AdapterManager:
|
||||
# Create a download result.
|
||||
future = AdapterManager._create_download_result(
|
||||
AdapterManager._instance.ground_truth_adapter.get_cover_art_uri(
|
||||
cover_art_id, AdapterManager._get_networked_scheme(), size=size,
|
||||
cover_art_id,
|
||||
AdapterManager._get_networked_scheme(),
|
||||
size=size,
|
||||
),
|
||||
cover_art_id,
|
||||
before_download,
|
||||
@@ -963,7 +966,8 @@ class AdapterManager:
|
||||
):
|
||||
AdapterManager._instance.download_limiter_semaphore.release()
|
||||
AdapterManager._instance.song_download_progress(
|
||||
song_id, DownloadProgress(DownloadProgress.Type.CANCELLED),
|
||||
song_id,
|
||||
DownloadProgress(DownloadProgress.Type.CANCELLED),
|
||||
)
|
||||
return Result("", is_download=True)
|
||||
|
||||
@@ -977,7 +981,8 @@ class AdapterManager:
|
||||
)
|
||||
AdapterManager._instance.download_limiter_semaphore.release()
|
||||
AdapterManager._instance.song_download_progress(
|
||||
song_id, DownloadProgress(DownloadProgress.Type.DONE),
|
||||
song_id,
|
||||
DownloadProgress(DownloadProgress.Type.DONE),
|
||||
)
|
||||
return Result("", is_download=True)
|
||||
except CacheMissError:
|
||||
@@ -1033,7 +1038,8 @@ class AdapterManager:
|
||||
for song_id in song_ids:
|
||||
# Everything succeeded.
|
||||
AdapterManager._instance.song_download_progress(
|
||||
song_id, DownloadProgress(DownloadProgress.Type.QUEUED),
|
||||
song_id,
|
||||
DownloadProgress(DownloadProgress.Type.QUEUED),
|
||||
)
|
||||
|
||||
for song_id in song_ids:
|
||||
@@ -1057,7 +1063,8 @@ class AdapterManager:
|
||||
# Alert the UI that the downloads are cancelled.
|
||||
for song_id in song_ids:
|
||||
AdapterManager._instance.song_download_progress(
|
||||
song_id, DownloadProgress(DownloadProgress.Type.CANCELLED),
|
||||
song_id,
|
||||
DownloadProgress(DownloadProgress.Type.CANCELLED),
|
||||
)
|
||||
|
||||
return Result(do_batch_download_songs, is_download=True, on_cancel=on_cancel)
|
||||
@@ -1070,7 +1077,8 @@ class AdapterManager:
|
||||
)
|
||||
for song_id in song_ids:
|
||||
AdapterManager._instance.song_download_progress(
|
||||
song_id, DownloadProgress(DownloadProgress.Type.CANCELLED),
|
||||
song_id,
|
||||
DownloadProgress(DownloadProgress.Type.CANCELLED),
|
||||
)
|
||||
if AdapterManager._song_download_jobs.get(song_id):
|
||||
AdapterManager._song_download_jobs[song_id].cancel()
|
||||
@@ -1360,8 +1368,10 @@ class AdapterManager:
|
||||
return True
|
||||
|
||||
try:
|
||||
ground_truth_search_results = AdapterManager._instance.ground_truth_adapter.search( # noqa: E501
|
||||
query
|
||||
ground_truth_search_results = (
|
||||
AdapterManager._instance.ground_truth_adapter.search( # noqa: E501
|
||||
query
|
||||
)
|
||||
)
|
||||
search_result.update(ground_truth_search_results)
|
||||
search_callback(search_result)
|
@@ -27,7 +27,7 @@ import requests
|
||||
import semver
|
||||
from gi.repository import Gtk
|
||||
|
||||
from sublime.util import resolve_path
|
||||
from sublime_music.util import resolve_path
|
||||
|
||||
from .api_objects import Directory, Response
|
||||
from .. import (
|
||||
@@ -261,7 +261,9 @@ class SubsonicAdapter(Adapter):
|
||||
|
||||
# Try to ping the server.
|
||||
self._get_json(
|
||||
self._make_url("ping"), timeout=timeout, is_exponential_backoff_ping=True,
|
||||
self._make_url("ping"),
|
||||
timeout=timeout,
|
||||
is_exponential_backoff_ping=True,
|
||||
)
|
||||
|
||||
def on_offline_mode_change(self, offline_mode: bool):
|
||||
@@ -388,7 +390,10 @@ class SubsonicAdapter(Adapter):
|
||||
result = self._get_mock_data()
|
||||
else:
|
||||
result = requests.get(
|
||||
url, params=params, verify=self.verify_cert, timeout=timeout,
|
||||
url,
|
||||
params=params,
|
||||
verify=self.verify_cert,
|
||||
timeout=timeout,
|
||||
)
|
||||
|
||||
if result.status_code != 200:
|
||||
@@ -469,10 +474,10 @@ class SubsonicAdapter(Adapter):
|
||||
raise data
|
||||
if hasattr(data, "__next__"):
|
||||
if d := next(data):
|
||||
logging.info("MOCK DATA", d)
|
||||
logging.info("MOCK DATA: %s", d)
|
||||
return MockResult(d)
|
||||
|
||||
logging.info("MOCK DATA", data)
|
||||
logging.info("MOCK DATA: %s", data)
|
||||
return MockResult(data)
|
||||
|
||||
self._get_mock_data = get_mock_data
|
||||
@@ -490,7 +495,7 @@ class SubsonicAdapter(Adapter):
|
||||
return result
|
||||
|
||||
def create_playlist(
|
||||
self, name: str, songs: Sequence[API.Song] = None,
|
||||
self, name: str, songs: Sequence[API.Song] = None
|
||||
) -> Optional[API.Playlist]:
|
||||
return self._get_json(
|
||||
self._make_url("createPlaylist"),
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
@@ -107,7 +107,8 @@ class SublimeMusicApp(Gtk.Application):
|
||||
add_action("go-online", self.on_go_online)
|
||||
add_action("refresh-devices", self.on_refresh_devices)
|
||||
add_action(
|
||||
"refresh-window", lambda *a: self.on_refresh_window(None, {}, True),
|
||||
"refresh-window",
|
||||
lambda *a: self.on_refresh_window(None, {}, True),
|
||||
)
|
||||
add_action("mute-toggle", self.on_mute_toggle)
|
||||
add_action(
|
||||
@@ -413,7 +414,8 @@ class SublimeMusicApp(Gtk.Application):
|
||||
# repeat song IDs.
|
||||
metadatas: Iterable[Any] = [
|
||||
self.dbus_manager.get_mpris_metadata(
|
||||
i, self.app_config.state.play_queue,
|
||||
i,
|
||||
self.app_config.state.play_queue,
|
||||
)
|
||||
for i in range(len(self.app_config.state.play_queue))
|
||||
]
|
||||
@@ -457,7 +459,10 @@ class SublimeMusicApp(Gtk.Application):
|
||||
)
|
||||
|
||||
def get_playlists(
|
||||
index: int, max_count: int, order: str, reverse_order: bool,
|
||||
index: int,
|
||||
max_count: int,
|
||||
order: str,
|
||||
reverse_order: bool,
|
||||
) -> GLib.Variant:
|
||||
playlists_result = AdapterManager.get_playlists()
|
||||
if not playlists_result.data_is_available:
|
||||
@@ -473,12 +478,15 @@ class SublimeMusicApp(Gtk.Application):
|
||||
"Modified": lambda p: p.changed,
|
||||
}
|
||||
playlists.sort(
|
||||
key=sorters.get(order, lambda p: p), reverse=reverse_order,
|
||||
key=sorters.get(order, lambda p: p),
|
||||
reverse=reverse_order,
|
||||
)
|
||||
|
||||
def make_playlist_tuple(p: Playlist) -> GLib.Variant:
|
||||
cover_art_filename = AdapterManager.get_cover_art_uri(
|
||||
p.cover_art, "file", allow_download=False,
|
||||
p.cover_art,
|
||||
"file",
|
||||
allow_download=False,
|
||||
).result()
|
||||
return (f"/playlist/{p.id}", p.name, cover_art_filename or "")
|
||||
|
||||
@@ -570,9 +578,7 @@ class SublimeMusicApp(Gtk.Application):
|
||||
|
||||
# ########## ACTION HANDLERS ########## #
|
||||
@dbus_propagate()
|
||||
def on_refresh_window(
|
||||
self, _, state_updates: Dict[str, Any], force: bool = False,
|
||||
):
|
||||
def on_refresh_window(self, _, state_updates: Dict[str, Any], force: bool = False):
|
||||
if settings := state_updates.get("__settings__"):
|
||||
for k, v in settings.items():
|
||||
setattr(self.app_config, k, v)
|
||||
@@ -879,9 +885,7 @@ class SublimeMusicApp(Gtk.Application):
|
||||
return
|
||||
|
||||
self.app_config.state.current_song_index -= len(before_current)
|
||||
self.play_song(
|
||||
self.app_config.state.current_song_index, reset=True,
|
||||
)
|
||||
self.play_song(self.app_config.state.current_song_index, reset=True)
|
||||
else:
|
||||
self.app_config.state.current_song_index -= len(before_current)
|
||||
self.update_window()
|
||||
@@ -1001,7 +1005,8 @@ class SublimeMusicApp(Gtk.Application):
|
||||
|
||||
# ########## HELPER METHODS ########## #
|
||||
def show_configure_servers_dialog(
|
||||
self, provider_config: Optional[ProviderConfiguration] = None,
|
||||
self,
|
||||
provider_config: Optional[ProviderConfiguration] = None,
|
||||
):
|
||||
"""Show the Connect to Server dialog."""
|
||||
dialog = ConfigureProviderDialog(self.window, provider_config)
|
||||
@@ -1192,7 +1197,8 @@ class SublimeMusicApp(Gtk.Application):
|
||||
if artist := song.artist:
|
||||
notification_lines.append(bleach.clean(artist.name))
|
||||
song_notification = Notify.Notification.new(
|
||||
song.title, "\n".join(notification_lines),
|
||||
song.title,
|
||||
"\n".join(notification_lines),
|
||||
)
|
||||
song_notification.add_action(
|
||||
"clicked",
|
@@ -8,8 +8,8 @@ from typing import Any, cast, Dict, Optional, Tuple, Type, Union
|
||||
import dataclasses_json
|
||||
from dataclasses_json import config, DataClassJsonMixin
|
||||
|
||||
from sublime.adapters import ConfigurationStore
|
||||
from sublime.ui.state import UIState
|
||||
from .adapters import ConfigurationStore
|
||||
from .ui.state import UIState
|
||||
|
||||
|
||||
# JSON decoder and encoder translations
|
||||
@@ -18,9 +18,7 @@ def encode_path(path: Path) -> str:
|
||||
|
||||
|
||||
dataclasses_json.cfg.global_config.decoders[Path] = Path
|
||||
dataclasses_json.cfg.global_config.decoders[
|
||||
Optional[Path] # type: ignore
|
||||
] = (
|
||||
dataclasses_json.cfg.global_config.decoders[Optional[Path]] = ( # type: ignore
|
||||
lambda p: Path(p) if p else None
|
||||
)
|
||||
|
||||
@@ -87,7 +85,7 @@ def encode_providers(
|
||||
def decode_providers(
|
||||
providers_dict: Dict[str, Dict[str, Any]]
|
||||
) -> Dict[str, ProviderConfiguration]:
|
||||
from sublime.adapters import AdapterManager
|
||||
from sublime_music.adapters import AdapterManager
|
||||
|
||||
def find_adapter_type(type_name: str) -> Type:
|
||||
for available_adapter in AdapterManager.available_adapters:
|
@@ -137,7 +137,13 @@ class DBusManager:
|
||||
|
||||
return
|
||||
self.do_on_method_call(
|
||||
connection, sender, path, interface, method, params, invocation,
|
||||
connection,
|
||||
sender,
|
||||
path,
|
||||
interface,
|
||||
method,
|
||||
params,
|
||||
invocation,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
@@ -153,7 +159,8 @@ class DBusManager:
|
||||
|
||||
if type(value) == dict:
|
||||
return GLib.Variant(
|
||||
"a{sv}", {k: DBusManager.to_variant(v) for k, v in value.items()},
|
||||
"a{sv}",
|
||||
{k: DBusManager.to_variant(v) for k, v in value.items()},
|
||||
)
|
||||
|
||||
variant_type = {list: "as", str: "s", int: "i", float: "d", bool: "b"}.get(
|
||||
@@ -230,7 +237,8 @@ class DBusManager:
|
||||
"Rate": 1.0,
|
||||
"Shuffle": state.shuffle_on,
|
||||
"Metadata": self.get_mpris_metadata(
|
||||
state.current_song_index, state.play_queue,
|
||||
state.current_song_index,
|
||||
state.play_queue,
|
||||
)
|
||||
if state.current_song
|
||||
else {},
|
@@ -4,7 +4,7 @@ from datetime import timedelta
|
||||
from enum import Enum
|
||||
from typing import Callable, Dict, Optional, Set, Tuple, Type, Union
|
||||
|
||||
from sublime.adapters.api_objects import Song
|
||||
from ..adapters.api_objects import Song
|
||||
|
||||
|
||||
@dataclass
|
@@ -12,10 +12,9 @@ from uuid import UUID
|
||||
|
||||
from gi.repository import GLib
|
||||
|
||||
from sublime.adapters import AdapterManager
|
||||
from sublime.adapters.api_objects import Song
|
||||
|
||||
from .base import Player, PlayerDeviceEvent, PlayerEvent
|
||||
from ..adapters import AdapterManager
|
||||
from ..adapters.api_objects import Song
|
||||
|
||||
try:
|
||||
import pychromecast
|
@@ -2,11 +2,10 @@ import logging
|
||||
from datetime import timedelta
|
||||
from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Type, Union
|
||||
|
||||
from sublime.adapters.api_objects import Song
|
||||
|
||||
from .base import PlayerDeviceEvent, PlayerEvent
|
||||
from .chromecast import ChromecastPlayer # noqa: F401
|
||||
from .mpv import MPVPlayer # noqa: F401
|
||||
from ..adapters.api_objects import Song
|
||||
|
||||
|
||||
class PlayerManager:
|
||||
@@ -19,7 +18,8 @@ class PlayerManager:
|
||||
]:
|
||||
"""
|
||||
:returns: Dictionary of the name of the player -> option configs (see
|
||||
:class:`sublime.players.base.Player.get_configuration_options` for details).
|
||||
:class:`sublime_music.players.base.Player.get_configuration_options` for
|
||||
details).
|
||||
"""
|
||||
return {
|
||||
p.name: p.get_configuration_options()
|
||||
@@ -67,7 +67,8 @@ class PlayerManager:
|
||||
}
|
||||
|
||||
def change_settings(
|
||||
self, config: Dict[str, Dict[str, Union[Type, Tuple[str, ...]]]],
|
||||
self,
|
||||
config: Dict[str, Dict[str, Union[Type, Tuple[str, ...]]]],
|
||||
):
|
||||
self.config = config
|
||||
for player_type, player in self.players.items():
|
@@ -4,9 +4,8 @@ from typing import Callable, cast, Dict, Optional, Tuple, Type, Union
|
||||
|
||||
import mpv
|
||||
|
||||
from sublime.adapters.api_objects import Song
|
||||
|
||||
from .base import Player, PlayerDeviceEvent, PlayerEvent
|
||||
from ..adapters.api_objects import Song
|
||||
|
||||
REPLAY_GAIN_KEY = "Replay Gain"
|
||||
|
@@ -6,16 +6,16 @@ from typing import Any, Callable, cast, Iterable, List, Optional, Tuple
|
||||
|
||||
from gi.repository import Gdk, Gio, GLib, GObject, Gtk, Pango
|
||||
|
||||
from sublime.adapters import (
|
||||
from ..adapters import (
|
||||
AdapterManager,
|
||||
AlbumSearchQuery,
|
||||
api_objects as API,
|
||||
CacheMissError,
|
||||
Result,
|
||||
)
|
||||
from sublime.config import AppConfiguration
|
||||
from sublime.ui import util
|
||||
from sublime.ui.common import AlbumWithSongs, IconButton, LoadError, SpinnerImage
|
||||
from ..config import AppConfiguration
|
||||
from ..ui import util
|
||||
from ..ui.common import AlbumWithSongs, IconButton, LoadError, SpinnerImage
|
||||
|
||||
|
||||
def _to_type(query_type: AlbumSearchQuery.Type) -> str:
|
||||
@@ -164,10 +164,12 @@ class AlbumsPanel(Gtk.Box):
|
||||
scrolled_window = Gtk.ScrolledWindow()
|
||||
self.grid = AlbumsGrid()
|
||||
self.grid.connect(
|
||||
"song-clicked", lambda _, *args: self.emit("song-clicked", *args),
|
||||
"song-clicked",
|
||||
lambda _, *args: self.emit("song-clicked", *args),
|
||||
)
|
||||
self.grid.connect(
|
||||
"refresh-window", lambda _, *args: self.emit("refresh-window", *args),
|
||||
"refresh-window",
|
||||
lambda _, *args: self.emit("refresh-window", *args),
|
||||
)
|
||||
self.grid.connect("cover-clicked", self.on_grid_cover_clicked)
|
||||
self.grid.connect("num-pages-changed", self.on_grid_num_pages_changed)
|
||||
@@ -195,7 +197,9 @@ class AlbumsPanel(Gtk.Box):
|
||||
return combo, store
|
||||
|
||||
def populate_genre_combo(
|
||||
self, app_config: AppConfiguration = None, force: bool = False,
|
||||
self,
|
||||
app_config: AppConfiguration = None,
|
||||
force: bool = False,
|
||||
):
|
||||
if not AdapterManager.can_get_genres():
|
||||
self.updating_query = False
|
||||
@@ -464,13 +468,17 @@ class AlbumsPanel(Gtk.Box):
|
||||
|
||||
def on_grid_cover_clicked(self, grid: Any, id: str):
|
||||
self.emit(
|
||||
"refresh-window", {"selected_album_id": id}, False,
|
||||
"refresh-window",
|
||||
{"selected_album_id": id},
|
||||
False,
|
||||
)
|
||||
|
||||
def on_show_count_dropdown_change(self, combo: Gtk.ComboBox):
|
||||
show_count = int(self.get_id(combo) or 30)
|
||||
self.emit(
|
||||
"refresh-window", {"album_page_size": show_count, "album_page": 0}, False,
|
||||
"refresh-window",
|
||||
{"album_page_size": show_count, "album_page": 0},
|
||||
False,
|
||||
)
|
||||
|
||||
def emit_if_not_updating(self, *args):
|
||||
@@ -483,7 +491,7 @@ class AlbumsGrid(Gtk.Overlay):
|
||||
"""Defines the albums panel."""
|
||||
|
||||
__gsignals__ = {
|
||||
"cover-clicked": (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE, (object,),),
|
||||
"cover-clicked": (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE, (object,)),
|
||||
"refresh-window": (
|
||||
GObject.SignalFlags.RUN_FIRST,
|
||||
GObject.TYPE_NONE,
|
||||
@@ -789,9 +797,7 @@ class AlbumsGrid(Gtk.Overlay):
|
||||
new_items_per_row = min((rect.width // 230), 7)
|
||||
if new_items_per_row != self.items_per_row:
|
||||
self.items_per_row = new_items_per_row
|
||||
self.detail_box_inner.set_size_request(
|
||||
self.items_per_row * 230 - 10, -1,
|
||||
)
|
||||
self.detail_box_inner.set_size_request(self.items_per_row * 230 - 10, -1)
|
||||
|
||||
self.reflow_grids(
|
||||
force_reload_from_master=True,
|
||||
@@ -899,10 +905,14 @@ class AlbumsGrid(Gtk.Overlay):
|
||||
# Just remove everything and re-add all of the items. It's not worth trying
|
||||
# to diff in this case.
|
||||
self.list_store_top.splice(
|
||||
0, len(self.list_store_top), window[:entries_before_fold],
|
||||
0,
|
||||
len(self.list_store_top),
|
||||
window[:entries_before_fold],
|
||||
)
|
||||
self.list_store_bottom.splice(
|
||||
0, len(self.list_store_bottom), window[entries_before_fold:],
|
||||
0,
|
||||
len(self.list_store_bottom),
|
||||
window[entries_before_fold:],
|
||||
)
|
||||
elif selected_index or entries_before_fold != self.page_size:
|
||||
# This case handles when the selection changes and the entries need to be
|
||||
@@ -940,7 +950,8 @@ class AlbumsGrid(Gtk.Overlay):
|
||||
model = self.list_store_top[relative_selected_index]
|
||||
detail_element = AlbumWithSongs(model.album, cover_art_size=300)
|
||||
detail_element.connect(
|
||||
"song-clicked", lambda _, *args: self.emit("song-clicked", *args),
|
||||
"song-clicked",
|
||||
lambda _, *args: self.emit("song-clicked", *args),
|
||||
)
|
||||
detail_element.connect("song-selected", lambda *a: None)
|
||||
|
@@ -5,15 +5,15 @@ from typing import cast, List, Sequence
|
||||
|
||||
from gi.repository import Gio, GLib, GObject, Gtk, Pango
|
||||
|
||||
from sublime.adapters import (
|
||||
from ..adapters import (
|
||||
AdapterManager,
|
||||
api_objects as API,
|
||||
CacheMissError,
|
||||
SongCacheStatus,
|
||||
)
|
||||
from sublime.config import AppConfiguration
|
||||
from sublime.ui import util
|
||||
from sublime.ui.common import AlbumWithSongs, IconButton, LoadError, SpinnerImage
|
||||
from ..config import AppConfiguration
|
||||
from ..ui import util
|
||||
from ..ui.common import AlbumWithSongs, IconButton, LoadError, SpinnerImage
|
||||
|
||||
|
||||
class ArtistsPanel(Gtk.Paned):
|
||||
@@ -40,10 +40,12 @@ class ArtistsPanel(Gtk.Paned):
|
||||
|
||||
self.artist_detail_panel = ArtistDetailPanel()
|
||||
self.artist_detail_panel.connect(
|
||||
"song-clicked", lambda _, *args: self.emit("song-clicked", *args),
|
||||
"song-clicked",
|
||||
lambda _, *args: self.emit("song-clicked", *args),
|
||||
)
|
||||
self.artist_detail_panel.connect(
|
||||
"refresh-window", lambda _, *args: self.emit("refresh-window", *args),
|
||||
"refresh-window",
|
||||
lambda _, *args: self.emit("refresh-window", *args),
|
||||
)
|
||||
self.pack2(self.artist_detail_panel, True, False)
|
||||
|
||||
@@ -315,7 +317,8 @@ class ArtistDetailPanel(Gtk.Box):
|
||||
self.album_list_scrolledwindow = Gtk.ScrolledWindow()
|
||||
self.albums_list = AlbumsListWithSongs()
|
||||
self.albums_list.connect(
|
||||
"song-clicked", lambda _, *args: self.emit("song-clicked", *args),
|
||||
"song-clicked",
|
||||
lambda _, *args: self.emit("song-clicked", *args),
|
||||
)
|
||||
self.album_list_scrolledwindow.add(self.albums_list)
|
||||
self.pack_start(self.album_list_scrolledwindow, True, True, 0)
|
||||
@@ -408,7 +411,9 @@ class ArtistDetailPanel(Gtk.Box):
|
||||
self.play_shuffle_buttons.show_all()
|
||||
|
||||
self.update_artist_artwork(
|
||||
artist.artist_image_url, force=force, order_token=order_token,
|
||||
artist.artist_image_url,
|
||||
force=force,
|
||||
order_token=order_token,
|
||||
)
|
||||
|
||||
for c in self.error_container.get_children():
|
||||
@@ -489,24 +494,31 @@ class ArtistDetailPanel(Gtk.Box):
|
||||
# =========================================================================
|
||||
def on_view_refresh_click(self, *args):
|
||||
self.update_artist_view(
|
||||
self.artist_id, force=True, order_token=self.update_order_token,
|
||||
self.artist_id,
|
||||
force=True,
|
||||
order_token=self.update_order_token,
|
||||
)
|
||||
|
||||
def on_download_all_click(self, _):
|
||||
AdapterManager.batch_download_songs(
|
||||
self.get_artist_song_ids(),
|
||||
before_download=lambda _: self.update_artist_view(
|
||||
self.artist_id, order_token=self.update_order_token,
|
||||
self.artist_id,
|
||||
order_token=self.update_order_token,
|
||||
),
|
||||
on_song_download_complete=lambda _: self.update_artist_view(
|
||||
self.artist_id, order_token=self.update_order_token,
|
||||
self.artist_id,
|
||||
order_token=self.update_order_token,
|
||||
),
|
||||
)
|
||||
|
||||
def on_play_all_clicked(self, _):
|
||||
songs = self.get_artist_song_ids()
|
||||
self.emit(
|
||||
"song-clicked", 0, songs, {"force_shuffle_state": False},
|
||||
"song-clicked",
|
||||
0,
|
||||
songs,
|
||||
{"force_shuffle_state": False},
|
||||
)
|
||||
|
||||
def on_shuffle_all_button(self, _):
|
||||
@@ -633,7 +645,8 @@ class AlbumsListWithSongs(Gtk.Overlay):
|
||||
for album in self.albums:
|
||||
album_with_songs = AlbumWithSongs(album, show_artist_name=False)
|
||||
album_with_songs.connect(
|
||||
"song-clicked", lambda _, *args: self.emit("song-clicked", *args),
|
||||
"song-clicked",
|
||||
lambda _, *args: self.emit("song-clicked", *args),
|
||||
)
|
||||
album_with_songs.connect("song-selected", self.on_song_selected)
|
||||
album_with_songs.show_all()
|
@@ -3,10 +3,10 @@ from typing import Any, cast, List, Optional, Tuple
|
||||
|
||||
from gi.repository import Gdk, Gio, GLib, GObject, Gtk, Pango
|
||||
|
||||
from sublime.adapters import AdapterManager, api_objects as API, CacheMissError, Result
|
||||
from sublime.config import AppConfiguration
|
||||
from sublime.ui import util
|
||||
from sublime.ui.common import IconButton, LoadError, SongListColumn
|
||||
from ..adapters import AdapterManager, api_objects as API, CacheMissError, Result
|
||||
from ..config import AppConfiguration
|
||||
from ..ui import util
|
||||
from ..ui.common import IconButton, LoadError, SongListColumn
|
||||
|
||||
|
||||
class BrowsePanel(Gtk.Overlay):
|
||||
@@ -37,10 +37,12 @@ class BrowsePanel(Gtk.Overlay):
|
||||
|
||||
self.root_directory_listing = ListAndDrilldown()
|
||||
self.root_directory_listing.connect(
|
||||
"song-clicked", lambda _, *args: self.emit("song-clicked", *args),
|
||||
"song-clicked",
|
||||
lambda _, *args: self.emit("song-clicked", *args),
|
||||
)
|
||||
self.root_directory_listing.connect(
|
||||
"refresh-window", lambda _, *args: self.emit("refresh-window", *args),
|
||||
"refresh-window",
|
||||
lambda _, *args: self.emit("refresh-window", *args),
|
||||
)
|
||||
window_box.add(self.root_directory_listing)
|
||||
|
||||
@@ -88,7 +90,8 @@ class BrowsePanel(Gtk.Overlay):
|
||||
while current_dir_id:
|
||||
try:
|
||||
directory = AdapterManager.get_directory(
|
||||
current_dir_id, before_download=self.spinner.show,
|
||||
current_dir_id,
|
||||
before_download=self.spinner.show,
|
||||
).result()
|
||||
except CacheMissError as e:
|
||||
directory = cast(API.Directory, e.partial_data)
|
||||
@@ -128,10 +131,12 @@ class ListAndDrilldown(Gtk.Paned):
|
||||
|
||||
self.list = MusicDirectoryList()
|
||||
self.list.connect(
|
||||
"song-clicked", lambda _, *args: self.emit("song-clicked", *args),
|
||||
"song-clicked",
|
||||
lambda _, *args: self.emit("song-clicked", *args),
|
||||
)
|
||||
self.list.connect(
|
||||
"refresh-window", lambda _, *args: self.emit("refresh-window", *args),
|
||||
"refresh-window",
|
||||
lambda _, *args: self.emit("refresh-window", *args),
|
||||
)
|
||||
self.pack1(self.list, False, False)
|
||||
|
||||
@@ -163,7 +168,8 @@ class ListAndDrilldown(Gtk.Paned):
|
||||
if len(children) == 0:
|
||||
drilldown = ListAndDrilldown()
|
||||
drilldown.connect(
|
||||
"song-clicked", lambda _, *args: self.emit("song-clicked", *args),
|
||||
"song-clicked",
|
||||
lambda _, *args: self.emit("song-clicked", *args),
|
||||
)
|
||||
drilldown.connect(
|
||||
"refresh-window",
|
||||
@@ -277,7 +283,9 @@ class MusicDirectoryList(Gtk.Box):
|
||||
self.directory_id = directory_id or self.directory_id
|
||||
self.selected_id = selected_id or self.selected_id
|
||||
self.update_store(
|
||||
self.directory_id, force=force, order_token=self.update_order_token,
|
||||
self.directory_id,
|
||||
force=force,
|
||||
order_token=self.update_order_token,
|
||||
)
|
||||
|
||||
if app_config:
|
||||
@@ -428,7 +436,8 @@ class MusicDirectoryList(Gtk.Box):
|
||||
# ==================================================================================
|
||||
def create_row(self, model: DrilldownElement) -> Gtk.ListBoxRow:
|
||||
row = Gtk.ListBoxRow(
|
||||
action_name="app.browse-to", action_target=GLib.Variant("s", model.id),
|
||||
action_name="app.browse-to",
|
||||
action_target=GLib.Variant("s", model.id),
|
||||
)
|
||||
rowbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
||||
rowbox.add(
|
||||
@@ -461,7 +470,7 @@ class MusicDirectoryList(Gtk.Box):
|
||||
{},
|
||||
)
|
||||
|
||||
def on_song_button_press(self, tree: Gtk.TreeView, event: Gdk.EventButton,) -> bool:
|
||||
def on_song_button_press(self, tree: Gtk.TreeView, event: Gdk.EventButton) -> bool:
|
||||
if event.button == 3: # Right click
|
||||
clicked_path = tree.get_path_at_pos(event.x, event.y)
|
||||
if not clicked_path:
|
@@ -3,9 +3,9 @@ from typing import Any, cast, List
|
||||
|
||||
from gi.repository import Gdk, GLib, GObject, Gtk, Pango
|
||||
|
||||
from sublime.adapters import AdapterManager, api_objects as API, Result
|
||||
from sublime.config import AppConfiguration
|
||||
from sublime.ui import util
|
||||
from sublime_music.adapters import AdapterManager, api_objects as API, Result
|
||||
from sublime_music.config import AppConfiguration
|
||||
from sublime_music.ui import util
|
||||
|
||||
from .icon_button import IconButton
|
||||
from .load_error import LoadError
|
||||
@@ -15,7 +15,7 @@ from .spinner_image import SpinnerImage
|
||||
|
||||
class AlbumWithSongs(Gtk.Box):
|
||||
__gsignals__ = {
|
||||
"song-selected": (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE, (),),
|
||||
"song-selected": (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE, ()),
|
||||
"song-clicked": (
|
||||
GObject.SignalFlags.RUN_FIRST,
|
||||
GObject.TYPE_NONE,
|
||||
@@ -122,7 +122,9 @@ class AlbumWithSongs(Gtk.Box):
|
||||
|
||||
album_details.add(
|
||||
Gtk.Label(
|
||||
label=util.dot_join(*stats), halign=Gtk.Align.START, margin_left=10,
|
||||
label=util.dot_join(*stats),
|
||||
halign=Gtk.Align.START,
|
||||
margin_left=10,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -235,7 +237,10 @@ class AlbumWithSongs(Gtk.Box):
|
||||
def play_btn_clicked(self, btn: Any):
|
||||
song_ids = [x[-1] for x in self.album_song_store]
|
||||
self.emit(
|
||||
"song-clicked", 0, song_ids, {"force_shuffle_state": False},
|
||||
"song-clicked",
|
||||
0,
|
||||
song_ids,
|
||||
{"force_shuffle_state": False},
|
||||
)
|
||||
|
||||
def shuffle_btn_clicked(self, btn: Any):
|
@@ -35,7 +35,7 @@ class SpinnerImage(Gtk.Overlay):
|
||||
self.filename = filename
|
||||
if self.image_size is not None and filename:
|
||||
pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(
|
||||
filename, self.image_size, self.image_size, True,
|
||||
filename, self.image_size, self.image_size, True
|
||||
)
|
||||
self.image.set_from_pixbuf(pixbuf)
|
||||
else:
|
@@ -4,9 +4,9 @@ from typing import Any, Optional, Type
|
||||
|
||||
from gi.repository import Gio, GObject, Gtk, Pango
|
||||
|
||||
from sublime.adapters import AdapterManager, UIInfo
|
||||
from sublime.adapters.filesystem import FilesystemAdapter
|
||||
from sublime.config import ConfigurationStore, ProviderConfiguration
|
||||
from ..adapters import AdapterManager, UIInfo
|
||||
from ..adapters.filesystem import FilesystemAdapter
|
||||
from ..config import ConfigurationStore, ProviderConfiguration
|
||||
|
||||
|
||||
class AdapterTypeModel(GObject.GObject):
|
Before Width: | Height: | Size: 325 B After Width: | Height: | Size: 325 B |
Before Width: | Height: | Size: 402 B After Width: | Height: | Size: 402 B |
Before Width: | Height: | Size: 402 B After Width: | Height: | Size: 402 B |
Before Width: | Height: | Size: 372 B After Width: | Height: | Size: 372 B |
Before Width: | Height: | Size: 276 B After Width: | Height: | Size: 276 B |
Before Width: | Height: | Size: 391 B After Width: | Height: | Size: 391 B |
Before Width: | Height: | Size: 392 B After Width: | Height: | Size: 392 B |
Before Width: | Height: | Size: 388 B After Width: | Height: | Size: 388 B |