diff --git a/.gitignore b/.gitignore index 0e46bbe..9ba370d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ flatpak/flatpak_build_dir/ flatpak/sublime-music.flatpak +sublime/adapters/subsonic/api_specs/ # Created by https://www.gitignore.io/api/python # Edit at https://www.gitignore.io/?templates=python diff --git a/api_object_generator/api_specs/subsonic-rest-api-1.1.0.xsd b/api_object_generator/api_specs/subsonic-rest-api-1.1.0.xsd deleted file mode 100644 index 04349b2..0000000 --- a/api_object_generator/api_specs/subsonic-rest-api-1.1.0.xsd +++ /dev/null @@ -1,156 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/api_object_generator/api_specs/subsonic-rest-api-1.1.1.xsd b/api_object_generator/api_specs/subsonic-rest-api-1.1.1.xsd deleted file mode 100644 index d9f440a..0000000 --- a/api_object_generator/api_specs/subsonic-rest-api-1.1.1.xsd +++ /dev/null @@ -1,157 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/api_object_generator/api_specs/subsonic-rest-api-1.10.2.xsd b/api_object_generator/api_specs/subsonic-rest-api-1.10.2.xsd deleted file mode 100644 index a409c45..0000000 --- a/api_object_generator/api_specs/subsonic-rest-api-1.10.2.xsd +++ /dev/null @@ -1,500 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/api_object_generator/api_specs/subsonic-rest-api-1.11.0.xsd b/api_object_generator/api_specs/subsonic-rest-api-1.11.0.xsd deleted file mode 100644 index d2ccf24..0000000 --- a/api_object_generator/api_specs/subsonic-rest-api-1.11.0.xsd +++ /dev/null @@ -1,548 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/api_object_generator/api_specs/subsonic-rest-api-1.12.0.xsd b/api_object_generator/api_specs/subsonic-rest-api-1.12.0.xsd deleted file mode 100644 index 6104348..0000000 --- a/api_object_generator/api_specs/subsonic-rest-api-1.12.0.xsd +++ /dev/null @@ -1,563 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/api_object_generator/api_specs/subsonic-rest-api-1.13.0.xsd b/api_object_generator/api_specs/subsonic-rest-api-1.13.0.xsd deleted file mode 100644 index 30845f1..0000000 --- a/api_object_generator/api_specs/subsonic-rest-api-1.13.0.xsd +++ /dev/null @@ -1,588 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/api_object_generator/api_specs/subsonic-rest-api-1.14.0.xsd b/api_object_generator/api_specs/subsonic-rest-api-1.14.0.xsd deleted file mode 100644 index 47fbfdc..0000000 --- a/api_object_generator/api_specs/subsonic-rest-api-1.14.0.xsd +++ /dev/null @@ -1,632 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/api_object_generator/api_specs/subsonic-rest-api-1.15.0.xsd b/api_object_generator/api_specs/subsonic-rest-api-1.15.0.xsd deleted file mode 100644 index 44d5103..0000000 --- a/api_object_generator/api_specs/subsonic-rest-api-1.15.0.xsd +++ /dev/null @@ -1,638 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/api_object_generator/api_specs/subsonic-rest-api-1.16.0.xsd b/api_object_generator/api_specs/subsonic-rest-api-1.16.0.xsd deleted file mode 100644 index 590ff89..0000000 --- a/api_object_generator/api_specs/subsonic-rest-api-1.16.0.xsd +++ /dev/null @@ -1,638 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/api_object_generator/api_specs/subsonic-rest-api-1.16.1.xsd b/api_object_generator/api_specs/subsonic-rest-api-1.16.1.xsd deleted file mode 100644 index f610574..0000000 --- a/api_object_generator/api_specs/subsonic-rest-api-1.16.1.xsd +++ /dev/null @@ -1,640 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/api_object_generator/api_specs/subsonic-rest-api-1.2.0.xsd b/api_object_generator/api_specs/subsonic-rest-api-1.2.0.xsd deleted file mode 100644 index 5970c60..0000000 --- a/api_object_generator/api_specs/subsonic-rest-api-1.2.0.xsd +++ /dev/null @@ -1,199 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/api_object_generator/api_specs/subsonic-rest-api-1.3.0.xsd b/api_object_generator/api_specs/subsonic-rest-api-1.3.0.xsd deleted file mode 100644 index 1865afa..0000000 --- a/api_object_generator/api_specs/subsonic-rest-api-1.3.0.xsd +++ /dev/null @@ -1,214 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/api_object_generator/api_specs/subsonic-rest-api-1.4.0.xsd b/api_object_generator/api_specs/subsonic-rest-api-1.4.0.xsd deleted file mode 100644 index 65c9ea5..0000000 --- a/api_object_generator/api_specs/subsonic-rest-api-1.4.0.xsd +++ /dev/null @@ -1,226 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/api_object_generator/api_specs/subsonic-rest-api-1.5.0.xsd b/api_object_generator/api_specs/subsonic-rest-api-1.5.0.xsd deleted file mode 100644 index 756c578..0000000 --- a/api_object_generator/api_specs/subsonic-rest-api-1.5.0.xsd +++ /dev/null @@ -1,227 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/api_object_generator/api_specs/subsonic-rest-api-1.6.0.xsd b/api_object_generator/api_specs/subsonic-rest-api-1.6.0.xsd deleted file mode 100644 index 5f1f398..0000000 --- a/api_object_generator/api_specs/subsonic-rest-api-1.6.0.xsd +++ /dev/null @@ -1,306 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/api_object_generator/api_specs/subsonic-rest-api-1.7.0.xsd b/api_object_generator/api_specs/subsonic-rest-api-1.7.0.xsd deleted file mode 100644 index 070690c..0000000 --- a/api_object_generator/api_specs/subsonic-rest-api-1.7.0.xsd +++ /dev/null @@ -1,319 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/api_object_generator/api_specs/subsonic-rest-api-1.8.0.xsd b/api_object_generator/api_specs/subsonic-rest-api-1.8.0.xsd deleted file mode 100644 index ba404c5..0000000 --- a/api_object_generator/api_specs/subsonic-rest-api-1.8.0.xsd +++ /dev/null @@ -1,448 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/api_object_generator/api_specs/subsonic-rest-api-1.9.0.xsd b/api_object_generator/api_specs/subsonic-rest-api-1.9.0.xsd deleted file mode 100644 index 1a80d96..0000000 --- a/api_object_generator/api_specs/subsonic-rest-api-1.9.0.xsd +++ /dev/null @@ -1,488 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/sublime/adapters/adapter_base.py b/sublime/adapters/adapter_base.py index ce73450..921bf38 100644 --- a/sublime/adapters/adapter_base.py +++ b/sublime/adapters/adapter_base.py @@ -1,16 +1,16 @@ import abc from dataclasses import dataclass +from pathlib import Path from typing import ( Any, Dict, Iterable, - List, + Sequence, Optional, - Type, Tuple, + Type, Union, ) -from pathlib import Path from .api_objects import ( Playlist, @@ -187,7 +187,7 @@ class Adapter(abc.ABC): # These properties determine if what things the adapter can be used to do # at the current moment. # ========================================================================= - def get_playlists(self) -> List[Playlist]: + def get_playlists(self) -> Sequence[Playlist]: """ Gets a list of all of the :class:`sublime.adapter.api_objects.Playlist` objects known to the adapter. diff --git a/sublime/adapters/adapter_manager.py b/sublime/adapters/adapter_manager.py index d0c812a..8308b32 100644 --- a/sublime/adapters/adapter_manager.py +++ b/sublime/adapters/adapter_manager.py @@ -156,6 +156,7 @@ class AdapterManager: def can_get_playlists() -> bool: # It only matters that the ground truth one can service the request. return ( + AdapterManager._instance is not None and AdapterManager._instance.ground_truth_adapter.can_service_requests and AdapterManager._instance.ground_truth_adapter.can_get_playlists) diff --git a/sublime/adapters/api_objects.py b/sublime/adapters/api_objects.py index 6c19e90..c050b51 100644 --- a/sublime/adapters/api_objects.py +++ b/sublime/adapters/api_objects.py @@ -1,40 +1,37 @@ """ Defines the objects that are returned by adapter methods. """ -from dataclasses import dataclass +import abc from datetime import datetime, timedelta -from typing import List, Optional +from typing import Optional, Sequence -@dataclass(frozen=True) -class Song: +class Song(abc.ABC): id: str -@dataclass(frozen=True) -class Playlist: +class Playlist(abc.ABC): id: str name: str - song_count: Optional[int] = None - duration: Optional[timedelta] = None - created: Optional[datetime] = None - changed: Optional[datetime] = None - comment: Optional[str] = None - owner: Optional[str] = None - public: Optional[bool] = None - cover_art: Optional[str] = None + song_count: Optional[int] + duration: Optional[timedelta] + created: Optional[datetime] + changed: Optional[datetime] + comment: Optional[str] + owner: Optional[str] + public: Optional[bool] + cover_art: Optional[str] -@dataclass(frozen=True) -class PlaylistDetails: +class PlaylistDetails(abc.ABC): id: str name: str song_count: int duration: timedelta - songs: List[Song] - created: Optional[datetime] = None - changed: Optional[datetime] = None - comment: Optional[str] = None - owner: Optional[str] = None - public: Optional[bool] = None - cover_art: Optional[str] = None + songs: Sequence[Song] + created: Optional[datetime] + changed: Optional[datetime] + comment: Optional[str] + owner: Optional[str] + public: Optional[bool] + cover_art: Optional[str] diff --git a/sublime/adapters/filesystem/adapter.py b/sublime/adapters/filesystem/adapter.py index 77bf439..18d2066 100644 --- a/sublime/adapters/filesystem/adapter.py +++ b/sublime/adapters/filesystem/adapter.py @@ -1,7 +1,7 @@ import logging from dataclasses import asdict from pathlib import Path -from typing import Any, Dict, List, Optional, Tuple +from typing import Any, Dict, Sequence, Optional, Tuple from playhouse.sqliteq import SqliteQueueDatabase @@ -60,7 +60,7 @@ class FilesystemAdapter(CachingAdapter): # ========================================================================= can_get_playlists: bool = True - def get_playlists(self) -> List[Playlist]: + def get_playlists(self) -> Sequence[Playlist]: playlists = list(database.Playlist.select()) if len(playlists) == 0: # TODO not necessarily a cache miss raise CacheMissError() diff --git a/sublime/adapters/subsonic/adapter.py b/sublime/adapters/subsonic/adapter.py index c1c1283..ea23195 100644 --- a/sublime/adapters/subsonic/adapter.py +++ b/sublime/adapters/subsonic/adapter.py @@ -1,19 +1,16 @@ +import json import logging import os import re from datetime import datetime, timedelta from pathlib import Path from time import sleep -from typing import Any, Dict, List, Optional, Union +from typing import Any, Dict, Sequence, Optional, Tuple, Union import requests -from sublime.adapters.api_objects import ( - Playlist, - PlaylistDetails, - Song, -) -from .. import Adapter, ConfigParamDescriptor +from .api_objects import Response +from .. import Adapter, api_objects as API, ConfigParamDescriptor class SubsonicAdapter(Adapter): @@ -47,14 +44,20 @@ class SubsonicAdapter(Adapter): self.hostname = config['server_address'] self.username = config['username'] self.password = config['password'] - self.disable_cert_verify = config['disable_cert_verify'] + self.disable_cert_verify = config.get('disable_cert_verify') + + # TODO support XML | JSON # Availability Properties # ========================================================================= @property def can_service_requests(self) -> bool: - # TODO: detect ping - return True + try: + self._get_json('ping', timeout=2) + return True + except Exception: + logging.exception(f'Could not connect to {self.hostname}') + return False # Helper mothods for making requests # ========================================================================= @@ -74,8 +77,12 @@ class SubsonicAdapter(Adapter): def _make_url(self, endpoint: str) -> str: return f'{self.hostname}/rest/{endpoint}.view' - # def _get(self, url, timeout=(3.05, 2), **params): - def _get(self, url: str, **params) -> Any: + def _get( + self, + url: str, + timeout: Union[float, Tuple[float, float], None] = None, + **params, + ) -> Any: params = {**self._get_params(), **params} logging.info(f'[START] get: {url}') @@ -90,12 +97,16 @@ class SubsonicAdapter(Adapter): if type(v) == datetime: params[k] = int(v.timestamp() * 1000) + if self._is_mock: + return self._get_mock_data() + result = requests.get( url, params=params, verify=not self.disable_cert_verify, - # timeout=timeout, + timeout=timeout, ) + # TODO (#122): make better if result.status_code != 200: raise Exception(f'[FAIL] get: {url} status={result.status_code}') @@ -106,8 +117,8 @@ class SubsonicAdapter(Adapter): def _get_json( self, url: str, - **params: Union[None, str, datetime, int, List[int]], - ) -> Dict[str, Any]: + **params: Union[None, str, datetime, int, Sequence[int]], + ) -> Response: """ Make a get request to a *Sonic REST API. Handle all types of errors including *Sonic ```` responses. @@ -129,47 +140,57 @@ class SubsonicAdapter(Adapter): ) raise Exception(f'Subsonic API Error #{code}: {message}') - return subsonic_response + logging.debug(f'Response from {url}', subsonic_response) + return Response.from_dict(subsonic_response) - _snake_case_re = re.compile(r'(? Dict[str, Any]: - return { - self._snake_case_re.sub('_', k).lower(): v - for k, v in obj.items() - } + def _set_mock_data(self, data: Any): + class MockResult: + def __init__(self, content: Any): + self._content = content + + def content(self) -> Any: + return self._content + + def json(self) -> Any: + return json.loads(self._content) + + def get_mock_data() -> Any: + if type(data) == Exception: + raise data + return MockResult(data) + + self._get_mock_data = get_mock_data # Data Retrieval Methods # ========================================================================= can_get_playlists = True - def get_playlists(self) -> List[Playlist]: - result = [ - { - **p, - 'duration': - timedelta( - seconds=p['duration']) if p.get('duration') else None, - } for p in self._get_json(self._make_url('getPlaylists')).get( - 'playlists', {}).get('playlist') - ] - return [Playlist(**self._to_snake_case(p)) for p in result] + def get_playlists(self) -> Sequence[API.Playlist]: + response = self._get_json(self._make_url('getPlaylists')).playlists + if not response: + return [] + return response.playlist can_get_playlist_details = True def get_playlist_details( self, playlist_id: str, - ) -> PlaylistDetails: + ) -> API.PlaylistDetails: result = self._get_json( self._make_url('getPlaylist'), id=playlist_id, - ).get('playlist') + ).playlist print(result) - assert result - result['duration'] = result.get('duration') or sum( - s.get('duration') or 0 for s in result['entry']) - result['songCount'] = result.get('songCount') or len(result['entry']) - songs = [Song(id=s['id']) for s in result['entry']] - del result['entry'] - return PlaylistDetails(**self._to_snake_case(result), songs=songs) + return result + # assert result + # result['duration'] = result.get('duration') or sum( + # s.get('duration') or 0 for s in result['entry']) + # result['songCount'] = result.get('songCount') or len(result['entry']) + # songs = [Song(id=s['id']) for s in result['entry']] + # del result['entry'] + # return API.PlaylistDetails(**self._to_snake_case(result), songs=songs) diff --git a/sublime/adapters/subsonic/api_objects.py b/sublime/adapters/subsonic/api_objects.py new file mode 100644 index 0000000..e229a30 --- /dev/null +++ b/sublime/adapters/subsonic/api_objects.py @@ -0,0 +1,110 @@ +""" +These are the API objects that are returned by Subsonic. +""" + +from dataclasses import dataclass, field +from datetime import datetime, timedelta +from enum import Enum +from typing import List, Optional, Sequence + +from dataclasses_json import config, dataclass_json, DataClassJsonMixin, LetterCase +from dateutil import parser +from marshmallow import fields + +from .. import api_objects as SublimeAPI + +datetime_metadata = config( + encoder=datetime.isoformat, + decoder=lambda d: parser.parse(d) if d else None, + mm_field=fields.DateTime(format='iso'), +) + +timedelta_metadata = config( + encoder=datetime.isoformat, + decoder=lambda s: timedelta(seconds=s) if s else None, + mm_field=fields.TimeDelta(), +) + + +@dataclass_json(letter_case=LetterCase.CAMEL) +@dataclass(frozen=True) +class Child(SublimeAPI.Song, DataClassJsonMixin): + title: str + value: Optional[str] = None + parent: Optional[str] = None + album: Optional[str] = None + artist: Optional[str] = None + track: Optional[int] = None + year: Optional[int] = None + genre: Optional[str] = None + coverArt: Optional[str] = None + size: Optional[int] = None + content_type: Optional[str] = None + suffix: Optional[str] = None + transcoded_content_type: Optional[str] = None + transcoded_suffix: Optional[str] = None + duration: Optional[int] = None + bit_rate: Optional[int] = None + path: Optional[str] = None + isVideo: Optional[bool] = None + # userRating: Optional[UserRating] = None + # averageRating: Optional[AverageRating] = None + play_count: Optional[int] = None + disc_number: Optional[int] = None + created: Optional[datetime] = None + starred: Optional[datetime] = None + albumId: Optional[str] = None + artistId: Optional[str] = None + # type_: Optional[MediaType] = None + bookmark_position: Optional[int] = None + original_width: Optional[int] = None + original_height: Optional[int] = None + + +@dataclass_json(letter_case=LetterCase.CAMEL) +@dataclass +class Playlist(SublimeAPI.Playlist): + id: str + name: str + song_count: Optional[int] = None + duration: Optional[timedelta] = field( + default=None, metadata=timedelta_metadata) + created: Optional[datetime] = field( + default=None, metadata=datetime_metadata) + changed: Optional[datetime] = field( + default=None, metadata=datetime_metadata) + comment: Optional[str] = None + owner: Optional[str] = None + public: Optional[bool] = None + cover_art: Optional[str] = None + + +@dataclass_json(letter_case=LetterCase.CAMEL) +@dataclass +class PlaylistWithSongs(SublimeAPI.PlaylistDetails): + duration: timedelta = field( + default_factory=timedelta, metadata=timedelta_metadata) + songs: List[SublimeAPI.Song] = field( + default_factory=list, + metadata=config(field_name='entry'), + ) + created: Optional[datetime] = field( + default=None, metadata=datetime_metadata) + changed: Optional[datetime] = field( + default=None, metadata=datetime_metadata) + + +@dataclass(frozen=True) +class Playlists(DataClassJsonMixin): + playlist: List[Playlist] = field(default_factory=list) + + +@dataclass(frozen=True) +class Response(DataClassJsonMixin): + """ + The base Subsonic response object. + """ + song: Optional[Child] = None + playlists: Optional[Playlists] = None + playlist: Optional[PlaylistWithSongs] = None + value: Optional[str] = None diff --git a/tests/adapter_tests/adapter_api_object_tests.py b/tests/adapter_tests/adapter_api_object_tests.py deleted file mode 100644 index b5786ac..0000000 --- a/tests/adapter_tests/adapter_api_object_tests.py +++ /dev/null @@ -1,11 +0,0 @@ -import pytest - -from datetime import datetime, timedelta - -from sublime.adapters.api_objects import (Playlist, PlaylistDetails) - - -def test_playlist_inheritance(): - Playlist('foo', 'Bar') - - PlaylistDetails('foo', 'bar', 3, timedelta(seconds=720)) diff --git a/tests/adapter_tests/adapter_base_tests.py b/tests/adapter_tests/adapter_base_tests.py deleted file mode 100644 index ed4a2c3..0000000 --- a/tests/adapter_tests/adapter_base_tests.py +++ /dev/null @@ -1,58 +0,0 @@ -import pytest - -from sublime.adapters import Adapter, AdapterManager - - -def test_adapter_manager_singleton(): - AdapterManager.reset() - AdapterManager.get_playlists() - - -def test_functions_not_implemented(): - with pytest.raises(NotImplementedError): - Adapter(None) - - class MyAdapter(Adapter): - def __init__(self, s: dict, c: bool = False): - pass - - can_be_cache: bool = True - - with pytest.raises(NotImplementedError): - adapter = MyAdapter({}) - adapter.can_service_requests - - with pytest.raises(NotImplementedError): - adapter = MyAdapter({}) - adapter.ingest_new_data() - - -def test_override_bool(): - class MyAdapter(Adapter): - def __init__(self, s: dict, c: bool = False): - pass - - can_be_cache = True - can_service_requests = True - - adapter = MyAdapter({}) - assert adapter.can_be_cache is True - assert adapter.can_service_requests is True - - -def test_override_bool_with_property(): - class MyAdapter(Adapter): - def __init__(self, s: dict, c: bool = False): - pass - - @property - def can_be_cache(self) -> bool: - return True - - @property - def can_service_requests(self) -> bool: - return True - - adapter = MyAdapter({}) - assert adapter.can_be_cache is True - assert adapter.can_service_requests is True diff --git a/tests/adapter_tests/sublime_adapter_tests.py b/tests/adapter_tests/sublime_adapter_tests.py new file mode 100644 index 0000000..db547c6 --- /dev/null +++ b/tests/adapter_tests/sublime_adapter_tests.py @@ -0,0 +1,140 @@ +import json +import re +from datetime import datetime, timedelta +from pathlib import Path +from typing import Any, Callable, Dict, Tuple + +import pytest + +from dateutil import parser + +from sublime.adapters.subsonic import ( + SubsonicAdapter, api_objects as SubsonicAPI) + + +@pytest.fixture +def subsonic_adapter(tmp_path: Path): + adapter = SubsonicAdapter( + { + 'server_address': 'http://localhost:4533', + 'username': 'test', + 'password': 'testpass', + }, + tmp_path, + ) + adapter._is_mock = True + yield adapter + + +def mock_json(**obj: Any) -> str: + return json.dumps( + { + 'subsonic-response': { + 'status': 'ok', + 'version': '1.15.0', + **obj, + }, + }) + + +def camel_to_snake(name: str) -> str: + name = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name) + return re.sub('([a-z0-9])([A-Z])', r'\1_\2', name).lower() + + +def test_request_making_methods(subsonic_adapter: SubsonicAdapter): + expected = { + 'u': 'test', + 'p': 'testpass', + 'c': 'Sublime Music', + 'f': 'json', + 'v': '1.15.0', + } + assert ( + sorted(expected.items()) == sorted( + subsonic_adapter._get_params().items())) + + assert subsonic_adapter._make_url( + 'foo') == 'http://localhost:4533/rest/foo.view' + + +def test_can_service_requests(subsonic_adapter: SubsonicAdapter): + # Mock a connection error + subsonic_adapter._set_mock_data(Exception()) + assert subsonic_adapter.can_service_requests is False + + # Simulate some sort of ping error + subsonic_adapter._set_mock_data( + mock_json( + status='failed', + error={ + 'code': '1', + 'message': 'Test message', + }, + )) + assert subsonic_adapter.can_service_requests is False + + # Simulate valid ping + subsonic_adapter._set_mock_data(mock_json()) + assert subsonic_adapter.can_service_requests is True + + +def test_get_playlists(subsonic_adapter: SubsonicAdapter): + playlists = [ + { + "id": "6", + "name": "Playlist 1", + "comment": "Foo", + "owner": "test", + "public": True, + "songCount": 2, + "duration": 625, + "created": "2020-03-27T05:39:35.188Z", + "changed": "2020-04-08T00:07:01.748Z", + "coverArt": "pl-6" + }, + { + "id": "7", + "name": "Playlist 2", + "comment": "", + "owner": "test", + "public": True, + "songCount": 3, + "duration": 952, + "created": "2020-03-27T05:39:43.327Z", + "changed": "2020-03-27T05:44:37.275Z", + "coverArt": "pl-7" + }, + ] + subsonic_adapter._set_mock_data( + mock_json(playlists={ + 'playlist': playlists, + })) + + expected = [ + SubsonicAPI.Playlist( + id='6', + name='Playlist 1', + song_count=2, + duration=timedelta(seconds=625), + created=parser.parse('2020-03-27T05:39:35.188Z'), + changed=parser.parse('2020-04-08T00:07:01.748Z'), + comment='Foo', + owner='test', + public=True, + cover_art='pl-6', + ), + SubsonicAPI.Playlist( + id='7', + name='Playlist 2', + song_count=3, + duration=timedelta(seconds=952), + created=parser.parse('2020-03-27T05:39:43.327Z'), + changed=parser.parse('2020-03-27T05:44:37.275Z'), + comment='', + owner='test', + public=True, + cover_art='pl-7', + ), + ] + assert subsonic_adapter.get_playlists() == expected