Compare commits

..

6 commits

20 changed files with 429 additions and 388 deletions

View file

@ -0,0 +1,87 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk+" version="4.8"/>
<requires lib="adw" version="1.2" />
<object class="GtkAdjustment" id="server-port-adjustment">
<property name="lower">1024</property>
<property name="upper">9999</property>
<property name="value">6600</property>
<property name="step-increment">1</property>
<property name="page-increment">100</property>
</object>
<object class="GtkBox" id="toolbar">
<property name="orientation">horizontal</property>
<property name="spacing">6</property>
</object>
<template class="McgConnectionPanel" parent="AdwBin">
<child>
<object class="AdwStatusPage">
<property name="title">Connect to MPD</property>
<child>
<object class="GtkBox">
<property name="hexpand">false</property>
<property name="halign">center</property>
<child>
<object class="GtkListBox" id="zeroconf_list">
<property name="hexpand">true</property>
<child type="placeholder">
<object class="GtkLabel">
<property name="label" translatable="yes">No service found</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkListBox">
<property name="hexpand">true</property>
<property name="selection-mode">none</property>
<style>
<class name="boxed-list"/>
</style>
<child>
<object class="AdwEntryRow" id="host_row">
<property name="title" translatable="yes">Host</property>
<property name="show-apply-button">true</property>
<signal name="apply" handler="on_host_entry_apply"/>
</object>
</child>
<child>
<object class="GtkBox">
<property name="orientation">vertical</property>
<style>
<class name="header"/>
</style>
<child>
<object class="GtkLabel">
<property name="label" translatable="yes">Port</property>
<property name="halign">start</property>
<property name="hexpand">false</property>
<style>
<class name="subtitle"/>
</style>
</object>
</child>
<child>
<object class="GtkSpinButton" id="port_spinner">
<property name="name">port_spinner</property>
<property name="value">6600</property>
<property name="adjustment">server-port-adjustment</property>
<signal name="value-changed" handler="on_port_spinner_value_changed"/>
</object>
</child>
</object>
</child>
<child>
<object class="AdwPasswordEntryRow" id="password_row">
<property name="title" translatable="yes">Password</property>
<property name="show-apply-button">true</property>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</template>
</interface>

View file

@ -221,10 +221,14 @@
<property name="child"> <property name="child">
<object class="GtkBox"> <object class="GtkBox">
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<property name="vexpand">true</property>
<property name="hexpand">true</property>
<child> <child>
<object class="GtkPicture"> <object class="GtkPicture">
<property name="content-fit">contain</property> <property name="content-fit">contain</property>
<property name="can-shrink">false</property> <property name="can-shrink">false</property>
<property name="vexpand">true</property>
<property name="hexpand">true</property>
<binding name="tooltip-markup"> <binding name="tooltip-markup">
<lookup name="tooltip" type="GridItem"> <lookup name="tooltip" type="GridItem">
<lookup name="item">GtkListItem</lookup> <lookup name="item">GtkListItem</lookup>

View file

@ -2,13 +2,6 @@
<interface> <interface>
<requires lib="gtk+" version="4.8"/> <requires lib="gtk+" version="4.8"/>
<requires lib="adw" version="1.2" /> <requires lib="adw" version="1.2" />
<object class="GtkAdjustment" id="server-port-adjustment">
<property name="lower">1024</property>
<property name="upper">9999</property>
<property name="value">6600</property>
<property name="step-increment">1</property>
<property name="page-increment">100</property>
</object>
<object class="GtkBox" id="toolbar"> <object class="GtkBox" id="toolbar">
<property name="orientation">horizontal</property> <property name="orientation">horizontal</property>
<property name="halign">end</property> <property name="halign">end</property>
@ -38,84 +31,8 @@
<object class="GtkStack" id="stack"> <object class="GtkStack" id="stack">
<child> <child>
<object class="GtkStackPage"> <object class="GtkStackPage">
<property name="name">connection</property>
<property name="title" translatable="yes">Connection</property>
<property name="child">
<object class="AdwStatusPage" id="connection_status_page">
<property name="icon-name">network-wired-symbolic</property>
<child>
<object class="GtkBox">
<property name="hexpand">false</property>
<property name="halign">center</property>
<child>
<object class="GtkListBox" id="zeroconf_list">
<property name="hexpand">true</property>
<child type="placeholder">
<object class="GtkLabel">
<property name="label" translatable="yes">No service found</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkListBox">
<property name="hexpand">true</property>
<property name="selection-mode">none</property>
<style>
<class name="boxed-list"/>
</style>
<child>
<object class="AdwEntryRow" id="host_row">
<property name="title" translatable="yes">Host</property>
<property name="show-apply-button">true</property>
<signal name="apply" handler="on_host_entry_apply"/>
</object>
</child>
<child>
<object class="GtkBox">
<property name="orientation">vertical</property>
<style>
<class name="header"/>
</style>
<child>
<object class="GtkLabel">
<property name="label" translatable="yes">Port</property>
<property name="halign">start</property>
<property name="hexpand">false</property>
<style>
<class name="subtitle"/>
</style>
</object>
</child>
<child>
<object class="GtkSpinButton" id="port_spinner">
<property name="name">port_spinner</property>
<property name="value">6600</property>
<property name="adjustment">server-port-adjustment</property>
<signal name="value-changed" handler="on_port_spinner_value_changed"/>
</object>
</child>
</object>
</child>
<child>
<object class="AdwPasswordEntryRow" id="password_row">
<property name="title" translatable="yes">Password</property>
<property name="show-apply-button">true</property>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</property>
</object>
</child>
<child>
<object class="GtkStackPage" id="status_page">
<property name="name">status</property> <property name="name">status</property>
<property name="title" translatable="yes">Status</property> <property name="title" translatable="yes">Status</property>
<property name="visible">false</property>
<property name="child"> <property name="child">
<object class="AdwStatusPage"> <object class="AdwStatusPage">
<property name="icon-name">dialog-information-symbolic</property> <property name="icon-name">dialog-information-symbolic</property>
@ -232,10 +149,9 @@
</object> </object>
</child> </child>
<child> <child>
<object class="GtkStackPage" id="stats_page"> <object class="GtkStackPage">
<property name="name">stats</property> <property name="name">stats</property>
<property name="title" translatable="yes">Statistics</property> <property name="title" translatable="yes">Statistics</property>
<property name="visible">false</property>
<property name="child"> <property name="child">
<object class="AdwStatusPage"> <object class="AdwStatusPage">
<property name="icon-name">starred-symbolic</property> <property name="icon-name">starred-symbolic</property>
@ -382,10 +298,9 @@
</object> </object>
</child> </child>
<child> <child>
<object class="GtkStackPage" id="devices_page"> <object class="GtkStackPage">
<property name="name">devices</property> <property name="name">devices</property>
<property name="title" translatable="yes">Audio Devices</property> <property name="title" translatable="yes">Audio Devices</property>
<property name="visible">false</property>
<property name="child"> <property name="child">
<object class="AdwStatusPage"> <object class="AdwStatusPage">
<property name="icon-name">audio-speakers-symbolic</property> <property name="icon-name">audio-speakers-symbolic</property>

View file

@ -48,6 +48,10 @@
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<child> <child>
<object class="AdwToastOverlay" id="info_toast"> <object class="AdwToastOverlay" id="info_toast">
<child>
<object class="GtkStack" id="content_stack">
<property name="name">content_stack</property>
<property name="vexpand">true</property>
<child> <child>
<object class="AdwViewStack" id="panel_stack"> <object class="AdwViewStack" id="panel_stack">
<property name="vexpand">true</property> <property name="vexpand">true</property>
@ -56,6 +60,8 @@
</child> </child>
</object> </object>
</child> </child>
</object>
</child>
<child> <child>
<object class="AdwViewSwitcherBar"> <object class="AdwViewSwitcherBar">
<property name="stack">panel_stack</property> <property name="stack">panel_stack</property>

