Removed CacheManager from the face of the earth
This commit is contained in:
@@ -41,7 +41,6 @@ except Exception:
|
||||
|
||||
from .adapters import AdapterManager, AlbumSearchQuery, Result
|
||||
from .adapters.api_objects import Playlist, PlayQueue, Song
|
||||
from .cache_manager import CacheManager
|
||||
from .config import AppConfiguration, ReplayGainType
|
||||
from .dbus import dbus_propagate, DBusManager
|
||||
from .players import ChromecastPlayer, MPVPlayer, PlayerEvent
|
||||
@@ -880,7 +879,6 @@ class SublimeMusicApp(Gtk.Application):
|
||||
self.save_play_queue()
|
||||
if self.dbus_manager:
|
||||
self.dbus_manager.shutdown()
|
||||
CacheManager.shutdown()
|
||||
AdapterManager.shutdown()
|
||||
|
||||
# ########## HELPER METHODS ########## #
|
||||
|
@@ -1,398 +0,0 @@
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import threading
|
||||
from collections import defaultdict
|
||||
from concurrent.futures import Future, ThreadPoolExecutor
|
||||
from datetime import datetime
|
||||
from enum import EnumMeta
|
||||
from pathlib import Path
|
||||
from typing import (
|
||||
Any,
|
||||
Callable,
|
||||
DefaultDict,
|
||||
Dict,
|
||||
Generic,
|
||||
List,
|
||||
Optional,
|
||||
Set,
|
||||
TypeVar,
|
||||
Union,
|
||||
)
|
||||
|
||||
try:
|
||||
import gi
|
||||
|
||||
gi.require_version("NM", "1.0")
|
||||
from gi.repository import NM
|
||||
|
||||
networkmanager_imported = True
|
||||
except Exception:
|
||||
# I really don't care what kind of exception it is, all that matters is the
|
||||
# import failed for some reason.
|
||||
logging.warning(
|
||||
"Unable to import NM from GLib. Detection of SSID will be disabled."
|
||||
)
|
||||
networkmanager_imported = False
|
||||
|
||||
from .config import AppConfiguration
|
||||
from .server import Server
|
||||
from .server.api_object import APIObject
|
||||
from .server.api_objects import (
|
||||
AlbumWithSongsID3,
|
||||
Artist,
|
||||
ArtistID3,
|
||||
ArtistInfo2,
|
||||
ArtistWithAlbumsID3,
|
||||
Child,
|
||||
Directory,
|
||||
)
|
||||
|
||||
|
||||
class Singleton(type):
|
||||
"""
|
||||
Metaclass for :class:`CacheManager` so that it can be used like a
|
||||
singleton.
|
||||
"""
|
||||
|
||||
def __getattr__(cls, name: str) -> Any:
|
||||
if not CacheManager._instance:
|
||||
return None
|
||||
# If the cache has a function to do the thing we want, use it. If
|
||||
# not, then go directly to the server (this is useful for things
|
||||
# that just send data to the server.)
|
||||
if hasattr(CacheManager._instance, name):
|
||||
return getattr(CacheManager._instance, name)
|
||||
else:
|
||||
return getattr(CacheManager._instance.server, name)
|
||||
|
||||
return None
|
||||
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
class CacheManager(metaclass=Singleton):
|
||||
"""
|
||||
Handles everything related to caching metadata and song files.
|
||||
"""
|
||||
|
||||
executor: ThreadPoolExecutor = ThreadPoolExecutor(max_workers=50)
|
||||
should_exit: bool = False
|
||||
|
||||
class Result(Generic[T]):
|
||||
# This needs to accept some way of:
|
||||
# 1. getting data from the server to fulfill the request
|
||||
# 2. coercing the data to the schema of the cachedb
|
||||
# 3. queries for retriving the data from the cachedb
|
||||
# All results should be retrieved using select statements from the DB
|
||||
"""
|
||||
A result from a CacheManager function. This is effectively a wrapper
|
||||
around a Future, but it can also resolve immediately if the data
|
||||
already exists.
|
||||
"""
|
||||
data: Optional[T] = None
|
||||
future: Optional[Future] = None
|
||||
on_cancel: Optional[Callable[[], None]] = None
|
||||
|
||||
@staticmethod
|
||||
def from_data(data: T) -> "CacheManager.Result[T]":
|
||||
result: "CacheManager.Result[T]" = CacheManager.Result()
|
||||
result.data = data
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
def from_server(
|
||||
download_fn: Callable[[], T],
|
||||
before_download: Callable[[], Any] = None,
|
||||
after_download: Callable[[T], Any] = None,
|
||||
on_cancel: Callable[[], Any] = None,
|
||||
) -> "CacheManager.Result[T]":
|
||||
result: "CacheManager.Result[T]" = CacheManager.Result()
|
||||
|
||||
def future_fn() -> T:
|
||||
if before_download:
|
||||
before_download()
|
||||
return download_fn()
|
||||
|
||||
result.future = CacheManager.executor.submit(future_fn)
|
||||
result.on_cancel = on_cancel
|
||||
|
||||
if after_download is not None:
|
||||
result.future.add_done_callback(
|
||||
lambda f: after_download and after_download(f.result())
|
||||
)
|
||||
|
||||
return result
|
||||
|
||||
def result(self) -> T:
|
||||
if self.data is not None:
|
||||
return self.data
|
||||
if self.future is not None:
|
||||
return self.future.result()
|
||||
|
||||
raise Exception(
|
||||
"CacheManager.Result did not have either a data or future " "member."
|
||||
)
|
||||
|
||||
def add_done_callback(self, fn: Callable, *args):
|
||||
if self.future is not None:
|
||||
self.future.add_done_callback(fn, *args)
|
||||
else:
|
||||
# Run the function immediately if it's not a future.
|
||||
fn(self, *args)
|
||||
|
||||
def cancel(self) -> bool:
|
||||
if self.on_cancel is not None:
|
||||
self.on_cancel()
|
||||
|
||||
if self.future is not None:
|
||||
return self.future.cancel()
|
||||
return True
|
||||
|
||||
@property
|
||||
def is_future(self) -> bool:
|
||||
return self.future is not None
|
||||
|
||||
@staticmethod
|
||||
def ready() -> bool:
|
||||
return CacheManager._instance is not None
|
||||
|
||||
@staticmethod
|
||||
def shutdown():
|
||||
logging.info("CacheManager shutdown start")
|
||||
CacheManager.should_exit = True
|
||||
CacheManager.executor.shutdown()
|
||||
CacheManager._instance.save_cache_info()
|
||||
logging.info("CacheManager shutdown complete")
|
||||
|
||||
class CacheEncoder(json.JSONEncoder):
|
||||
def default(self, obj: Any) -> Optional[Union[int, List, Dict]]:
|
||||
"""
|
||||
Encodes Python objects to JSON.
|
||||
|
||||
- ``datetime`` objects are converted to UNIX timestamps (``int``)
|
||||
- ``set`` objects are converted to ``list`` objects
|
||||
- ``APIObject`` objects are recursively encoded
|
||||
- ``EnumMeta`` objects are ignored
|
||||
- everything else is encoded using the default encoder
|
||||
"""
|
||||
if type(obj) == datetime:
|
||||
return int(obj.timestamp() * 1000)
|
||||
elif type(obj) == set:
|
||||
return list(obj)
|
||||
elif isinstance(obj, APIObject):
|
||||
return {k: v for k, v in obj.__dict__.items() if v is not None}
|
||||
elif isinstance(obj, EnumMeta):
|
||||
return None
|
||||
|
||||
return json.JSONEncoder.default(self, obj)
|
||||
|
||||
class __CacheManagerInternal:
|
||||
# Thread lock for preventing threads from overriding the state while
|
||||
# it's being saved.
|
||||
cache_lock = threading.Lock()
|
||||
|
||||
cache: DefaultDict[str, Any] = defaultdict(dict)
|
||||
permanently_cached_paths: Set[str] = set()
|
||||
|
||||
# The server instance.
|
||||
server: Server
|
||||
|
||||
# TODO (#56): need to split out the song downloads and make them higher
|
||||
# priority I think. Maybe even need to just make this a priority queue.
|
||||
download_set_lock = threading.Lock()
|
||||
current_downloads: Set[str] = set()
|
||||
|
||||
def __init__(self, app_config: AppConfiguration):
|
||||
self.app_config = app_config
|
||||
assert self.app_config.server is not None
|
||||
self.app_config.server
|
||||
|
||||
# If connected to the "Local Network SSID", use the "Local Network
|
||||
# Address" instead of the "Server Address".
|
||||
hostname = self.app_config.server.server_address
|
||||
if self.app_config.server.local_network_ssid in self.current_ssids:
|
||||
hostname = self.app_config.server.local_network_address
|
||||
|
||||
self.server = Server(
|
||||
name=self.app_config.server.name,
|
||||
hostname=hostname,
|
||||
username=self.app_config.server.username,
|
||||
password=self.app_config.server.password,
|
||||
disable_cert_verify=self.app_config.server.disable_cert_verify,
|
||||
)
|
||||
self.download_limiter_semaphore = threading.Semaphore(
|
||||
self.app_config.concurrent_download_limit
|
||||
)
|
||||
|
||||
self.load_cache_info()
|
||||
|
||||
@property
|
||||
def current_ssids(self) -> Set[str]:
|
||||
if not networkmanager_imported:
|
||||
return set()
|
||||
|
||||
self.networkmanager_client = NM.Client.new()
|
||||
self.nmclient_initialized = False
|
||||
self._current_ssids: Set[str] = set()
|
||||
if not self.nmclient_initialized:
|
||||
# Only look at the active WiFi connections.
|
||||
for ac in self.networkmanager_client.get_active_connections():
|
||||
if ac.get_connection_type() != "802-11-wireless":
|
||||
continue
|
||||
devs = ac.get_devices()
|
||||
if len(devs) != 1:
|
||||
continue
|
||||
if devs[0].get_device_type() != NM.DeviceType.WIFI:
|
||||
continue
|
||||
|
||||
self._current_ssids.add(ac.get_id())
|
||||
|
||||
return self._current_ssids
|
||||
|
||||
def load_cache_info(self):
|
||||
cache_meta_file = self.calculate_abs_path(".cache_meta")
|
||||
|
||||
meta_json = {}
|
||||
if cache_meta_file.exists():
|
||||
with open(cache_meta_file, "r") as f:
|
||||
try:
|
||||
meta_json = json.load(f)
|
||||
except json.decoder.JSONDecodeError:
|
||||
# Just continue with the default meta_json.
|
||||
logging.warning("Unable to load cache", stack_info=True)
|
||||
|
||||
cache_version = meta_json.get("version", 0)
|
||||
|
||||
if cache_version < 1:
|
||||
logging.info("Migrating cache to version 1.")
|
||||
cover_art_re = re.compile(r"(\d+)_(\d+)")
|
||||
abs_path = self.calculate_abs_path("cover_art/")
|
||||
abs_path.mkdir(parents=True, exist_ok=True)
|
||||
for cover_art_file in abs_path.iterdir():
|
||||
match = cover_art_re.match(cover_art_file.name)
|
||||
if match:
|
||||
art_id, dimensions = map(int, match.groups())
|
||||
if dimensions == 1000:
|
||||
no_dimens = cover_art_file.parent.joinpath("{art_id}")
|
||||
logging.info(f"Moving {cover_art_file} to {no_dimens}")
|
||||
shutil.move(cover_art_file, no_dimens)
|
||||
else:
|
||||
logging.info(f"Deleting {cover_art_file}")
|
||||
cover_art_file.unlink()
|
||||
|
||||
self.cache["version"] = 1
|
||||
|
||||
cache_configs = [
|
||||
("song_details", Child, dict),
|
||||
# Non-ID3 caches
|
||||
("music_directories", Directory, dict),
|
||||
("indexes", Artist, list),
|
||||
# ID3 caches
|
||||
("albums", AlbumWithSongsID3, "dict-list"),
|
||||
("album_details", AlbumWithSongsID3, dict),
|
||||
("artists", ArtistID3, list),
|
||||
("artist_details", ArtistWithAlbumsID3, dict),
|
||||
("artist_infos", ArtistInfo2, dict),
|
||||
]
|
||||
for name, type_name, default in cache_configs:
|
||||
if default == list:
|
||||
self.cache[name] = [
|
||||
type_name.from_json(x) for x in meta_json.get(name) or []
|
||||
]
|
||||
elif default == dict:
|
||||
self.cache[name] = {
|
||||
id: type_name.from_json(x)
|
||||
for id, x in (meta_json.get(name) or {}).items()
|
||||
}
|
||||
elif default == "dict-list":
|
||||
self.cache[name] = {
|
||||
n: [type_name.from_json(x) for x in xs]
|
||||
for n, xs in (meta_json.get(name) or {}).items()
|
||||
}
|
||||
|
||||
def save_cache_info(self):
|
||||
os.makedirs(self.app_config.cache_location, exist_ok=True)
|
||||
|
||||
cache_meta_file = self.calculate_abs_path(".cache_meta")
|
||||
os.makedirs(os.path.dirname(cache_meta_file), exist_ok=True)
|
||||
with open(cache_meta_file, "w+") as f, self.cache_lock:
|
||||
f.write(json.dumps(self.cache, indent=2, cls=CacheManager.CacheEncoder))
|
||||
|
||||
def save_file(self, absolute_path: Path, data: bytes):
|
||||
# Make the necessary directories and write to file.
|
||||
os.makedirs(absolute_path.parent, exist_ok=True)
|
||||
with open(absolute_path, "wb+") as f:
|
||||
f.write(data)
|
||||
|
||||
def calculate_abs_path(self, *relative_paths) -> Path:
|
||||
assert self.app_config.server is not None
|
||||
return Path(self.app_config.cache_location).joinpath(
|
||||
self.app_config.server.strhash(), *relative_paths
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def create_future(fn: Callable, *args) -> Future:
|
||||
"""Creates a future on the CacheManager's executor."""
|
||||
return CacheManager.executor.submit(fn, *args)
|
||||
|
||||
def get_indexes(
|
||||
self,
|
||||
before_download: Callable[[], None] = lambda: None,
|
||||
force: bool = False,
|
||||
) -> "CacheManager.Result[List[Artist]]":
|
||||
cache_name = "indexes"
|
||||
|
||||
if self.cache.get(cache_name) and not force:
|
||||
return CacheManager.Result.from_data(self.cache[cache_name])
|
||||
|
||||
def download_fn() -> List[Artist]:
|
||||
artists: List[Artist] = []
|
||||
for index in self.server.get_indexes().index:
|
||||
artists.extend(index.artist)
|
||||
return artists
|
||||
|
||||
def after_download(artists: List[Artist]):
|
||||
with self.cache_lock:
|
||||
self.cache[cache_name] = artists
|
||||
self.save_cache_info()
|
||||
|
||||
return CacheManager.Result.from_server(
|
||||
download_fn,
|
||||
before_download=before_download,
|
||||
after_download=after_download,
|
||||
)
|
||||
|
||||
def get_music_directory(
|
||||
self,
|
||||
id: int,
|
||||
before_download: Callable[[], None] = lambda: None,
|
||||
force: bool = False,
|
||||
) -> "CacheManager.Result[Directory]":
|
||||
cache_name = "music_directories"
|
||||
|
||||
if id in self.cache.get(cache_name, {}) and not force:
|
||||
return CacheManager.Result.from_data(self.cache[cache_name][id])
|
||||
|
||||
def after_download(directory: Directory):
|
||||
with self.cache_lock:
|
||||
self.cache[cache_name][id] = directory
|
||||
self.save_cache_info()
|
||||
|
||||
return CacheManager.Result.from_server(
|
||||
lambda: self.server.get_music_directory(id),
|
||||
before_download=before_download,
|
||||
after_download=after_download,
|
||||
)
|
||||
|
||||
_instance: Optional[__CacheManagerInternal] = None
|
||||
|
||||
def __init__(self):
|
||||
raise Exception("Do not instantiate the CacheManager.")
|
||||
|
||||
@staticmethod
|
||||
def reset(app_config: AppConfiguration):
|
||||
CacheManager._instance = CacheManager.__CacheManagerInternal(app_config)
|
@@ -153,10 +153,8 @@ class AppConfiguration:
|
||||
self._state = UIState()
|
||||
|
||||
# Do the import in the function to avoid circular imports.
|
||||
from sublime.cache_manager import CacheManager
|
||||
from sublime.adapters import AdapterManager
|
||||
|
||||
CacheManager.reset(self)
|
||||
AdapterManager.reset(self)
|
||||
|
||||
@property
|
||||
|
@@ -1,6 +0,0 @@
|
||||
"""
|
||||
This module defines a stateless server which interops with the Subsonic API.
|
||||
"""
|
||||
from .server import Server
|
||||
|
||||
__all__ = ("Server",)
|
@@ -1,20 +0,0 @@
|
||||
# from dataclasses import Field, fields
|
||||
from typing import Any, Dict
|
||||
|
||||
from sublime.from_json import from_json as _from_json
|
||||
|
||||
|
||||
class APIObject:
|
||||
"""Defines the base class for objects coming from the Subsonic API."""
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, data: Dict[str, Any]) -> Any:
|
||||
"""
|
||||
Creates an :class:`APIObject` by taking the ``data`` and passing it to the class
|
||||
constructor and then recursively calling ``from_json`` on all of the fields.
|
||||
``data`` just has to be a well-formed :class:`dict`, so it can come from the
|
||||
JSON or XML APIs.
|
||||
|
||||
:param data: a Python dictionary representation of the data to deserialize
|
||||
"""
|
||||
return _from_json(cls, data)
|
@@ -1,936 +0,0 @@
|
||||
"""
|
||||
WARNING: AUTOGENERATED FILE
|
||||
This file was generated by the api_object_generator.py
|
||||
script. Do not modify this file directly, rather modify the
|
||||
script or run it on a new API version.
|
||||
"""
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
from typing import Any, List, Optional
|
||||
|
||||
from sublime.server.api_object import APIObject
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class AlbumInfo(APIObject):
|
||||
notes: List[str] = field(default_factory=list)
|
||||
musicBrainzId: List[str] = field(default_factory=list)
|
||||
lastFmUrl: List[str] = field(default_factory=list)
|
||||
smallImageUrl: List[str] = field(default_factory=list)
|
||||
mediumImageUrl: List[str] = field(default_factory=list)
|
||||
largeImageUrl: List[str] = field(default_factory=list)
|
||||
value: Optional[str] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class AverageRating(APIObject, float):
|
||||
pass
|
||||
|
||||
|
||||
class MediaType(APIObject, Enum):
|
||||
MUSIC = "music"
|
||||
PODCAST = "podcast"
|
||||
AUDIOBOOK = "audiobook"
|
||||
VIDEO = "video"
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class UserRating(APIObject, int):
|
||||
pass
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Child(APIObject):
|
||||
id: str
|
||||
isDir: bool
|
||||
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
|
||||
contentType: Optional[str] = None
|
||||
suffix: Optional[str] = None
|
||||
transcodedContentType: Optional[str] = None
|
||||
transcodedSuffix: Optional[str] = None
|
||||
duration: Optional[int] = None
|
||||
bitRate: Optional[int] = None
|
||||
path: Optional[str] = None
|
||||
isVideo: Optional[bool] = None
|
||||
userRating: Optional[UserRating] = None
|
||||
averageRating: Optional[AverageRating] = None
|
||||
playCount: Optional[int] = None
|
||||
discNumber: Optional[int] = None
|
||||
created: Optional[datetime] = None
|
||||
starred: Optional[datetime] = None
|
||||
albumId: Optional[str] = None
|
||||
artistId: Optional[str] = None
|
||||
type: Optional[MediaType] = None
|
||||
bookmarkPosition: Optional[int] = None
|
||||
originalWidth: Optional[int] = None
|
||||
originalHeight: Optional[int] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class AlbumList(APIObject):
|
||||
album: List[Child] = field(default_factory=list)
|
||||
value: Optional[str] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class AlbumID3(APIObject):
|
||||
id: str
|
||||
name: str
|
||||
songCount: int
|
||||
duration: int
|
||||
created: datetime
|
||||
value: Optional[str] = None
|
||||
artist: Optional[str] = None
|
||||
artistId: Optional[str] = None
|
||||
coverArt: Optional[str] = None
|
||||
playCount: Optional[int] = None
|
||||
starred: Optional[datetime] = None
|
||||
year: Optional[int] = None
|
||||
genre: Optional[str] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class AlbumList2(APIObject):
|
||||
album: List[AlbumID3] = field(default_factory=list)
|
||||
value: Optional[str] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class AlbumWithSongsID3(APIObject):
|
||||
id: str
|
||||
name: str
|
||||
songCount: int
|
||||
duration: int
|
||||
created: datetime
|
||||
song: List[Child] = field(default_factory=list)
|
||||
value: Optional[str] = None
|
||||
artist: Optional[str] = None
|
||||
artistId: Optional[str] = None
|
||||
coverArt: Optional[str] = None
|
||||
playCount: Optional[int] = None
|
||||
starred: Optional[datetime] = None
|
||||
year: Optional[int] = None
|
||||
genre: Optional[str] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Artist(APIObject):
|
||||
id: str
|
||||
name: str
|
||||
value: Optional[str] = None
|
||||
artistImageUrl: Optional[str] = None
|
||||
starred: Optional[datetime] = None
|
||||
userRating: Optional[UserRating] = None
|
||||
averageRating: Optional[AverageRating] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ArtistInfoBase(APIObject):
|
||||
biography: List[str] = field(default_factory=list)
|
||||
musicBrainzId: List[str] = field(default_factory=list)
|
||||
lastFmUrl: List[str] = field(default_factory=list)
|
||||
smallImageUrl: List[str] = field(default_factory=list)
|
||||
mediumImageUrl: List[str] = field(default_factory=list)
|
||||
largeImageUrl: List[str] = field(default_factory=list)
|
||||
value: Optional[str] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ArtistInfo(APIObject):
|
||||
similarArtist: List[Artist] = field(default_factory=list)
|
||||
value: Optional[str] = None
|
||||
biography: List[str] = field(default_factory=list)
|
||||
musicBrainzId: List[str] = field(default_factory=list)
|
||||
lastFmUrl: List[str] = field(default_factory=list)
|
||||
smallImageUrl: List[str] = field(default_factory=list)
|
||||
mediumImageUrl: List[str] = field(default_factory=list)
|
||||
largeImageUrl: List[str] = field(default_factory=list)
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ArtistID3(APIObject):
|
||||
id: str
|
||||
name: str
|
||||
albumCount: int
|
||||
value: Optional[str] = None
|
||||
coverArt: Optional[str] = None
|
||||
artistImageUrl: Optional[str] = None
|
||||
starred: Optional[datetime] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ArtistInfo2(APIObject):
|
||||
similarArtist: List[ArtistID3] = field(default_factory=list)
|
||||
value: Optional[str] = None
|
||||
biography: List[str] = field(default_factory=list)
|
||||
musicBrainzId: List[str] = field(default_factory=list)
|
||||
lastFmUrl: List[str] = field(default_factory=list)
|
||||
smallImageUrl: List[str] = field(default_factory=list)
|
||||
mediumImageUrl: List[str] = field(default_factory=list)
|
||||
largeImageUrl: List[str] = field(default_factory=list)
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ArtistWithAlbumsID3(APIObject):
|
||||
id: str
|
||||
name: str
|
||||
albumCount: int
|
||||
album: List[AlbumID3] = field(default_factory=list)
|
||||
value: Optional[str] = None
|
||||
coverArt: Optional[str] = None
|
||||
artistImageUrl: Optional[str] = None
|
||||
starred: Optional[datetime] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class IndexID3(APIObject):
|
||||
name: str
|
||||
artist: List[ArtistID3] = field(default_factory=list)
|
||||
value: Optional[str] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ArtistsID3(APIObject):
|
||||
ignoredArticles: str
|
||||
index: List[IndexID3] = field(default_factory=list)
|
||||
value: Optional[str] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Bookmark(APIObject):
|
||||
position: int
|
||||
username: str
|
||||
created: datetime
|
||||
changed: datetime
|
||||
entry: List[Child] = field(default_factory=list)
|
||||
value: Optional[str] = None
|
||||
comment: Optional[str] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Bookmarks(APIObject):
|
||||
bookmark: List[Bookmark] = field(default_factory=list)
|
||||
value: Optional[str] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ChatMessage(APIObject):
|
||||
username: str
|
||||
time: int
|
||||
message: str
|
||||
value: Optional[str] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ChatMessages(APIObject):
|
||||
chatMessage: List[ChatMessage] = field(default_factory=list)
|
||||
value: Optional[str] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Directory(APIObject):
|
||||
id: str
|
||||
name: str
|
||||
child: List[Child] = field(default_factory=list)
|
||||
value: Optional[str] = None
|
||||
parent: Optional[str] = None
|
||||
starred: Optional[datetime] = None
|
||||
userRating: Optional[UserRating] = None
|
||||
averageRating: Optional[AverageRating] = None
|
||||
playCount: Optional[int] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Error(APIObject):
|
||||
code: int
|
||||
value: Optional[str] = None
|
||||
message: Optional[str] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Genre(APIObject):
|
||||
songCount: int
|
||||
albumCount: int
|
||||
value: Optional[str] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Genres(APIObject):
|
||||
genre: List[Genre] = field(default_factory=list)
|
||||
value: Optional[str] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Index(APIObject):
|
||||
name: str
|
||||
artist: List[Artist] = field(default_factory=list)
|
||||
value: Optional[str] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Indexes(APIObject):
|
||||
lastModified: int
|
||||
ignoredArticles: str
|
||||
shortcut: List[Artist] = field(default_factory=list)
|
||||
index: List[Index] = field(default_factory=list)
|
||||
child: List[Child] = field(default_factory=list)
|
||||
value: Optional[str] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class InternetRadioStation(APIObject):
|
||||
id: str
|
||||
name: str
|
||||
streamUrl: str
|
||||
value: Optional[str] = None
|
||||
homePageUrl: Optional[str] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class InternetRadioStations(APIObject):
|
||||
internetRadioStation: List[InternetRadioStation] = field(default_factory=list)
|
||||
value: Optional[str] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class JukeboxStatus(APIObject):
|
||||
currentIndex: int
|
||||
playing: bool
|
||||
gain: float
|
||||
value: Optional[str] = None
|
||||
position: Optional[int] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class JukeboxPlaylist(APIObject):
|
||||
currentIndex: int
|
||||
playing: bool
|
||||
gain: float
|
||||
entry: List[Child] = field(default_factory=list)
|
||||
value: Optional[str] = None
|
||||
position: Optional[int] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class License(APIObject):
|
||||
valid: bool
|
||||
value: Optional[str] = None
|
||||
email: Optional[str] = None
|
||||
licenseExpires: Optional[datetime] = None
|
||||
trialExpires: Optional[datetime] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Lyrics(APIObject):
|
||||
artist: Optional[str] = None
|
||||
value: Optional[str] = None
|
||||
title: Optional[str] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class MusicFolder(APIObject):
|
||||
id: int
|
||||
value: Optional[str] = None
|
||||
name: Optional[str] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class MusicFolders(APIObject):
|
||||
musicFolder: List[MusicFolder] = field(default_factory=list)
|
||||
value: Optional[str] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
class PodcastStatus(APIObject, Enum):
|
||||
NEW = "new"
|
||||
DOWNLOADING = "downloading"
|
||||
COMPLETED = "completed"
|
||||
ERROR = "error"
|
||||
DELETED = "deleted"
|
||||
SKIPPED = "skipped"
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class PodcastEpisode(APIObject):
|
||||
channelId: str
|
||||
status: PodcastStatus
|
||||
id: str
|
||||
isDir: bool
|
||||
title: str
|
||||
streamId: Optional[str] = None
|
||||
description: Optional[str] = None
|
||||
publishDate: Optional[datetime] = None
|
||||
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
|
||||
contentType: Optional[str] = None
|
||||
suffix: Optional[str] = None
|
||||
transcodedContentType: Optional[str] = None
|
||||
transcodedSuffix: Optional[str] = None
|
||||
duration: Optional[int] = None
|
||||
bitRate: Optional[int] = None
|
||||
path: Optional[str] = None
|
||||
isVideo: Optional[bool] = None
|
||||
userRating: Optional[UserRating] = None
|
||||
averageRating: Optional[AverageRating] = None
|
||||
playCount: Optional[int] = None
|
||||
discNumber: Optional[int] = None
|
||||
created: Optional[datetime] = None
|
||||
starred: Optional[datetime] = None
|
||||
albumId: Optional[str] = None
|
||||
artistId: Optional[str] = None
|
||||
type: Optional[MediaType] = None
|
||||
bookmarkPosition: Optional[int] = None
|
||||
originalWidth: Optional[int] = None
|
||||
originalHeight: Optional[int] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class NewestPodcasts(APIObject):
|
||||
episode: List[PodcastEpisode] = field(default_factory=list)
|
||||
value: Optional[str] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class NowPlayingEntry(APIObject):
|
||||
username: str
|
||||
minutesAgo: int
|
||||
playerId: int
|
||||
id: str
|
||||
isDir: bool
|
||||
title: str
|
||||
playerName: Optional[str] = None
|
||||
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
|
||||
contentType: Optional[str] = None
|
||||
suffix: Optional[str] = None
|
||||
transcodedContentType: Optional[str] = None
|
||||
transcodedSuffix: Optional[str] = None
|
||||
duration: Optional[int] = None
|
||||
bitRate: Optional[int] = None
|
||||
path: Optional[str] = None
|
||||
isVideo: Optional[bool] = None
|
||||
userRating: Optional[UserRating] = None
|
||||
averageRating: Optional[AverageRating] = None
|
||||
playCount: Optional[int] = None
|
||||
discNumber: Optional[int] = None
|
||||
created: Optional[datetime] = None
|
||||
starred: Optional[datetime] = None
|
||||
albumId: Optional[str] = None
|
||||
artistId: Optional[str] = None
|
||||
type: Optional[MediaType] = None
|
||||
bookmarkPosition: Optional[int] = None
|
||||
originalWidth: Optional[int] = None
|
||||
originalHeight: Optional[int] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class NowPlaying(APIObject):
|
||||
entry: List[NowPlayingEntry] = field(default_factory=list)
|
||||
value: Optional[str] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class PlayQueue(APIObject):
|
||||
username: str
|
||||
changed: datetime
|
||||
changedBy: str
|
||||
entry: List[Child] = field(default_factory=list)
|
||||
value: Optional[str] = None
|
||||
current: Optional[int] = None
|
||||
position: Optional[int] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Playlist(APIObject):
|
||||
id: str
|
||||
name: str
|
||||
songCount: int
|
||||
duration: int
|
||||
created: datetime
|
||||
changed: datetime
|
||||
allowedUser: List[str] = field(default_factory=list)
|
||||
value: Optional[str] = None
|
||||
comment: Optional[str] = None
|
||||
owner: Optional[str] = None
|
||||
public: Optional[bool] = None
|
||||
coverArt: Optional[str] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class PlaylistWithSongs(APIObject):
|
||||
id: str
|
||||
name: str
|
||||
songCount: int
|
||||
duration: int
|
||||
created: datetime
|
||||
changed: datetime
|
||||
entry: List[Child] = field(default_factory=list)
|
||||
value: Optional[str] = None
|
||||
allowedUser: List[str] = field(default_factory=list)
|
||||
comment: Optional[str] = None
|
||||
owner: Optional[str] = None
|
||||
public: Optional[bool] = None
|
||||
coverArt: Optional[str] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Playlists(APIObject):
|
||||
playlist: List[Playlist] = field(default_factory=list)
|
||||
value: Optional[str] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class PodcastChannel(APIObject):
|
||||
id: str
|
||||
url: str
|
||||
status: PodcastStatus
|
||||
episode: List[PodcastEpisode] = field(default_factory=list)
|
||||
value: Optional[str] = None
|
||||
title: Optional[str] = None
|
||||
description: Optional[str] = None
|
||||
coverArt: Optional[str] = None
|
||||
originalImageUrl: Optional[str] = None
|
||||
errorMessage: Optional[str] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Podcasts(APIObject):
|
||||
channel: List[PodcastChannel] = field(default_factory=list)
|
||||
value: Optional[str] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
class ResponseStatus(APIObject, Enum):
|
||||
OK = "ok"
|
||||
FAILED = "failed"
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ScanStatus(APIObject):
|
||||
scanning: bool
|
||||
value: Optional[str] = None
|
||||
count: Optional[int] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SearchResult(APIObject):
|
||||
offset: int
|
||||
totalHits: int
|
||||
match: List[Child] = field(default_factory=list)
|
||||
value: Optional[str] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SearchResult2(APIObject):
|
||||
artist: List[Artist] = field(default_factory=list)
|
||||
album: List[Child] = field(default_factory=list)
|
||||
song: List[Child] = field(default_factory=list)
|
||||
value: Optional[str] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SearchResult3(APIObject):
|
||||
artist: List[ArtistID3] = field(default_factory=list)
|
||||
album: List[AlbumID3] = field(default_factory=list)
|
||||
song: List[Child] = field(default_factory=list)
|
||||
value: Optional[str] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Share(APIObject):
|
||||
id: str
|
||||
url: str
|
||||
username: str
|
||||
created: datetime
|
||||
visitCount: int
|
||||
entry: List[Child] = field(default_factory=list)
|
||||
value: Optional[str] = None
|
||||
description: Optional[str] = None
|
||||
expires: Optional[datetime] = None
|
||||
lastVisited: Optional[datetime] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Shares(APIObject):
|
||||
share: List[Share] = field(default_factory=list)
|
||||
value: Optional[str] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SimilarSongs(APIObject):
|
||||
song: List[Child] = field(default_factory=list)
|
||||
value: Optional[str] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SimilarSongs2(APIObject):
|
||||
song: List[Child] = field(default_factory=list)
|
||||
value: Optional[str] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Songs(APIObject):
|
||||
song: List[Child] = field(default_factory=list)
|
||||
value: Optional[str] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Starred(APIObject):
|
||||
artist: List[Artist] = field(default_factory=list)
|
||||
album: List[Child] = field(default_factory=list)
|
||||
song: List[Child] = field(default_factory=list)
|
||||
value: Optional[str] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Starred2(APIObject):
|
||||
artist: List[ArtistID3] = field(default_factory=list)
|
||||
album: List[AlbumID3] = field(default_factory=list)
|
||||
song: List[Child] = field(default_factory=list)
|
||||
value: Optional[str] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class TopSongs(APIObject):
|
||||
song: List[Child] = field(default_factory=list)
|
||||
value: Optional[str] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class User(APIObject):
|
||||
username: str
|
||||
scrobblingEnabled: bool
|
||||
adminRole: bool
|
||||
settingsRole: bool
|
||||
downloadRole: bool
|
||||
uploadRole: bool
|
||||
playlistRole: bool
|
||||
coverArtRole: bool
|
||||
commentRole: bool
|
||||
podcastRole: bool
|
||||
streamRole: bool
|
||||
jukeboxRole: bool
|
||||
shareRole: bool
|
||||
videoConversionRole: bool
|
||||
folder: List[int] = field(default_factory=list)
|
||||
value: Optional[str] = None
|
||||
email: Optional[str] = None
|
||||
maxBitRate: Optional[int] = None
|
||||
avatarLastChanged: Optional[datetime] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Users(APIObject):
|
||||
user: List[User] = field(default_factory=list)
|
||||
value: Optional[str] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Version(APIObject, str):
|
||||
pass
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class AudioTrack(APIObject):
|
||||
id: str
|
||||
value: Optional[str] = None
|
||||
name: Optional[str] = None
|
||||
languageCode: Optional[str] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Captions(APIObject):
|
||||
id: str
|
||||
value: Optional[str] = None
|
||||
name: Optional[str] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class VideoConversion(APIObject):
|
||||
id: str
|
||||
value: Optional[str] = None
|
||||
bitRate: Optional[int] = None
|
||||
audioTrackId: Optional[int] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class VideoInfo(APIObject):
|
||||
id: str
|
||||
captions: List[Captions] = field(default_factory=list)
|
||||
audioTrack: List[AudioTrack] = field(default_factory=list)
|
||||
conversion: List[VideoConversion] = field(default_factory=list)
|
||||
value: Optional[str] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Videos(APIObject):
|
||||
video: List[Child] = field(default_factory=list)
|
||||
value: Optional[str] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Response(APIObject):
|
||||
musicFolders: Optional[MusicFolders] = None
|
||||
indexes: Optional[Indexes] = None
|
||||
directory: Optional[Directory] = None
|
||||
genres: Optional[Genres] = None
|
||||
artists: Optional[ArtistsID3] = None
|
||||
artist: Optional[ArtistWithAlbumsID3] = None
|
||||
album: Optional[AlbumWithSongsID3] = None
|
||||
song: Optional[Child] = None
|
||||
videos: Optional[Videos] = None
|
||||
videoInfo: Optional[VideoInfo] = None
|
||||
nowPlaying: Optional[NowPlaying] = None
|
||||
searchResult: Optional[SearchResult] = None
|
||||
searchResult2: Optional[SearchResult2] = None
|
||||
searchResult3: Optional[SearchResult3] = None
|
||||
playlists: Optional[Playlists] = None
|
||||
playlist: Optional[PlaylistWithSongs] = None
|
||||
jukeboxStatus: Optional[JukeboxStatus] = None
|
||||
jukeboxPlaylist: Optional[JukeboxPlaylist] = None
|
||||
license: Optional[License] = None
|
||||
users: Optional[Users] = None
|
||||
user: Optional[User] = None
|
||||
chatMessages: Optional[ChatMessages] = None
|
||||
albumList: Optional[AlbumList] = None
|
||||
albumList2: Optional[AlbumList2] = None
|
||||
randomSongs: Optional[Songs] = None
|
||||
songsByGenre: Optional[Songs] = None
|
||||
lyrics: Optional[Lyrics] = None
|
||||
podcasts: Optional[Podcasts] = None
|
||||
newestPodcasts: Optional[NewestPodcasts] = None
|
||||
internetRadioStations: Optional[InternetRadioStations] = None
|
||||
bookmarks: Optional[Bookmarks] = None
|
||||
playQueue: Optional[PlayQueue] = None
|
||||
shares: Optional[Shares] = None
|
||||
starred: Optional[Starred] = None
|
||||
starred2: Optional[Starred2] = None
|
||||
albumInfo: Optional[AlbumInfo] = None
|
||||
artistInfo: Optional[ArtistInfo] = None
|
||||
artistInfo2: Optional[ArtistInfo2] = None
|
||||
similarSongs: Optional[SimilarSongs] = None
|
||||
similarSongs2: Optional[SimilarSongs2] = None
|
||||
topSongs: Optional[TopSongs] = None
|
||||
scanStatus: Optional[ScanStatus] = None
|
||||
error: Optional[Error] = None
|
||||
value: Optional[str] = None
|
||||
status: Optional[ResponseStatus] = None
|
||||
version: Optional[Version] = None
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return getattr(self, key, default)
|
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,6 @@ from typing import Any
|
||||
from gi.repository import GObject, Gtk
|
||||
|
||||
from sublime.config import AppConfiguration, ServerConfiguration
|
||||
from sublime.server import Server
|
||||
from sublime.ui.common import EditFormDialog, IconButton
|
||||
|
||||
|
||||
@@ -26,7 +25,7 @@ class EditServerDialog(EditFormDialog):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
test_server = Gtk.Button(label="Test Connection to Server")
|
||||
test_server.connect("clicked", self.on_test_server_clicked)
|
||||
# test_server.connect("clicked", self.on_test_server_clicked)
|
||||
|
||||
open_in_browser = Gtk.Button(label="Open in Browser")
|
||||
open_in_browser.connect("clicked", self.on_open_in_browser_clicked)
|
||||
@@ -35,43 +34,43 @@ class EditServerDialog(EditFormDialog):
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def on_test_server_clicked(self, event: Any):
|
||||
# Instantiate the server.
|
||||
server_address = self.data["server_address"].get_text()
|
||||
server = Server(
|
||||
name=self.data["name"].get_text(),
|
||||
hostname=server_address,
|
||||
username=self.data["username"].get_text(),
|
||||
password=self.data["password"].get_text(),
|
||||
disable_cert_verify=self.data["disable_cert_verify"].get_active(),
|
||||
)
|
||||
# def on_test_server_clicked(self, event: Any):
|
||||
# # Instantiate the server.
|
||||
# server_address = self.data["server_address"].get_text()
|
||||
# server = Server(
|
||||
# name=self.data["name"].get_text(),
|
||||
# hostname=server_address,
|
||||
# username=self.data["username"].get_text(),
|
||||
# password=self.data["password"].get_text(),
|
||||
# disable_cert_verify=self.data["disable_cert_verify"].get_active(),
|
||||
# )
|
||||
|
||||
# Try to ping, and show a message box with whether or not it worked.
|
||||
try:
|
||||
server.ping()
|
||||
dialog = Gtk.MessageDialog(
|
||||
transient_for=self,
|
||||
message_type=Gtk.MessageType.INFO,
|
||||
buttons=Gtk.ButtonsType.OK,
|
||||
text="Connection to server successful.",
|
||||
)
|
||||
dialog.format_secondary_markup(
|
||||
f"Connection to {server_address} successful."
|
||||
)
|
||||
except Exception as err:
|
||||
dialog = Gtk.MessageDialog(
|
||||
transient_for=self,
|
||||
message_type=Gtk.MessageType.ERROR,
|
||||
buttons=Gtk.ButtonsType.OK,
|
||||
text="Connection to server unsuccessful.",
|
||||
)
|
||||
dialog.format_secondary_markup(
|
||||
f"Connection to {server_address} resulted in the following "
|
||||
f"error:\n\n{err}"
|
||||
)
|
||||
# # Try to ping, and show a message box with whether or not it worked.
|
||||
# try:
|
||||
# server.ping()
|
||||
# dialog = Gtk.MessageDialog(
|
||||
# transient_for=self,
|
||||
# message_type=Gtk.MessageType.INFO,
|
||||
# buttons=Gtk.ButtonsType.OK,
|
||||
# text="Connection to server successful.",
|
||||
# )
|
||||
# dialog.format_secondary_markup(
|
||||
# f"Connection to {server_address} successful."
|
||||
# )
|
||||
# except Exception as err:
|
||||
# dialog = Gtk.MessageDialog(
|
||||
# transient_for=self,
|
||||
# message_type=Gtk.MessageType.ERROR,
|
||||
# buttons=Gtk.ButtonsType.OK,
|
||||
# text="Connection to server unsuccessful.",
|
||||
# )
|
||||
# dialog.format_secondary_markup(
|
||||
# f"Connection to {server_address} resulted in the following "
|
||||
# f"error:\n\n{err}"
|
||||
# )
|
||||
|
||||
dialog.run()
|
||||
dialog.destroy()
|
||||
# dialog.run()
|
||||
# dialog.destroy()
|
||||
|
||||
def on_open_in_browser_clicked(self, event: Any):
|
||||
subprocess.call(["xdg-open", self.data["server_address"].get_text()])
|
||||
|
Reference in New Issue
Block a user