Made the Subsonic icons a lot better
@@ -296,6 +296,18 @@ class Adapter(abc.ABC):
|
|||||||
"""
|
"""
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@property
|
||||||
|
@abc.abstractmethod
|
||||||
|
def ping_status(self) -> bool:
|
||||||
|
"""
|
||||||
|
This function should return whether or not the server can be pinged, however it
|
||||||
|
must do it *instantly*. This function is called *very* often, and even a few
|
||||||
|
milliseconds delay stacks up quickly and can block the UI thread.
|
||||||
|
|
||||||
|
One option is to ping the server every few seconds and cache the result of the
|
||||||
|
ping and use that as the result of this function.
|
||||||
|
"""
|
||||||
|
|
||||||
# Availability Properties
|
# Availability Properties
|
||||||
# These properties determine if what things the adapter can be used to do
|
# These properties determine if what things the adapter can be used to do
|
||||||
# at the current moment.
|
# at the current moment.
|
||||||
@@ -751,6 +763,8 @@ class CachingAdapter(Adapter):
|
|||||||
:param is_cache: whether or not the adapter is being used as a cache.
|
:param is_cache: whether or not the adapter is being used as a cache.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
ping_status = True
|
||||||
|
|
||||||
# Data Ingestion Methods
|
# Data Ingestion Methods
|
||||||
# ==================================================================================
|
# ==================================================================================
|
||||||
class CachedDataKey(Enum):
|
class CachedDataKey(Enum):
|
||||||
|
@@ -206,6 +206,16 @@ class AdapterManager:
|
|||||||
assert AdapterManager._instance
|
assert AdapterManager._instance
|
||||||
return Result(AdapterManager._instance.ground_truth_adapter.initial_sync)
|
return Result(AdapterManager._instance.ground_truth_adapter.initial_sync)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def ground_truth_adapter_is_networked() -> bool:
|
||||||
|
assert AdapterManager._instance
|
||||||
|
return AdapterManager._instance.ground_truth_adapter.is_networked
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_ping_status() -> bool:
|
||||||
|
assert AdapterManager._instance
|
||||||
|
return AdapterManager._instance.ground_truth_adapter.ping_status
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def shutdown():
|
def shutdown():
|
||||||
logging.info("AdapterManager shutdown start")
|
logging.info("AdapterManager shutdown start")
|
||||||
|
@@ -160,6 +160,10 @@ class SubsonicAdapter(Adapter):
|
|||||||
logging.exception(f"Could not connect to {self.hostname}")
|
logging.exception(f"Could not connect to {self.hostname}")
|
||||||
self._server_available.value = False
|
self._server_available.value = False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def ping_status(self) -> bool:
|
||||||
|
return self._server_available.value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def can_service_requests(self) -> bool:
|
def can_service_requests(self) -> bool:
|
||||||
return self._server_available.value
|
return self._server_available.value
|
||||||
|
@@ -55,6 +55,7 @@ class SublimeMusicApp(Gtk.Application):
|
|||||||
self.connect("shutdown", self.on_app_shutdown)
|
self.connect("shutdown", self.on_app_shutdown)
|
||||||
|
|
||||||
player: Player
|
player: Player
|
||||||
|
exiting: bool = False
|
||||||
|
|
||||||
def do_startup(self):
|
def do_startup(self):
|
||||||
Gtk.Application.do_startup(self)
|
Gtk.Application.do_startup(self)
|
||||||
@@ -254,6 +255,15 @@ class SublimeMusicApp(Gtk.Application):
|
|||||||
inital_sync_result = AdapterManager.initial_sync()
|
inital_sync_result = AdapterManager.initial_sync()
|
||||||
inital_sync_result.add_done_callback(lambda _: self.update_window())
|
inital_sync_result.add_done_callback(lambda _: self.update_window())
|
||||||
|
|
||||||
|
# Start a loop for testing the ping.
|
||||||
|
def ping_update():
|
||||||
|
if self.exiting:
|
||||||
|
return
|
||||||
|
self.update_window()
|
||||||
|
GLib.timeout_add(5000, ping_update)
|
||||||
|
|
||||||
|
GLib.timeout_add(5000, ping_update)
|
||||||
|
|
||||||
# Prompt to load the play queue from the server.
|
# Prompt to load the play queue from the server.
|
||||||
if self.app_config.server.sync_enabled:
|
if self.app_config.server.sync_enabled:
|
||||||
self.update_play_state_from_server(prompt_confirm=True)
|
self.update_play_state_from_server(prompt_confirm=True)
|
||||||
@@ -494,7 +504,6 @@ class SublimeMusicApp(Gtk.Application):
|
|||||||
):
|
):
|
||||||
if settings := state_updates.get("__settings__"):
|
if settings := state_updates.get("__settings__"):
|
||||||
for k, v in settings.items():
|
for k, v in settings.items():
|
||||||
print("SET", k, v)
|
|
||||||
setattr(self.app_config, k, v)
|
setattr(self.app_config, k, v)
|
||||||
del state_updates["__settings__"]
|
del state_updates["__settings__"]
|
||||||
|
|
||||||
@@ -828,6 +837,7 @@ class SublimeMusicApp(Gtk.Application):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def on_app_shutdown(self, app: "SublimeMusicApp"):
|
def on_app_shutdown(self, app: "SublimeMusicApp"):
|
||||||
|
self.exiting = True
|
||||||
if glib_notify_exists:
|
if glib_notify_exists:
|
||||||
Notify.uninit()
|
Notify.uninit()
|
||||||
|
|
||||||
|
@@ -30,6 +30,8 @@ class PlayerEvent:
|
|||||||
PLAY_STATE_CHANGE = 0
|
PLAY_STATE_CHANGE = 0
|
||||||
VOLUME_CHANGE = 1
|
VOLUME_CHANGE = 1
|
||||||
STREAM_CACHE_PROGRESS_CHANGE = 2
|
STREAM_CACHE_PROGRESS_CHANGE = 2
|
||||||
|
CONNECTING = 3
|
||||||
|
CONNECTED = 4
|
||||||
|
|
||||||
type: Type
|
type: Type
|
||||||
playing: Optional[bool] = False
|
playing: Optional[bool] = False
|
||||||
@@ -320,6 +322,7 @@ class ChromecastPlayer(Player):
|
|||||||
return ChromecastPlayer.executor.submit(do_get_chromecasts)
|
return ChromecastPlayer.executor.submit(do_get_chromecasts)
|
||||||
|
|
||||||
def set_playing_chromecast(self, uuid: str):
|
def set_playing_chromecast(self, uuid: str):
|
||||||
|
self.on_player_event(PlayerEvent(PlayerEvent.Type.CONNECTING))
|
||||||
self.chromecast = next(
|
self.chromecast = next(
|
||||||
cc for cc in ChromecastPlayer.chromecasts if cc.device.uuid == UUID(uuid)
|
cc for cc in ChromecastPlayer.chromecasts if cc.device.uuid == UUID(uuid)
|
||||||
)
|
)
|
||||||
@@ -329,7 +332,8 @@ class ChromecastPlayer(Player):
|
|||||||
)
|
)
|
||||||
self.chromecast.register_status_listener(ChromecastPlayer.cast_status_listener)
|
self.chromecast.register_status_listener(ChromecastPlayer.cast_status_listener)
|
||||||
self.chromecast.wait()
|
self.chromecast.wait()
|
||||||
logging.info(f"Using: {self.chromecast.device.friendly_name}")
|
logging.info(f"Connected to Chromecast: {self.chromecast.device.friendly_name}")
|
||||||
|
self.on_player_event(PlayerEvent(PlayerEvent.Type.CONNECTED))
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@@ -455,7 +459,7 @@ class ChromecastPlayer(Player):
|
|||||||
# If it's a local file, then see if we can serve it over the LAN.
|
# If it's a local file, then see if we can serve it over the LAN.
|
||||||
if not stream_scheme:
|
if not stream_scheme:
|
||||||
if self.serve_over_lan:
|
if self.serve_over_lan:
|
||||||
token = base64.b64encode(os.urandom(64)).decode("ascii")
|
token = base64.b64encode(os.urandom(8)).decode("ascii")
|
||||||
for r in (("+", "."), ("/", "-"), ("=", "_")):
|
for r in (("+", "."), ("/", "-"), ("=", "_")):
|
||||||
token = token.replace(*r)
|
token = token.replace(*r)
|
||||||
self.server_thread.set_song_and_token(song.id, token)
|
self.server_thread.set_song_and_token(song.id, token)
|
||||||
|
3
sublime/ui/icons/server-connected-symbolic.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 7.937 7.937" height="30" width="30">
|
||||||
|
<circle class="success" r="3.385" cy="3.969" cx="3.969"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 157 B |
3
sublime/ui/icons/server-error-symbolic.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 7.937 7.937" height="30" width="30">
|
||||||
|
<circle class="error" r="3.385" cy="3.969" cx="3.969"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 155 B |
3
sublime/ui/icons/server-offline-symbolic.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 7.937 7.937" height="30" width="30">
|
||||||
|
<circle class="warning" r="3.385" cy="3.969" cx="3.969"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 157 B |
@@ -1,62 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
sodipodi:docname="server-online.svg"
|
|
||||||
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
|
|
||||||
id="svg8"
|
|
||||||
version="1.1"
|
|
||||||
viewBox="0 0 7.9374995 7.9374995"
|
|
||||||
height="30"
|
|
||||||
width="30">
|
|
||||||
<defs
|
|
||||||
id="defs2" />
|
|
||||||
<sodipodi:namedview
|
|
||||||
inkscape:window-maximized="1"
|
|
||||||
inkscape:window-y="44"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-height="1394"
|
|
||||||
inkscape:window-width="2556"
|
|
||||||
units="px"
|
|
||||||
showgrid="false"
|
|
||||||
inkscape:document-rotation="0"
|
|
||||||
inkscape:current-layer="layer1"
|
|
||||||
inkscape:document-units="mm"
|
|
||||||
inkscape:cy="13.255986"
|
|
||||||
inkscape:cx="24.041419"
|
|
||||||
inkscape:zoom="22.4"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
borderopacity="1.0"
|
|
||||||
bordercolor="#666666"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
id="base" />
|
|
||||||
<metadata
|
|
||||||
id="metadata5">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title></dc:title>
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<g
|
|
||||||
id="layer1"
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
inkscape:label="Layer 1">
|
|
||||||
<circle
|
|
||||||
r="3.3851781"
|
|
||||||
cy="3.96875"
|
|
||||||
cx="3.96875"
|
|
||||||
id="path835"
|
|
||||||
style="fill:#18c918;fill-opacity:1;stroke-width:0.34228" />
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 1.7 KiB |
4
sublime/ui/icons/server-subsonic-connected-symbolic.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="24" width="24">
|
||||||
|
<path d="M6.93 2.13v.04h-.08l-1.39.26c-.15.04-.22.2-.22.38l.22 4.39c0 .22.16.37.34.34l1.69-.57c.11-.03.19-.18.15-.37l-.49-4.17c0-.18-.11-.3-.22-.3zm4.65 4.54c-1.35 0-2.25 0-3.49.68l-2.32.82c-.15.04-.23.19-.27.34a3.6 3.6 0 00-.15 2.1 24 24 0 00-1.46.75c-.45-.71-1.87-.64-1.87.37-.08.5-.42 1.2.22 1.47.3.37-.53.75-.68 1.16-.15.37-.22.75-.7.68-.5.18-.72.63-.34 1.04.37.3 1 0 .9.68.07.49.4.97.6 1.35-.45.37-.38.98-.72 1.43-.26.56.15 1.27.83 1.12a4.73 4.73 0 002.02-.75c3 .82 6.03 1.75 9.13 1.92a6.1 6.1 0 01-.86-4.97l-.02-.21c0-1.02.19-1.62.83-2.25.26-.27.64-.43 1.04-.52a6.1 6.1 0 013.97-1.5 6.01 6.01 0 015.43 3.43 5.5 5.5 0 00-.36-1.94c-.52-1.42-1.87-2.51-3.15-3.26-.75-.38-1.72-.41-2.54-.53l-.27-1.27c.11-.68-.64-.6-1.12-.75l-2.03-.34c-.48-.67-1.87-1.05-2.62-1.05zM5.9 13.05c.38.03.57.67.57 1.83 0 1.1-.23 1.77-.75 2.25-.38.34-.6.3-.83-.18-.45-1.05-.22-2.89.41-3.57.23-.22.42-.37.6-.33zm3.72.22c.56 0 .86.6.93 1.77.04 1.3-.26 2.2-.97 2.73-.34.23-.75.34-.97.23-.3-.12-.64-.98-.68-1.7-.11-1.38.41-2.58 1.27-2.92.13-.06.27-.1.42-.1z" fill="#e4e4e4"/>
|
||||||
|
<path d="M18.2 13.17A5.23 5.23 0 0013 18.4a5.23 5.23 0 005.24 5.24 5.23 5.23 0 005.23-5.24 5.23 5.23 0 00-5.27-5.23z" fill="#c70e0e" class="success"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
@@ -1 +1,4 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"> <path fill="#e4e4e4" d="M6.93 2.13v.04h-.08l-1.39.26c-.15.04-.22.2-.22.38l.22 4.39c0 .22.16.37.34.34l1.69-.57c.11-.03.19-.18.15-.37l-.49-4.17c0-.18-.11-.3-.22-.3zm4.65 4.54c-1.35 0-2.25 0-3.49.68l-2.32.82c-.15.04-.23.19-.27.34a3.6 3.6 0 00-.15 2.1 24 24 0 00-1.46.75c-.45-.71-1.87-.64-1.87.37-.08.5-.42 1.2.22 1.47.3.37-.53.75-.68 1.16-.15.37-.22.75-.7.68-.5.18-.72.63-.34 1.04.37.3 1 0 .9.68.07.49.4.97.6 1.35-.45.37-.38.98-.72 1.43-.26.56.15 1.27.83 1.12a4.73 4.73 0 002.02-.75c3 .82 6.03 1.75 9.13 1.92a6.1 6.1 0 01-.86-4.97l-.02-.21c0-1.02.19-1.62.83-2.25.26-.27.64-.43 1.04-.52a6.1 6.1 0 013.97-1.5 6.01 6.01 0 015.43 3.43 5.5 5.5 0 00-.36-1.94c-.52-1.42-1.87-2.51-3.15-3.26-.75-.38-1.72-.41-2.54-.53l-.27-1.27c.11-.68-.64-.6-1.12-.75l-2.03-.34c-.48-.67-1.87-1.05-2.62-1.05zM5.9 13.05c.38.03.57.67.57 1.83 0 1.1-.23 1.77-.75 2.25-.38.34-.6.3-.83-.18-.45-1.05-.22-2.89.41-3.57.23-.22.42-.37.6-.33zm3.72.22c.56 0 .86.6.93 1.77.04 1.3-.26 2.2-.97 2.73-.34.23-.75.34-.97.23-.3-.12-.64-.98-.68-1.7-.11-1.38.41-2.58 1.27-2.92.13-.06.27-.1.42-.1z"/> <path class="error" fill="#c70e0e" d="M18.2 13.17A5.23 5.23 0 0013 18.4a5.23 5.23 0 005.24 5.24 5.23 5.23 0 005.23-5.24 5.23 5.23 0 00-5.23-5.23 5.23 5.23 0 00-.04 0zM16 14.85l2.24 2.24 2.24-2.24 1.31 1.3-2.24 2.25 2.24 2.25-1.3 1.3-2.25-2.24L16 21.96l-1.31-1.31 2.24-2.25-2.24-2.24z"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" height="24" width="24">
|
||||||
|
<path d="M6.93 2.13v.04h-.08l-1.39.26c-.15.04-.22.2-.22.38l.22 4.39c0 .22.16.37.34.34l1.69-.57c.11-.03.19-.18.15-.37l-.49-4.17c0-.18-.11-.3-.22-.3zm4.65 4.54c-1.35 0-2.25 0-3.49.68l-2.32.82c-.15.04-.23.19-.27.34a3.6 3.6 0 00-.15 2.1 24 24 0 00-1.46.75c-.45-.71-1.87-.64-1.87.37-.08.5-.42 1.2.22 1.47.3.37-.53.75-.68 1.16-.15.37-.22.75-.7.68-.5.18-.72.63-.34 1.04.37.3 1 0 .9.68.07.49.4.97.6 1.35-.45.37-.38.98-.72 1.43-.26.56.15 1.27.83 1.12a4.73 4.73 0 002.02-.75c3 .82 6.03 1.75 9.13 1.92a6.1 6.1 0 01-.86-4.97l-.02-.21c0-1.02.19-1.62.83-2.25.26-.27.64-.43 1.04-.52a6.1 6.1 0 013.97-1.5 6.01 6.01 0 015.43 3.43 5.5 5.5 0 00-.36-1.94c-.52-1.42-1.87-2.51-3.15-3.26-.75-.38-1.72-.41-2.54-.53l-.27-1.27c.11-.68-.64-.6-1.12-.75l-2.03-.34c-.48-.67-1.87-1.05-2.62-1.05zM5.9 13.05c.38.03.57.67.57 1.83 0 1.1-.23 1.77-.75 2.25-.38.34-.6.3-.83-.18-.45-1.05-.22-2.89.41-3.57.23-.22.42-.37.6-.33zm3.72.22c.56 0 .86.6.93 1.77.04 1.3-.26 2.2-.97 2.73-.34.23-.75.34-.97.23-.3-.12-.64-.98-.68-1.7-.11-1.38.41-2.58 1.27-2.92.13-.06.27-.1.42-.1z" fill="#e4e4e4"/>
|
||||||
|
<path d="M18.2 13.17A5.23 5.23 0 0013 18.4a5.23 5.23 0 005.24 5.24 5.23 5.23 0 005.23-5.24 5.23 5.23 0 00-5.27-5.23z" fill="#c70e0e" class="error"/>
|
||||||
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.2 KiB |
4
sublime/ui/icons/server-subsonic-offline-symbolic.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="24" width="24">
|
||||||
|
<path d="M6.93 2.13v.04h-.08l-1.39.26c-.15.04-.22.2-.22.38l.22 4.39c0 .22.16.37.34.34l1.69-.57c.11-.03.19-.18.15-.37l-.49-4.17c0-.18-.11-.3-.22-.3zm4.65 4.54c-1.35 0-2.25 0-3.49.68l-2.32.82c-.15.04-.23.19-.27.34a3.6 3.6 0 00-.15 2.1 24 24 0 00-1.46.75c-.45-.71-1.87-.64-1.87.37-.08.5-.42 1.2.22 1.47.3.37-.53.75-.68 1.16-.15.37-.22.75-.7.68-.5.18-.72.63-.34 1.04.37.3 1 0 .9.68.07.49.4.97.6 1.35-.45.37-.38.98-.72 1.43-.26.56.15 1.27.83 1.12a4.73 4.73 0 002.02-.75c3 .82 6.03 1.75 9.13 1.92a6.1 6.1 0 01-.86-4.97l-.02-.21c0-1.02.19-1.62.83-2.25.26-.27.64-.43 1.04-.52a6.1 6.1 0 013.97-1.5 6.01 6.01 0 015.43 3.43 5.5 5.5 0 00-.36-1.94c-.52-1.42-1.87-2.51-3.15-3.26-.75-.38-1.72-.41-2.54-.53l-.27-1.27c.11-.68-.64-.6-1.12-.75l-2.03-.34c-.48-.67-1.87-1.05-2.62-1.05zM5.9 13.05c.38.03.57.67.57 1.83 0 1.1-.23 1.77-.75 2.25-.38.34-.6.3-.83-.18-.45-1.05-.22-2.89.41-3.57.23-.22.42-.37.6-.33zm3.72.22c.56 0 .86.6.93 1.77.04 1.3-.26 2.2-.97 2.73-.34.23-.75.34-.97.23-.3-.12-.64-.98-.68-1.7-.11-1.38.41-2.58 1.27-2.92.13-.06.27-.1.42-.1z" fill="#e4e4e4"/>
|
||||||
|
<path d="M18.2 13.17A5.23 5.23 0 0013 18.4a5.23 5.23 0 005.24 5.24 5.23 5.23 0 005.23-5.24 5.23 5.23 0 00-5.27-5.23z" fill="#c70e0e" class="warning"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
@@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"> <path fill="#e4e4e4" d="M6.93 2.17h-.08l-1.39.26c-.15.04-.22.2-.22.38l.22 4.39c0 .22.15.37.34.33l1.69-.56c.11-.04.19-.19.15-.37l-.49-4.17c0-.18-.11-.3-.22-.3zm4.65 4.5c-1.35 0-2.25 0-3.49.68l-2.33.82c-.15.04-.22.19-.26.34a3.6 3.6 0 00-.15 2.1 24 24 0 00-1.46.75c-.45-.71-1.88-.64-1.88.37-.07.5-.4 1.2.23 1.47.3.37-.53.75-.68 1.16-.15.37-.22.75-.7.67-.5.2-.72.64-.35 1.05.38.3 1.02 0 .9.68.08.49.42.97.6 1.35-.45.37-.37.97-.7 1.42-.27.57.14 1.28.82 1.13a4.72 4.72 0 002.02-.75c3.83 1.05 7.69 2.29 11.7 1.87 2.78-.18 5.81-1.23 7.28-3.75.71-1.27.67-2.88.18-4.16-.52-1.42-1.87-2.51-3.15-3.26-.75-.38-1.72-.41-2.55-.53l-.26-1.27c.11-.68-.64-.6-1.12-.75l-2.03-.34c-.49-.67-1.87-1.05-2.62-1.05zM5.9 13.05c.38.03.57.67.57 1.83 0 1.1-.23 1.77-.75 2.25-.38.34-.6.3-.83-.18-.45-1.05-.22-2.9.41-3.57.23-.22.42-.37.6-.33zm3.72.22c.56 0 .86.6.93 1.76.04 1.32-.26 2.22-.97 2.74-.34.23-.75.34-.98.23-.3-.12-.63-.98-.67-1.7-.11-1.38.41-2.58 1.27-2.92a1.13 1.13 0 01.42-.1zm5.13.53c.6 0 1.17.18 1.47.63.3.57.41 1.8.22 2.48a3.41 3.41 0 01-1.16 1.72c-.6.38-1.65.45-2.1.12-.56-.45-.75-1.05-.79-2.1 0-1.02.19-1.62.83-2.25.37-.38.97-.57 1.53-.6z"/></svg>
|
|
Before Width: | Height: | Size: 1.2 KiB |
@@ -116,6 +116,26 @@ class MainWindow(Gtk.ApplicationWindow):
|
|||||||
else:
|
else:
|
||||||
self.connected_to_label.set_markup("<i>No Music Source Selected</i>")
|
self.connected_to_label.set_markup("<i>No Music Source Selected</i>")
|
||||||
|
|
||||||
|
if AdapterManager.ground_truth_adapter_is_networked:
|
||||||
|
status_label = ""
|
||||||
|
if app_config.offline_mode:
|
||||||
|
status_label = "Offline"
|
||||||
|
elif AdapterManager.get_ping_status():
|
||||||
|
status_label = "Connected"
|
||||||
|
else:
|
||||||
|
status_label = "Error"
|
||||||
|
|
||||||
|
self.server_connection_menu_button.set_icon(
|
||||||
|
f"server-subsonic-{status_label.lower()}-symbolic"
|
||||||
|
)
|
||||||
|
self.connection_status_icon.set_from_icon_name(
|
||||||
|
f"server-{status_label.lower()}-symbolic", Gtk.IconSize.BUTTON
|
||||||
|
)
|
||||||
|
self.connection_status_label.set_text(status_label)
|
||||||
|
self.connected_status_box.show_all()
|
||||||
|
else:
|
||||||
|
self.connected_status_box.hide()
|
||||||
|
|
||||||
self._updating_settings = True
|
self._updating_settings = True
|
||||||
|
|
||||||
# Main Settings
|
# Main Settings
|
||||||
@@ -213,7 +233,7 @@ class MainWindow(Gtk.ApplicationWindow):
|
|||||||
# Server icon and change server dropdown
|
# Server icon and change server dropdown
|
||||||
self.server_connection_popover = self._create_server_connection_popover()
|
self.server_connection_popover = self._create_server_connection_popover()
|
||||||
self.server_connection_menu_button = IconMenuButton(
|
self.server_connection_menu_button = IconMenuButton(
|
||||||
"server-subsonic-symbolic",
|
"server-subsonic-offline-symbolic",
|
||||||
tooltip_text="Server connection settings",
|
tooltip_text="Server connection settings",
|
||||||
popover=self.server_connection_popover,
|
popover=self.server_connection_popover,
|
||||||
)
|
)
|
||||||
@@ -346,24 +366,24 @@ class MainWindow(Gtk.ApplicationWindow):
|
|||||||
)
|
)
|
||||||
vbox.add(self.connected_to_label)
|
vbox.add(self.connected_to_label)
|
||||||
|
|
||||||
connected_status_box = Gtk.Box(
|
self.connected_status_box = Gtk.Box(
|
||||||
orientation=Gtk.Orientation.HORIZONTAL, name="connected-status-row"
|
orientation=Gtk.Orientation.HORIZONTAL, name="connected-status-row"
|
||||||
)
|
)
|
||||||
connected_status_box.pack_start(Gtk.Box(), True, True, 0)
|
self.connected_status_box.pack_start(Gtk.Box(), True, True, 0)
|
||||||
|
|
||||||
self.connection_status_icon = Gtk.Image.new_from_icon_name(
|
self.connection_status_icon = Gtk.Image.new_from_icon_name(
|
||||||
"server-online", Gtk.IconSize.BUTTON
|
"server-online", Gtk.IconSize.BUTTON
|
||||||
)
|
)
|
||||||
self.connection_status_icon.set_name("online-status-icon")
|
self.connection_status_icon.set_name("online-status-icon")
|
||||||
connected_status_box.add(self.connection_status_icon)
|
self.connected_status_box.add(self.connection_status_icon)
|
||||||
|
|
||||||
self.connection_status_label = Gtk.Label(
|
self.connection_status_label = Gtk.Label(
|
||||||
label="Connected", name="connection-status-label"
|
label="Connected", name="connection-status-label"
|
||||||
)
|
)
|
||||||
connected_status_box.add(self.connection_status_label)
|
self.connected_status_box.add(self.connection_status_label)
|
||||||
|
|
||||||
connected_status_box.pack_start(Gtk.Box(), True, True, 0)
|
self.connected_status_box.pack_start(Gtk.Box(), True, True, 0)
|
||||||
vbox.add(connected_status_box)
|
vbox.add(self.connected_status_box)
|
||||||
|
|
||||||
# Offline Mode
|
# Offline Mode
|
||||||
offline_box, self.offline_mode_switch = self._create_toggle_menu_button(
|
offline_box, self.offline_mode_switch = self._create_toggle_menu_button(
|
||||||
|
@@ -115,6 +115,12 @@ class PlayerControls(Gtk.ActionBar):
|
|||||||
self.play_button.set_sensitive(has_current_song)
|
self.play_button.set_sensitive(has_current_song)
|
||||||
self.next_button.set_sensitive(has_current_song and has_next_song)
|
self.next_button.set_sensitive(has_current_song and has_next_song)
|
||||||
|
|
||||||
|
self.device_button.set_icon(
|
||||||
|
"chromecast{}-symbolic".format(
|
||||||
|
"" if app_config.state.current_device == "this device" else "-connected"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
# Volume button and slider
|
# Volume button and slider
|
||||||
if app_config.state.is_muted:
|
if app_config.state.is_muted:
|
||||||
icon_name = "muted"
|
icon_name = "muted"
|
||||||
@@ -609,14 +615,14 @@ class PlayerControls(Gtk.ActionBar):
|
|||||||
|
|
||||||
# Device button (for chromecast)
|
# Device button (for chromecast)
|
||||||
self.device_button = IconButton(
|
self.device_button = IconButton(
|
||||||
"chromecast-connected-symbolic",
|
"chromecast-symbolic",
|
||||||
"Show available audio output devices",
|
"Show available audio output devices",
|
||||||
icon_size=Gtk.IconSize.LARGE_TOOLBAR,
|
icon_size=Gtk.IconSize.LARGE_TOOLBAR,
|
||||||
)
|
)
|
||||||
self.device_button.connect("clicked", self.on_device_click)
|
self.device_button.connect("clicked", self.on_device_click)
|
||||||
box.pack_start(self.device_button, False, True, 5)
|
box.pack_start(self.device_button, False, True, 5)
|
||||||
|
|
||||||
self.device_popover = Gtk.PopoverMenu(modal=False, name="device-popover",)
|
self.device_popover = Gtk.PopoverMenu(modal=False, name="device-popover")
|
||||||
self.device_popover.set_relative_to(self.device_button)
|
self.device_popover.set_relative_to(self.device_button)
|
||||||
|
|
||||||
device_popover_box = Gtk.Box(
|
device_popover_box = Gtk.Box(
|
||||||
|
@@ -616,8 +616,7 @@ class PlaylistDetailPanel(Gtk.Overlay):
|
|||||||
Gtk.ResponseType.CANCEL,
|
Gtk.ResponseType.CANCEL,
|
||||||
)
|
)
|
||||||
confirm_dialog.format_secondary_markup(
|
confirm_dialog.format_secondary_markup(
|
||||||
"Are you sure you want to delete the "
|
'Are you sure you want to delete the "{playlist.name}" playlist?'
|
||||||
f'"{playlist.name}" playlist?'
|
|
||||||
)
|
)
|
||||||
result = confirm_dialog.run()
|
result = confirm_dialog.run()
|
||||||
confirm_dialog.destroy()
|
confirm_dialog.destroy()
|
||||||
|