View file

@ -5,6 +5,7 @@
<file>noise-texture.png</file> <file>noise-texture.png</file>
<file>ui/window.ui</file> <file>ui/window.ui</file>
<file>ui/shortcuts-dialog.ui</file> <file>ui/shortcuts-dialog.ui</file>
<file>ui/connection-panel.ui</file>
<file>ui/album-headerbar.ui</file> <file>ui/album-headerbar.ui</file>
<file>ui/server-panel.ui</file> <file>ui/server-panel.ui</file>
<file>ui/cover-panel.ui</file> <file>ui/cover-panel.ui</file>

View file

@ -1,5 +1,5 @@
project('mcg', project('mcg',
version: '4.0.1', version: '4.0.2',
meson_version: '>= 0.59.0', meson_version: '>= 0.59.0',
default_options: [ default_options: [
'warning_level=2', 'warning_level=2',

View file

@ -1,4 +1,5 @@
data/ui/album-headerbar.ui data/ui/album-headerbar.ui
data/ui/connection-panel.ui
data/ui/cover-panel.ui data/ui/cover-panel.ui
data/ui/library-panel.ui data/ui/library-panel.ui
data/ui/playlist-panel.ui data/ui/playlist-panel.ui
@ -8,6 +9,7 @@ data/ui/window.ui
src/albumheaderbar.py src/albumheaderbar.py
src/application.py src/application.py
src/client.py src/client.py
src/connectionpanel.py
src/coverpanel.py src/coverpanel.py
src/librarypanel.py src/librarypanel.py
src/main.py src/main.py

BIN
po/de.mo

Binary file not shown.

View file

@ -2,8 +2,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: CoverGrid (mcg)\n" "Project-Id-Version: CoverGrid (mcg)\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-05-24 16:44+0200\n" "POT-Creation-Date: 2024-05-22 14:39+0200\n"
"PO-Revision-Date: 2024-05-24 16:45+0200\n" "PO-Revision-Date: 2024-05-22 14:39+0200\n"
"Last-Translator: coderkun <olli@suruatoel.xyz>\n" "Last-Translator: coderkun <olli@suruatoel.xyz>\n"
"Language-Team: \n" "Language-Team: \n"
"Language: de_DE\n" "Language: de_DE\n"
@ -15,6 +15,22 @@ msgstr ""
"X-Poedit-Basepath: ../../..\n" "X-Poedit-Basepath: ../../..\n"
"X-Poedit-SourceCharset: UTF-8\n" "X-Poedit-SourceCharset: UTF-8\n"
#: data/ui/connection-panel.ui:29
msgid "No service found"
msgstr "Keine Dienste gefunden"
#: data/ui/connection-panel.ui:43
msgid "Host"
msgstr "Host"
#: data/ui/connection-panel.ui:56
msgid "Port"
msgstr "Port"
#: data/ui/connection-panel.ui:76
msgid "Password"
msgstr "Passwort"
#: data/ui/cover-panel.ui:12 data/ui/shortcuts-dialog.ui:88 #: data/ui/cover-panel.ui:12 data/ui/shortcuts-dialog.ui:88
msgid "Show the cover in fullscreen mode" msgid "Show the cover in fullscreen mode"
msgstr "Das Cover im Vollbildmodus anzeigen" msgstr "Das Cover im Vollbildmodus anzeigen"
@ -63,15 +79,15 @@ msgstr "Einstellungen und Aktionen"
msgid "search library" msgid "search library"
msgstr "Bibliothek durchsuchen" msgstr "Bibliothek durchsuchen"
#: data/ui/library-panel.ui:264 data/ui/playlist-panel.ui:107 #: data/ui/library-panel.ui:263 data/ui/playlist-panel.ui:107
msgid "cancel" msgid "cancel"
msgstr "abbrechen" msgstr "abbrechen"
#: data/ui/library-panel.ui:271 data/ui/library-panel.ui:328 #: data/ui/library-panel.ui:270 data/ui/library-panel.ui:327
msgid "queue" msgid "queue"
msgstr "einreihen" msgstr "einreihen"
#: data/ui/library-panel.ui:321 data/ui/playlist-panel.ui:163 #: data/ui/library-panel.ui:320 data/ui/playlist-panel.ui:163
msgid "play" msgid "play"
msgstr "abspielen" msgstr "abspielen"
@ -83,80 +99,60 @@ msgstr "Die Wiedergabeliste leeren"
msgid "remove" msgid "remove"
msgstr "entfernen" msgstr "entfernen"
#: data/ui/server-panel.ui:42 #: data/ui/server-panel.ui:35
msgid "Connection"
msgstr "Verbindung"
#: data/ui/server-panel.ui:55
msgid "No service found"
msgstr "Keine Dienste gefunden"
#: data/ui/server-panel.ui:69
msgid "Host"
msgstr "Host"
#: data/ui/server-panel.ui:82
msgid "Port"
msgstr "Port"
#: data/ui/server-panel.ui:102
msgid "Password"
msgstr "Passwort"
#: data/ui/server-panel.ui:117
msgid "Status" msgid "Status"
msgstr "Status" msgstr "Status"
#: data/ui/server-panel.ui:132 #: data/ui/server-panel.ui:49
msgid "File:" msgid "File:"
msgstr "Datei:" msgstr "Datei:"
#: data/ui/server-panel.ui:143 #: data/ui/server-panel.ui:60
msgid "Audio:" msgid "Audio:"
msgstr "Audio:" msgstr "Audio:"
#: data/ui/server-panel.ui:154 #: data/ui/server-panel.ui:71
msgid "Bitrate:" msgid "Bitrate:"
msgstr "Bitrate:" msgstr "Bitrate:"
#: data/ui/server-panel.ui:165 #: data/ui/server-panel.ui:82
msgid "Error:" msgid "Error:"
msgstr "Fehler:" msgstr "Fehler:"
#: data/ui/server-panel.ui:175 data/ui/server-panel.ui:189 #: data/ui/server-panel.ui:92 data/ui/server-panel.ui:106
#: data/ui/server-panel.ui:203 data/ui/server-panel.ui:217 #: data/ui/server-panel.ui:120 data/ui/server-panel.ui:134
msgid "<i>none</i>" msgid "<i>none</i>"
msgstr "<i>nichts</i>" msgstr "<i>nichts</i>"
#: data/ui/server-panel.ui:237 #: data/ui/server-panel.ui:154
msgid "Statistics" msgid "Statistics"
msgstr "Statistiken" msgstr "Statistiken"
#: data/ui/server-panel.ui:262 #: data/ui/server-panel.ui:178
msgid "Artists" msgid "Artists"
msgstr "Künstler" msgstr "Künstler"
#: data/ui/server-panel.ui:282 #: data/ui/server-panel.ui:198
msgid "Albums" msgid "Albums"
msgstr "Alben" msgstr "Alben"
#: data/ui/server-panel.ui:302 #: data/ui/server-panel.ui:218
msgid "Songs" msgid "Songs"
msgstr "Songs" msgstr "Songs"
#: data/ui/server-panel.ui:322 #: data/ui/server-panel.ui:238
msgid "Seconds" msgid "Seconds"
msgstr "Sekunden" msgstr "Sekunden"
#: data/ui/server-panel.ui:351 #: data/ui/server-panel.ui:267
msgid "Seconds played" msgid "Seconds played"
msgstr "Sekunden gespielt" msgstr "Sekunden gespielt"
#: data/ui/server-panel.ui:371 #: data/ui/server-panel.ui:287
msgid "Seconds running" msgid "Seconds running"
msgstr "Sekunden laufend" msgstr "Sekunden laufend"
#: data/ui/server-panel.ui:387 #: data/ui/server-panel.ui:303
msgid "Audio Devices" msgid "Audio Devices"
msgstr "Audiogeräte" msgstr "Audiogeräte"
@ -212,18 +208,18 @@ msgstr "Cover-Paneel"
msgid "Library Panel" msgid "Library Panel"
msgstr "Bibliothekspaneel" msgstr "Bibliothekspaneel"
#: src/connectionpanel.py:51
msgid "use"
msgstr "verwenden"
#: src/librarypanel.py:291 #: src/librarypanel.py:291
msgid "Loading albums" msgid "Loading albums"
msgstr "Alben werden geladen" msgstr "Alben werden geladen"
#: src/librarypanel.py:370 #: src/librarypanel.py:379
msgid "Loading images" msgid "Loading images"
msgstr "Bilder werden geladen" msgstr "Bilder werden geladen"
#: src/serverpanel.py:77
msgid "use"
msgstr "verwenden"
#: src/utils.py:50 src/utils.py:67 #: src/utils.py:50 src/utils.py:67
msgid "{} feat. {}" msgid "{} feat. {}"
msgstr "{} mit {}" msgstr "{} mit {}"
@ -232,20 +228,19 @@ msgstr "{} mit {}"
msgid "{}:{} minutes" msgid "{}:{} minutes"
msgstr "{}:{} Minuten" msgstr "{}:{} Minuten"
#. Stack #: src/window.py:114
#: src/window.py:106
msgid "Server" msgid "Server"
msgstr "Server" msgstr "Server"
#: src/window.py:107 #: src/window.py:115
msgid "Cover" msgid "Cover"
msgstr "Cover" msgstr "Cover"
#: src/window.py:108 #: src/window.py:116
msgid "Playlist" msgid "Playlist"
msgstr "Wiedergabeliste" msgstr "Wiedergabeliste"
#: src/window.py:109 #: src/window.py:117
msgid "Library" msgid "Library"
msgstr "Bibliothek" msgstr "Bibliothek"
@ -280,6 +275,9 @@ msgstr "Bibliothek"
#~ msgid "Search Library" #~ msgid "Search Library"
#~ msgstr "Bibliothek durchsuchen" #~ msgstr "Bibliothek durchsuchen"
#~ msgid "Connection"
#~ msgstr "Verbindung"
#~ msgid "Keyboard Shortcuts" #~ msgid "Keyboard Shortcuts"
#~ msgstr "Tastenkombinationen" #~ msgstr "Tastenkombinationen"

BIN
po/en.mo

Binary file not shown.

View file

@ -2,8 +2,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: CoverGrid (mcg)\n" "Project-Id-Version: CoverGrid (mcg)\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-05-24 16:44+0200\n" "POT-Creation-Date: 2024-05-22 14:39+0200\n"
"PO-Revision-Date: 2024-05-24 16:45+0200\n" "PO-Revision-Date: 2024-05-22 14:39+0200\n"
"Last-Translator: coderkun <olli@suruatoel.xyz>\n" "Last-Translator: coderkun <olli@suruatoel.xyz>\n"
"Language-Team: \n" "Language-Team: \n"
"Language: en\n" "Language: en\n"
@ -16,6 +16,22 @@ msgstr ""
"X-Poedit-SearchPath-0: mcg\n" "X-Poedit-SearchPath-0: mcg\n"
"X-Poedit-SearchPath-1: data/ui\n" "X-Poedit-SearchPath-1: data/ui\n"
#: data/ui/connection-panel.ui:29
msgid "No service found"
msgstr "No service found"
#: data/ui/connection-panel.ui:43
msgid "Host"
msgstr "Host"
#: data/ui/connection-panel.ui:56
msgid "Port"
msgstr "Port"
#: data/ui/connection-panel.ui:76
msgid "Password"
msgstr "Password"
#: data/ui/cover-panel.ui:12 data/ui/shortcuts-dialog.ui:88 #: data/ui/cover-panel.ui:12 data/ui/shortcuts-dialog.ui:88
msgid "Show the cover in fullscreen mode" msgid "Show the cover in fullscreen mode"
msgstr "Show the cover in fullscreen mode" msgstr "Show the cover in fullscreen mode"
@ -64,15 +80,15 @@ msgstr "Settings and actions"
msgid "search library" msgid "search library"
msgstr "search library" msgstr "search library"
#: data/ui/library-panel.ui:264 data/ui/playlist-panel.ui:107 #: data/ui/library-panel.ui:263 data/ui/playlist-panel.ui:107
msgid "cancel" msgid "cancel"
msgstr "cancel" msgstr "cancel"
#: data/ui/library-panel.ui:271 data/ui/library-panel.ui:328 #: data/ui/library-panel.ui:270 data/ui/library-panel.ui:327
msgid "queue" msgid "queue"
msgstr "queue" msgstr "queue"
#: data/ui/library-panel.ui:321 data/ui/playlist-panel.ui:163 #: data/ui/library-panel.ui:320 data/ui/playlist-panel.ui:163
msgid "play" msgid "play"
msgstr "play" msgstr "play"
@ -84,80 +100,60 @@ msgstr "Clear the playlist"
msgid "remove" msgid "remove"
msgstr "remove" msgstr "remove"
#: data/ui/server-panel.ui:42 #: data/ui/server-panel.ui:35
msgid "Connection"
msgstr "Connection"
#: data/ui/server-panel.ui:55
msgid "No service found"
msgstr "No service found"
#: data/ui/server-panel.ui:69
msgid "Host"
msgstr "Host"
#: data/ui/server-panel.ui:82
msgid "Port"
msgstr "Port"
#: data/ui/server-panel.ui:102
msgid "Password"
msgstr "Password"
#: data/ui/server-panel.ui:117
msgid "Status" msgid "Status"
msgstr "Status" msgstr "Status"
#: data/ui/server-panel.ui:132 #: data/ui/server-panel.ui:49
msgid "File:" msgid "File:"
msgstr "File:" msgstr "File:"
#: data/ui/server-panel.ui:143 #: data/ui/server-panel.ui:60
msgid "Audio:" msgid "Audio:"
msgstr "Audio:" msgstr "Audio:"
#: data/ui/server-panel.ui:154 #: data/ui/server-panel.ui:71
msgid "Bitrate:" msgid "Bitrate:"
msgstr "Bitrate:" msgstr "Bitrate:"
#: data/ui/server-panel.ui:165 #: data/ui/server-panel.ui:82
msgid "Error:" msgid "Error:"
msgstr "Error:" msgstr "Error:"
#: data/ui/server-panel.ui:175 data/ui/server-panel.ui:189 #: data/ui/server-panel.ui:92 data/ui/server-panel.ui:106
#: data/ui/server-panel.ui:203 data/ui/server-panel.ui:217 #: data/ui/server-panel.ui:120 data/ui/server-panel.ui:134
msgid "<i>none</i>" msgid "<i>none</i>"
msgstr "<i>none</i>" msgstr "<i>none</i>"
#: data/ui/server-panel.ui:237 #: data/ui/server-panel.ui:154
msgid "Statistics" msgid "Statistics"
msgstr "Statistics" msgstr "Statistics"
#: data/ui/server-panel.ui:262 #: data/ui/server-panel.ui:178
msgid "Artists" msgid "Artists"
msgstr "Artists" msgstr "Artists"
#: data/ui/server-panel.ui:282 #: data/ui/server-panel.ui:198
msgid "Albums" msgid "Albums"
msgstr "Albums" msgstr "Albums"
#: data/ui/server-panel.ui:302 #: data/ui/server-panel.ui:218
msgid "Songs" msgid "Songs"
msgstr "Songs" msgstr "Songs"
#: data/ui/server-panel.ui:322 #: data/ui/server-panel.ui:238
msgid "Seconds" msgid "Seconds"
msgstr "Seconds" msgstr "Seconds"
#: data/ui/server-panel.ui:351 #: data/ui/server-panel.ui:267
msgid "Seconds played" msgid "Seconds played"
msgstr "Seconds" msgstr "Seconds"
#: data/ui/server-panel.ui:371 #: data/ui/server-panel.ui:287
msgid "Seconds running" msgid "Seconds running"
msgstr "Seconds running" msgstr "Seconds running"
#: data/ui/server-panel.ui:387 #: data/ui/server-panel.ui:303
msgid "Audio Devices" msgid "Audio Devices"
msgstr "Audio Devices" msgstr "Audio Devices"
@ -213,18 +209,18 @@ msgstr "Cover Panel"
msgid "Library Panel" msgid "Library Panel"
msgstr "Library Panel" msgstr "Library Panel"
#: src/connectionpanel.py:51
msgid "use"
msgstr "use"
#: src/librarypanel.py:291 #: src/librarypanel.py:291
msgid "Loading albums" msgid "Loading albums"
msgstr "Loading albums" msgstr "Loading albums"
#: src/librarypanel.py:370 #: src/librarypanel.py:379
msgid "Loading images" msgid "Loading images"
msgstr "Loading images" msgstr "Loading images"
#: src/serverpanel.py:77
msgid "use"
msgstr "use"
#: src/utils.py:50 src/utils.py:67 #: src/utils.py:50 src/utils.py:67
msgid "{} feat. {}" msgid "{} feat. {}"
msgstr "{} feat. {}" msgstr "{} feat. {}"
@ -233,20 +229,19 @@ msgstr "{} feat. {}"
msgid "{}:{} minutes" msgid "{}:{} minutes"
msgstr "{}:{} minutes" msgstr "{}:{} minutes"
#. Stack #: src/window.py:114
#: src/window.py:106
msgid "Server" msgid "Server"
msgstr "Server" msgstr "Server"
#: src/window.py:107 #: src/window.py:115
msgid "Cover" msgid "Cover"
msgstr "Cover" msgstr "Cover"
#: src/window.py:108 #: src/window.py:116
msgid "Playlist" msgid "Playlist"
msgstr "Playlist" msgstr "Playlist"
#: src/window.py:109 #: src/window.py:117
msgid "Library" msgid "Library"
msgstr "Library" msgstr "Library"
@ -281,6 +276,9 @@ msgstr "Library"
#~ msgid "Search Library" #~ msgid "Search Library"
#~ msgstr "Search Library" #~ msgstr "Search Library"
#~ msgid "Connection"
#~ msgstr "Connection"
#~ msgid "Keyboard Shortcuts" #~ msgid "Keyboard Shortcuts"
#~ msgstr "Keyboard Shortcuts" #~ msgstr "Keyboard Shortcuts"

View file

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: mcg\n" "Project-Id-Version: mcg\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-05-24 16:44+0200\n" "POT-Creation-Date: 2024-05-22 14:39+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -17,6 +17,22 @@ msgstr ""
"Content-Type: text/plain; charset=CHARSET\n" "Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
#: data/ui/connection-panel.ui:29
msgid "No service found"
msgstr ""
#: data/ui/connection-panel.ui:43
msgid "Host"
msgstr ""
#: data/ui/connection-panel.ui:56
msgid "Port"
msgstr ""
#: data/ui/connection-panel.ui:76
msgid "Password"
msgstr ""
#: data/ui/cover-panel.ui:12 data/ui/shortcuts-dialog.ui:88 #: data/ui/cover-panel.ui:12 data/ui/shortcuts-dialog.ui:88
msgid "Show the cover in fullscreen mode" msgid "Show the cover in fullscreen mode"
msgstr "" msgstr ""
@ -65,15 +81,15 @@ msgstr ""
msgid "search library" msgid "search library"
msgstr "" msgstr ""
#: data/ui/library-panel.ui:264 data/ui/playlist-panel.ui:107 #: data/ui/library-panel.ui:263 data/ui/playlist-panel.ui:107
msgid "cancel" msgid "cancel"
msgstr "" msgstr ""
#: data/ui/library-panel.ui:271 data/ui/library-panel.ui:328 #: data/ui/library-panel.ui:270 data/ui/library-panel.ui:327
msgid "queue" msgid "queue"
msgstr "" msgstr ""
#: data/ui/library-panel.ui:321 data/ui/playlist-panel.ui:163 #: data/ui/library-panel.ui:320 data/ui/playlist-panel.ui:163
msgid "play" msgid "play"
msgstr "" msgstr ""
@ -85,80 +101,60 @@ msgstr ""
msgid "remove" msgid "remove"
msgstr "" msgstr ""
#: data/ui/server-panel.ui:42 #: data/ui/server-panel.ui:35
msgid "Connection"
msgstr ""
#: data/ui/server-panel.ui:55
msgid "No service found"
msgstr ""
#: data/ui/server-panel.ui:69
msgid "Host"
msgstr ""
#: data/ui/server-panel.ui:82
msgid "Port"
msgstr ""
#: data/ui/server-panel.ui:102
msgid "Password"
msgstr ""
#: data/ui/server-panel.ui:117
msgid "Status" msgid "Status"
msgstr "" msgstr ""
#: data/ui/server-panel.ui:132 #: data/ui/server-panel.ui:49
msgid "File:" msgid "File:"
msgstr "" msgstr ""
#: data/ui/server-panel.ui:143 #: data/ui/server-panel.ui:60
msgid "Audio:" msgid "Audio:"
msgstr "" msgstr ""
#: data/ui/server-panel.ui:154 #: data/ui/server-panel.ui:71
msgid "Bitrate:" msgid "Bitrate:"
msgstr "" msgstr ""
#: data/ui/server-panel.ui:165 #: data/ui/server-panel.ui:82
msgid "Error:" msgid "Error:"
msgstr "" msgstr ""
#: data/ui/server-panel.ui:175 data/ui/server-panel.ui:189 #: data/ui/server-panel.ui:92 data/ui/server-panel.ui:106
#: data/ui/server-panel.ui:203 data/ui/server-panel.ui:217 #: data/ui/server-panel.ui:120 data/ui/server-panel.ui:134
msgid "<i>none</i>" msgid "<i>none</i>"
msgstr "" msgstr ""
#: data/ui/server-panel.ui:237 #: data/ui/server-panel.ui:154
msgid "Statistics" msgid "Statistics"
msgstr "" msgstr ""
#: data/ui/server-panel.ui:262 #: data/ui/server-panel.ui:178
msgid "Artists" msgid "Artists"
msgstr "" msgstr ""
#: data/ui/server-panel.ui:282 #: data/ui/server-panel.ui:198
msgid "Albums" msgid "Albums"
msgstr "" msgstr ""
#: data/ui/server-panel.ui:302 #: data/ui/server-panel.ui:218
msgid "Songs" msgid "Songs"
msgstr "" msgstr ""
#: data/ui/server-panel.ui:322 #: data/ui/server-panel.ui:238
msgid "Seconds" msgid "Seconds"
msgstr "" msgstr ""
#: data/ui/server-panel.ui:351 #: data/ui/server-panel.ui:267
msgid "Seconds played" msgid "Seconds played"
msgstr "" msgstr ""
#: data/ui/server-panel.ui:371 #: data/ui/server-panel.ui:287
msgid "Seconds running" msgid "Seconds running"
msgstr "" msgstr ""
#: data/ui/server-panel.ui:387 #: data/ui/server-panel.ui:303
msgid "Audio Devices" msgid "Audio Devices"
msgstr "" msgstr ""
@ -214,18 +210,18 @@ msgstr ""
msgid "Library Panel" msgid "Library Panel"
msgstr "" msgstr ""
#: src/connectionpanel.py:51
msgid "use"
msgstr ""
#: src/librarypanel.py:291 #: src/librarypanel.py:291
msgid "Loading albums" msgid "Loading albums"
msgstr "" msgstr ""
#: src/librarypanel.py:370 #: src/librarypanel.py:379
msgid "Loading images" msgid "Loading images"
msgstr "" msgstr ""
#: src/serverpanel.py:77
msgid "use"
msgstr ""
#: src/utils.py:50 src/utils.py:67 #: src/utils.py:50 src/utils.py:67
msgid "{} feat. {}" msgid "{} feat. {}"
msgstr "" msgstr ""
@ -234,19 +230,18 @@ msgstr ""
msgid "{}:{} minutes" msgid "{}:{} minutes"
msgstr "" msgstr ""
#. Stack #: src/window.py:114
#: src/window.py:106
msgid "Server" msgid "Server"
msgstr "" msgstr ""
#: src/window.py:107 #: src/window.py:115
msgid "Cover" msgid "Cover"
msgstr "" msgstr ""
#: src/window.py:108 #: src/window.py:116
msgid "Playlist" msgid "Playlist"
msgstr "" msgstr ""
#: src/window.py:109 #: src/window.py:117
msgid "Library" msgid "Library"
msgstr "" msgstr ""

89
src/connectionpanel.py Normal file
View file

@ -0,0 +1,89 @@
#!/usr/bin/env python3
import gi
import locale
gi.require_version('Gtk', '4.0')
gi.require_version('Adw', '1')
from gi.repository import Gtk, GObject, Adw
from mcg.zeroconf import ZeroconfProvider
@Gtk.Template(resource_path='/xyz/suruatoel/mcg/ui/connection-panel.ui')
class ConnectionPanel(Adw.Bin):
__gtype_name__ = 'McgConnectionPanel'
__gsignals__ = {
'connection-changed': (GObject.SIGNAL_RUN_FIRST, None, (str, int, str))
}
# Widgets
toolbar = Gtk.Template.Child()
zeroconf_list = Gtk.Template.Child()
host_row = Gtk.Template.Child()
port_spinner = Gtk.Template.Child()
password_row = Gtk.Template.Child()
def __init__(self, **kwargs):
super().__init__(**kwargs)
# Zeroconf provider
self._zeroconf_provider = ZeroconfProvider()
self._zeroconf_provider.connect_signal(
ZeroconfProvider.SIGNAL_SERVICE_NEW, self.on_new_service)
def on_new_service(self, service):
name, host, port = service
row_button = Gtk.Button()
row_button.set_label(locale.gettext("use"))
row_button.connect("clicked", self.on_service_selected, host, port)
row = Adw.ActionRow()
row.set_title(name)
row.set_subtitle("{} ({})".format(host, port))
row.add_suffix(row_button)
self.zeroconf_list.insert(row, -1)
def on_service_selected(self, widget, host, port):
self.set_host(host)
self.set_port(port)
@Gtk.Template.Callback()
def on_host_entry_apply(self, widget):
self._call_back()
@Gtk.Template.Callback()
def on_port_spinner_value_changed(self, widget):
self._call_back()
def set_host(self, host):
self.host_row.set_text(host)
def get_host(self):
return self.host_row.get_text()
def set_port(self, port):
self.port_spinner.set_value(port)
def get_port(self):
return self.port_spinner.get_value_as_int()
def set_password(self, password):
if password is None:
password = ""
self.password_row.set_text(password)
def get_password(self):
if self.password_row.get_text() == "":
return None
else:
return self.password_entry.get_text()
def _call_back(self):
self.emit(
'connection-changed',
self.get_host(),
self.get_port(),
self.get_password(),
)

View file

@ -236,6 +236,7 @@ class CoverPanel(Gtk.Overlay):
pixbuf = self._cover_pixbuf pixbuf = self._cover_pixbuf
# Check pixelbuffer # Check pixelbuffer
if pixbuf is None: if pixbuf is None:
self.cover_default.set_pixel_size(min(size_width, size_height)/2)
return return
# Skalierungswert für Breite und Höhe ermitteln # Skalierungswert für Breite und Höhe ermitteln
@ -251,4 +252,5 @@ class CoverPanel(Gtk.Overlay):
return return
self.cover_image.set_from_pixbuf( self.cover_image.set_from_pixbuf(
pixbuf.scale_simple(width, height, GdkPixbuf.InterpType.HYPER)) pixbuf.scale_simple(width, height, GdkPixbuf.InterpType.HYPER))
self.cover_image.set_pixel_size(min(width, height))
self.cover_image.show() self.cover_image.show()

View file

@ -293,15 +293,13 @@ class LibraryPanel(Adw.Bin):
def set_albumart(self, album, data): def set_albumart(self, album, data):
if album in self._selected_albums: if album in self._selected_albums:
self._standalone_pixbuf = None
if data: if data:
# Load image and draw it # Load image and draw it
try: try:
self._standalone_pixbuf = Utils.load_pixbuf(data) self._standalone_pixbuf = Utils.load_pixbuf(data)
except Exception: except Exception:
self._logger.exception("Failed to set albumart") self._logger.exception("Failed to set albumart")
self._standalone_pixbuf = self._get_default_image()
else:
self._standalone_pixbuf = self._get_default_image()
# Show image # Show image
GObject.idle_add(self._show_image) GObject.idle_add(self._show_image)
@ -335,23 +333,24 @@ class LibraryPanel(Adw.Bin):
self._grid_pixbufs.clear() self._grid_pixbufs.clear()
for album_id in albums.keys(): for album_id in albums.keys():
album = albums[album_id] album = albums[album_id]
grid_item = GridItem(album)
pixbuf = None pixbuf = None
try: try:
pixbuf = Utils.load_thumbnail(cache, self._client, album, size) pixbuf = Utils.load_thumbnail(cache, self._client, album, size)
except client.CommandException: except client.CommandException:
# Exception is handled by client # Exception is handled by client
pass pass
except Exception as e: except Exception:
self._logger.exception("Failed to load albumart", e) self._logger.exception("Failed to load albumart")
if pixbuf is None: if pixbuf is None:
pixbuf = self._icon_theme.lookup_icon( icon = self._get_default_icon(self._item_size, self._item_size)
Utils.STOCK_ICON_DEFAULT, None, self._item_size, grid_item.set_icon(icon)
self._item_size, Gtk.TextDirection.LTR, else:
Gtk.IconLookupFlags.FORCE_SYMBOLIC)
if pixbuf is not None:
self._grid_pixbufs[album.get_id()] = pixbuf self._grid_pixbufs[album.get_id()] = pixbuf
GObject.idle_add(self._library_grid_model.append, grid_item.set_cover(pixbuf)
GridItem(album, pixbuf))
GObject.idle_add(self._library_grid_model.append, grid_item)
i += 1 i += 1
GObject.idle_add(self.progress_bar.set_fraction, i / n) GObject.idle_add(self.progress_bar.set_fraction, i / n)
@ -440,27 +439,26 @@ class LibraryPanel(Adw.Bin):
pixbuf = self._standalone_pixbuf pixbuf = self._standalone_pixbuf
# Check pixelbuffer # Check pixelbuffer
if pixbuf is None: if pixbuf is None:
icon = self._get_default_icon(size_width, size_height)
self.standalone_image.set_from_paintable(icon)
self.standalone_image.set_pixel_size(min(size_width, size_height)/2)
return return
# Skalierungswert für Breite und Höhe ermitteln (width, height) = Utils.calculate_size(pixbuf.get_width(),
ratio_w = float(size_width) / float(pixbuf.get_width()) pixbuf.get_height(), size_width,
ratio_h = float(size_height) / float(pixbuf.get_height()) size_height)
# Kleineren beider Skalierungswerte nehmen, nicht Hochskalieren
ratio = min(ratio_w, ratio_h)
ratio = min(ratio, 1)
# Neue Breite und Höhe berechnen
width = int(math.floor(pixbuf.get_width() * ratio))
height = int(math.floor(pixbuf.get_height() * ratio))
if width <= 0 or height <= 0: if width <= 0 or height <= 0:
return return
# Pixelpuffer auf Oberfläche zeichnen # Pixelpuffer auf Oberfläche zeichnen
self.standalone_image.set_from_pixbuf( self.standalone_image.set_from_pixbuf(
pixbuf.scale_simple(width, height, GdkPixbuf.InterpType.HYPER)) pixbuf.scale_simple(width, height, GdkPixbuf.InterpType.HYPER))
self.standalone_image.set_pixel_size(min(width, height))
self.standalone_image.show() self.standalone_image.show()
def _get_default_image(self): def _get_default_icon(self, width, height):
return self._icon_theme.lookup_icon(Utils.STOCK_ICON_DEFAULT, None, return self._icon_theme.lookup_icon(Utils.STOCK_ICON_DEFAULT, None,
512, 512, Gtk.TextDirection.LTR, width, height,
Gtk.TextDirection.LTR,
Gtk.IconLookupFlags.FORCE_SYMBOLIC) Gtk.IconLookupFlags.FORCE_SYMBOLIC)
def _get_selected_albums(self): def _get_selected_albums(self):

View file

@ -23,6 +23,7 @@ mcg_sources = [
'albumheaderbar.py', 'albumheaderbar.py',
'application.py', 'application.py',
'client.py', 'client.py',
'connectionpanel.py',
'coverpanel.py', 'coverpanel.py',
'librarypanel.py', 'librarypanel.py',
'playlistpanel.py', 'playlistpanel.py',

View file

@ -171,15 +171,13 @@ class PlaylistPanel(Adw.Bin):
def set_albumart(self, album, data): def set_albumart(self, album, data):
if album in self._selected_albums: if album in self._selected_albums:
self._standalone_pixbuf = None
if data: if data:
# Load image and draw it # Load image and draw it
try: try:
self._standalone_pixbuf = Utils.load_pixbuf(data) self._standalone_pixbuf = Utils.load_pixbuf(data)
except Exception: except Exception:
self._logger.exception("Failed to set albumart") self._logger.exception("Failed to set albumart")
self._cover_pixbuf = self._get_default_image()
else:
self._cover_pixbuf = self._get_default_image()
# Show image # Show image
GObject.idle_add(self._show_image) GObject.idle_add(self._show_image)
@ -197,6 +195,8 @@ class PlaylistPanel(Adw.Bin):
cache = client.MCGCache(host, size) cache = client.MCGCache(host, size)
for album in playlist: for album in playlist:
grid_item = GridItem(album)
pixbuf = None pixbuf = None
# Load albumart thumbnail # Load albumart thumbnail
try: try:
@ -207,12 +207,12 @@ class PlaylistPanel(Adw.Bin):
except Exception: except Exception:
self._logger.exception("Failed to load albumart") self._logger.exception("Failed to load albumart")
if pixbuf is None: if pixbuf is None:
pixbuf = self._icon_theme.lookup_icon( icon = self._get_default_icon(self._item_size, self._item_size)
Utils.STOCK_ICON_DEFAULT, None, self._item_size, grid_item.set_icon(icon)
self._item_size, Gtk.TextDirection.LTR, else:
Gtk.IconLookupFlags.FORCE_SYMBOLIC) grid_item.set_cover(pixbuf)
if pixbuf is not None:
self._playlist_grid_model.append(GridItem(album, pixbuf)) GObject.idle_add(self._playlist_grid_model.append, grid_item)
if self._playlist_stop.is_set(): if self._playlist_stop.is_set():
self._playlist_lock.release() self._playlist_lock.release()
@ -247,27 +247,26 @@ class PlaylistPanel(Adw.Bin):
pixbuf = self._standalone_pixbuf pixbuf = self._standalone_pixbuf
# Check pixelbuffer # Check pixelbuffer
if pixbuf is None: if pixbuf is None:
icon = self._get_default_icon(size_width, size_height)
self.standalone_image.set_from_paintable(icon)
self.standalone_image.set_pixel_size(min(size_width, size_height)/2)
return return
# Skalierungswert für Breite und Höhe ermitteln (width, height) = Utils.calculate_size(pixbuf.get_width(),
ratio_w = float(size_width) / float(pixbuf.get_width()) pixbuf.get_height(), size_width,
ratio_h = float(size_height) / float(pixbuf.get_height()) size_height)
# Kleineren beider Skalierungswerte nehmen, nicht Hochskalieren
ratio = min(ratio_w, ratio_h)
ratio = min(ratio, 1)
# Neue Breite und Höhe berechnen
width = int(math.floor(pixbuf.get_width() * ratio))
height = int(math.floor(pixbuf.get_height() * ratio))
if width <= 0 or height <= 0: if width <= 0 or height <= 0:
return return
# Pixelpuffer auf Oberfläche zeichnen # Pixelpuffer auf Oberfläche zeichnen
self.standalone_image.set_from_pixbuf( self.standalone_image.set_from_pixbuf(
pixbuf.scale_simple(width, height, GdkPixbuf.InterpType.HYPER)) pixbuf.scale_simple(width, height, GdkPixbuf.InterpType.HYPER))
self.standalone_image.set_pixel_size(min(width, height))
self.standalone_image.show() self.standalone_image.show()
def _get_default_image(self): def _get_default_icon(self, width, height):
return self._icon_theme.lookup_icon(Utils.STOCK_ICON_DEFAULT, None, return self._icon_theme.lookup_icon(Utils.STOCK_ICON_DEFAULT, None,
512, 512, Gtk.TextDirection.LTR, width, height,
Gtk.TextDirection.LTR,
Gtk.IconLookupFlags.FORCE_SYMBOLIC) Gtk.IconLookupFlags.FORCE_SYMBOLIC)
def _get_selected_albums(self): def _get_selected_albums(self):

View file

@ -4,19 +4,13 @@ import gi
gi.require_version('Gtk', '4.0') gi.require_version('Gtk', '4.0')
gi.require_version('Adw', '1') gi.require_version('Adw', '1')
import locale
from gi.repository import Gtk, Adw, GObject from gi.repository import Gtk, Adw, GObject
from mcg.zeroconf import ZeroconfProvider
@Gtk.Template(resource_path='/xyz/suruatoel/mcg/ui/server-panel.ui') @Gtk.Template(resource_path='/xyz/suruatoel/mcg/ui/server-panel.ui')
class ServerPanel(Adw.Bin): class ServerPanel(Adw.Bin):
__gtype_name__ = 'McgServerPanel' __gtype_name__ = 'McgServerPanel'
__gsignals__ = { __gsignals__ = {
'connection-changed':
(GObject.SIGNAL_RUN_FIRST, None, (str, int, str)),
'change-output-device': (GObject.SIGNAL_RUN_FIRST, None, ( 'change-output-device': (GObject.SIGNAL_RUN_FIRST, None, (
GObject.TYPE_PYOBJECT, GObject.TYPE_PYOBJECT,
bool, bool,
@ -25,15 +19,6 @@ class ServerPanel(Adw.Bin):
# Widgets # Widgets
toolbar = Gtk.Template.Child() toolbar = Gtk.Template.Child()
connection_status_page = Gtk.Template.Child()
status_page = Gtk.Template.Child()
stats_page = Gtk.Template.Child()
devices_page = Gtk.Template.Child()
# Connection widgets
zeroconf_list = Gtk.Template.Child()
host_row = Gtk.Template.Child()
port_spinner = Gtk.Template.Child()
password_row = Gtk.Template.Child()
# Status widgets # Status widgets
status_file = Gtk.Template.Child() status_file = Gtk.Template.Child()
status_audio = Gtk.Template.Child() status_audio = Gtk.Template.Child()
@ -58,72 +43,12 @@ class ServerPanel(Adw.Bin):
# Widgets # Widgets
self._none_label = self.status_file.get_label() self._none_label = self.status_file.get_label()
# Zeroconf provider
self._zeroconf_provider = ZeroconfProvider()
self._zeroconf_provider.connect_signal(
ZeroconfProvider.SIGNAL_SERVICE_NEW, self.on_new_service)
def set_selected(self, selected): def set_selected(self, selected):
self._is_selected = selected self._is_selected = selected
def get_toolbar(self): def get_toolbar(self):
return self.toolbar return self.toolbar
def on_new_service(self, service):
name, host, port = service
row_button = Gtk.Button()
row_button.set_label(locale.gettext("use"))
row_button.connect("clicked", self.on_service_selected, host, port)
row = Adw.ActionRow()
row.set_title(name)
row.set_subtitle("{} ({})".format(host, port))
row.add_suffix(row_button)
self.zeroconf_list.insert(row, -1)
def on_service_selected(self, widget, host, port):
self.set_host(host)
self.set_port(port)
@Gtk.Template.Callback()
def on_host_entry_apply(self, widget):
self._call_back()
@Gtk.Template.Callback()
def on_port_spinner_value_changed(self, widget):
self._call_back()
def set_connection_sensitive(self, sensitive):
self.connection_status_page.set_sensitive(sensitive)
self.status_page.set_visible(not sensitive)
self.stats_page.set_visible(not sensitive)
self.devices_page.set_visible(not sensitive)
def set_host(self, host):
self.host_row.set_text(host)
def get_host(self):
return self.host_row.get_text()
def set_port(self, port):
self.port_spinner.set_value(port)
def get_port(self):
return self.port_spinner.get_value_as_int()
def set_password(self, password):
if password is None:
password = ""
self.password_row.set_text(password)
def get_password(self):
if self.password_row.get_text() == "":
return None
else:
return self.password_entry.get_text()
def on_output_device_toggled(self, widget, device): def on_output_device_toggled(self, widget, device):
self.emit('change-output-device', device, widget.get_active()) self.emit('change-output-device', device, widget.get_active())
@ -188,12 +113,3 @@ class ServerPanel(Adw.Bin):
if id not in device_ids: if id not in device_ids:
self.output_devices.remove( self.output_devices.remove(
self._output_buttons[id].get_parent()) self._output_buttons[id].get_parent())
def _call_back(self):
self.emit(
'connection-changed',
self.get_host(),
self.get_port(),
self.get_password(),
)

View file

@ -2,7 +2,9 @@
import gi import gi
import hashlib import hashlib
import math
import locale import locale
import logging
import os import os
gi.require_version('Gtk', '4.0') gi.require_version('Gtk', '4.0')
@ -35,9 +37,17 @@ class Utils:
if albumart: if albumart:
pixbuf = Utils.load_pixbuf(albumart) pixbuf = Utils.load_pixbuf(albumart)
if pixbuf is not None: if pixbuf is not None:
pixbuf = pixbuf.scale_simple(size, size, (width, height) = Utils.calculate_size(pixbuf.get_width(),
pixbuf.get_height(),
size, size)
pixbuf = pixbuf.scale_simple(width, height,
GdkPixbuf.InterpType.HYPER) GdkPixbuf.InterpType.HYPER)
try:
pixbuf.savev(cache_url, 'jpeg', [], []) pixbuf.savev(cache_url, 'jpeg', [], [])
except Exception as e:
logger = logging.getLogger(__name__)
logger.warning("Failed to save thumbnail for album\"%s\": "
"%s", album.get_title(), e)
return pixbuf return pixbuf
@staticmethod @staticmethod
@ -72,6 +82,19 @@ class Utils:
m.update(value.encode('utf-8')) m.update(value.encode('utf-8'))
return m.hexdigest() return m.hexdigest()
@staticmethod
def calculate_size(src_width, src_height, dest_width, dest_height):
ratio_w = float(dest_width) / float(src_width)
ratio_h = float(dest_height) / float(src_height)
ratio = min(min(ratio_w, ratio_h), 1)
if ratio == 1:
return (src_width, src_height)
width = int(math.floor(src_width * ratio))
height = int(math.floor(src_height * ratio))
return (width, height)
class SortOrder: class SortOrder:
ARTIST = 0 ARTIST = 0
@ -86,11 +109,9 @@ class GridItem(GObject.GObject):
tooltip = GObject.Property(type=str, default=None) tooltip = GObject.Property(type=str, default=None)
cover = GObject.Property(type=Gdk.Paintable, default=None) cover = GObject.Property(type=Gdk.Paintable, default=None)
def __init__(self, album, cover): def __init__(self, album):
super().__init__() super().__init__()
self._album = album self._album = album
if cover:
self.cover = Gdk.Texture.new_for_pixbuf(cover)
self.tooltip = GObject.markup_escape_text("\n".join([ self.tooltip = GObject.markup_escape_text("\n".join([
album.get_title(), ', '.join(album.get_dates()), album.get_title(), ', '.join(album.get_dates()),
Utils.create_artists_label(album), Utils.create_artists_label(album),
@ -103,6 +124,9 @@ class GridItem(GObject.GObject):
def set_cover(self, cover): def set_cover(self, cover):
self.cover = Gdk.Texture.new_for_pixbuf(cover) self.cover = Gdk.Texture.new_for_pixbuf(cover)
def set_icon(self, icon):
self.cover = icon
class SearchFilter(Gtk.Filter): class SearchFilter(Gtk.Filter):

View file

@ -14,6 +14,7 @@ gi.require_version('Adw', '1')
from gi.repository import Gtk, Adw, Gdk, GObject, GLib, Gio from gi.repository import Gtk, Adw, Gdk, GObject, GLib, Gio
from . import client from . import client
from .shortcutsdialog import ShortcutsDialog from .shortcutsdialog import ShortcutsDialog
from .connectionpanel import ConnectionPanel
from .serverpanel import ServerPanel from .serverpanel import ServerPanel
from .coverpanel import CoverPanel from .coverpanel import CoverPanel
from .playlistpanel import PlaylistPanel from .playlistpanel import PlaylistPanel
@ -51,6 +52,7 @@ class Window(Adw.ApplicationWindow):
# Widgets # Widgets
toolbar_view = Gtk.Template.Child() toolbar_view = Gtk.Template.Child()
content_stack = Gtk.Template.Child()
panel_stack = Gtk.Template.Child() panel_stack = Gtk.Template.Child()
toolbar_stack = Gtk.Template.Child() toolbar_stack = Gtk.Template.Child()
# Headerbar # Headerbar
@ -79,6 +81,8 @@ class Window(Adw.ApplicationWindow):
# Help/Shortcuts dialog # Help/Shortcuts dialog
self.set_help_overlay(ShortcutsDialog()) self.set_help_overlay(ShortcutsDialog())
# Login screen
self._connection_panel = ConnectionPanel()
# Server panel # Server panel
self._server_panel = ServerPanel() self._server_panel = ServerPanel()
self._panels.append(self._server_panel) self._panels.append(self._server_panel)
@ -100,6 +104,7 @@ class Window(Adw.ApplicationWindow):
self.on_panel_close_standalone) self.on_panel_close_standalone)
self._panels.append(self._library_panel) self._panels.append(self._library_panel)
# Stack # Stack
self.content_stack.add_child(self._connection_panel)
self.panel_stack.add_titled_with_icon(self._server_panel, self.panel_stack.add_titled_with_icon(self._server_panel,
'server-panel', 'server-panel',
locale.gettext("Server"), locale.gettext("Server"),
@ -123,12 +128,12 @@ class Window(Adw.ApplicationWindow):
# Properties # Properties
self._set_headerbar_sensitive(False, False) self._set_headerbar_sensitive(False, False)
self._server_panel.set_host( self._connection_panel.set_host(
self._settings.get_string(Window.SETTING_HOST)) self._settings.get_string(Window.SETTING_HOST))
self._server_panel.set_port(self._settings.get_int( self._connection_panel.set_port(
Window.SETTING_PORT)) self._settings.get_int(Window.SETTING_PORT))
if use_keyring: if use_keyring:
self._server_panel.set_password( self._connection_panel.set_password(
keyring.get_password(ZeroconfProvider.KEYRING_SYSTEM, keyring.get_password(ZeroconfProvider.KEYRING_SYSTEM,
ZeroconfProvider.KEYRING_USERNAME)) ZeroconfProvider.KEYRING_USERNAME))
self._playlist_panel.set_item_size( self._playlist_panel.set_item_size(
@ -143,9 +148,8 @@ class Window(Adw.ApplicationWindow):
# Signals # Signals
self.connect("notify::maximized", self.on_maximized) self.connect("notify::maximized", self.on_maximized)
self.connect("notify::fullscreened", self.on_fullscreened) self.connect("notify::fullscreened", self.on_fullscreened)
self._server_panel.connect('connection-changed', self._connection_panel.connect(
self.on_server_panel_connection_changed) 'connection-changed', self.on_connection_panel_connection_changed)
self.panel_stack.connect('notify::visible-child', self.panel_stack.connect('notify::visible-child',
self.on_stack_switched) self.on_stack_switched)
self._server_panel.connect('change-output-device', self._server_panel.connect('change-output-device',
@ -215,7 +219,7 @@ class Window(Adw.ApplicationWindow):
self.set_default_size(self._state.width, self._state.height) self.set_default_size(self._state.width, self._state.height)
if self._state.get_property(WindowState.PROP_MAXIMIZED): if self._state.get_property(WindowState.PROP_MAXIMIZED):
self.maximize() self.maximize()
self.panel_stack.set_visible_child(self._server_panel) self.content_stack.set_visible_child(self._connection_panel)
if self._settings.get_boolean(Window.SETTING_CONNECTED): if self._settings.get_boolean(Window.SETTING_CONNECTED):
self._connect() self._connect()
@ -340,7 +344,8 @@ class Window(Adw.ApplicationWindow):
self.toolbar_view.add_top_bar(self.headerbar) self.toolbar_view.add_top_bar(self.headerbar)
self.toolbar_view.remove(panel.get_headerbar_standalone()) self.toolbar_view.remove(panel.get_headerbar_standalone())
def on_server_panel_connection_changed(self, widget, host, port, password): def on_connection_panel_connection_changed(self, widget, host, port,
password):
self._settings.set_string(Window.SETTING_HOST, host) self._settings.set_string(Window.SETTING_HOST, host)
self._settings.set_int(Window.SETTING_PORT, port) self._settings.set_int(Window.SETTING_PORT, port)
if use_keyring: if use_keyring:
@ -466,7 +471,7 @@ class Window(Adw.ApplicationWindow):
self._server_panel.set_output_devices(devices) self._server_panel.set_output_devices(devices)
def on_mcg_load_playlist(self, playlist): def on_mcg_load_playlist(self, playlist):
self._playlist_panel.set_playlist(self._server_panel.get_host(), self._playlist_panel.set_playlist(self._connection_panel.get_host(),
playlist) playlist)
def on_mcg_init_albums(self): def on_mcg_init_albums(self):
@ -476,7 +481,7 @@ class Window(Adw.ApplicationWindow):
GObject.idle_add(self._library_panel.load_albums) GObject.idle_add(self._library_panel.load_albums)
def on_mcg_load_albums(self, albums): def on_mcg_load_albums(self, albums):
self._library_panel.set_albums(self._server_panel.get_host(), self._library_panel.set_albums(self._connection_panel.get_host(),
albums) albums)
def on_mcg_load_albumart(self, album, data): def on_mcg_load_albumart(self, album, data):
@ -509,21 +514,22 @@ class Window(Adw.ApplicationWindow):
# Private methods # Private methods
def _connect(self): def _connect(self):
self._server_panel.set_connection_sensitive(False) self._connection_panel.set_sensitive(False)
self._set_headerbar_sensitive(False, True) self._set_headerbar_sensitive(False, True)
if self._mcg.is_connected(): if self._mcg.is_connected():
self._mcg.disconnect() self._mcg.disconnect()
self._settings.set_boolean(Window.SETTING_CONNECTED, False) self._settings.set_boolean(Window.SETTING_CONNECTED, False)
else: else:
host = self._server_panel.get_host() host = self._connection_panel.get_host()
port = self._server_panel.get_port() port = self._connection_panel.get_port()
password = self._server_panel.get_password() password = self._connection_panel.get_password()
self._mcg.connect(host, port, password) self._mcg.connect(host, port, password)
self._settings.set_boolean(Window.SETTING_CONNECTED, True) self._settings.set_boolean(Window.SETTING_CONNECTED, True)
def _connect_connected(self): def _connect_connected(self):
self._headerbar_connected() self._headerbar_connected()
self._set_headerbar_sensitive(True, False) self._set_headerbar_sensitive(True, False)
self.content_stack.set_visible_child(self.panel_stack)
self.panel_stack.set_visible_child(self._panels[self._settings.get_int( self.panel_stack.set_visible_child(self._panels[self._settings.get_int(
Window.SETTING_PANEL)]) Window.SETTING_PANEL)])
@ -533,8 +539,8 @@ class Window(Adw.ApplicationWindow):
self._headerbar_disconnected() self._headerbar_disconnected()
self._set_headerbar_sensitive(False, False) self._set_headerbar_sensitive(False, False)
self._save_visible_panel() self._save_visible_panel()
self.panel_stack.set_visible_child(self._server_panel) self.content_stack.set_visible_child(self._connection_panel)
self._server_panel.set_connection_sensitive(True) self._connection_panel.set_sensitive(True)
def _fullscreen(self, fullscreened_new): def _fullscreen(self, fullscreened_new):
if fullscreened_new != self._state.get_property( if fullscreened_new != self._state.get_property(