Compare commits
9 commits
1b4885bd20
...
685349da18
| Author | SHA1 | Date | |
|---|---|---|---|
| 685349da18 | |||
| 6ba8bc550f | |||
| 21bd0f5832 | |||
| bfb8eac62d | |||
| 44fb332c62 | |||
| e976e05efe | |||
| a1e87e8994 | |||
| 8607bf15a9 | |||
| 5618c9016c |
13 changed files with 142 additions and 75 deletions
|
|
@ -12,6 +12,7 @@ License: [GPL](http://www.gnu.org/licenses/gpl.html) v3
|
||||||
Dependencies:
|
Dependencies:
|
||||||
|
|
||||||
* [Python](http://www.python.org) 3
|
* [Python](http://www.python.org) 3
|
||||||
|
* [python-dateutil](https://pypi.org/project/python-dateutil/)
|
||||||
* [GTK](http://www.gtk.org) 3 (>= 3.22) ([python-gobject](https://live.gnome.org/PyGObject))
|
* [GTK](http://www.gtk.org) 3 (>= 3.22) ([python-gobject](https://live.gnome.org/PyGObject))
|
||||||
* [Avahi](http://www.avahi.org) (optional)
|
* [Avahi](http://www.avahi.org) (optional)
|
||||||
* [python-keyring](http://pypi.python.org/pypi/keyring) (optional)
|
* [python-keyring](http://pypi.python.org/pypi/keyring) (optional)
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,14 @@
|
||||||
<object class="GtkCheckButton" id="sort_year">
|
<object class="GtkCheckButton" id="sort_year">
|
||||||
<property name="label" translatable="yes">sort by year</property>
|
<property name="label" translatable="yes">sort by year</property>
|
||||||
<property name="receives-default">False</property>
|
<property name="receives-default">False</property>
|
||||||
<property name="active">True</property>
|
<signal name="toggled" handler="on_sort_toggled" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkCheckButton" id="sort_modified">
|
||||||
|
<property name="label" translatable="yes">sort by modification</property>
|
||||||
|
<property name="receives-default">False</property>
|
||||||
|
<property name="group">sort_year</property>
|
||||||
<signal name="toggled" handler="on_sort_toggled" swapped="no"/>
|
<signal name="toggled" handler="on_sort_toggled" swapped="no"/>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
<value nick="artist" value="0" />
|
<value nick="artist" value="0" />
|
||||||
<value nick="title" value="1" />
|
<value nick="title" value="1" />
|
||||||
<value nick="year" value="2" />
|
<value nick="year" value="2" />
|
||||||
|
<value nick="modified" value="3" />
|
||||||
</enum>
|
</enum>
|
||||||
<schema path="/xyz/suruatoel/mcg/" id="xyz.suruatoel.mcg" gettext-domain="mcg">
|
<schema path="/xyz/suruatoel/mcg/" id="xyz.suruatoel.mcg" gettext-domain="mcg">
|
||||||
<key type="s" name="host">
|
<key type="s" name="host">
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
project('mcg',
|
project('mcg',
|
||||||
version: '3.1',
|
version: '3.2.1',
|
||||||
meson_version: '>= 0.59.0',
|
meson_version: '>= 0.59.0',
|
||||||
default_options: [
|
default_options: [
|
||||||
'warning_level=2',
|
'warning_level=2',
|
||||||
|
|
|
||||||
BIN
po/de.mo
BIN
po/de.mo
Binary file not shown.
22
po/de.po
22
po/de.po
|
|
@ -2,17 +2,17 @@ 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: 2022-09-05 15:08+0200\n"
|
"POT-Creation-Date: 2023-01-08 19:06+0100\n"
|
||||||
"PO-Revision-Date: 2020-10-24 14:41+0200\n"
|
"PO-Revision-Date: 2023-01-08 19:07+0100\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"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Generator: Poedit 2.4.1\n"
|
|
||||||
"X-Poedit-Basepath: ../../..\n"
|
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
"X-Generator: Poedit 3.2.2\n"
|
||||||
|
"X-Poedit-Basepath: ../../..\n"
|
||||||
"X-Poedit-SourceCharset: UTF-8\n"
|
"X-Poedit-SourceCharset: UTF-8\n"
|
||||||
|
|
||||||
#: data/xyz.suruatoel.mcg.gschema.xml:11
|
#: data/xyz.suruatoel.mcg.gschema.xml:11
|
||||||
|
|
@ -216,15 +216,19 @@ msgstr "nach Titel"
|
||||||
msgid "sort by year"
|
msgid "sort by year"
|
||||||
msgstr "nach Jahr"
|
msgstr "nach Jahr"
|
||||||
|
|
||||||
#: data/ui/library-toolbar.ui:169 data/ui/shortcuts-dialog.ui:115
|
#: data/ui/library-toolbar.ui:134
|
||||||
|
msgid "sort by modification"
|
||||||
|
msgstr "nach Änderungsdatum"
|
||||||
|
|
||||||
|
#: data/ui/library-toolbar.ui:185 data/ui/shortcuts-dialog.ui:115
|
||||||
msgid "Search the library"
|
msgid "Search the library"
|
||||||
msgstr "Die Bibliothek durchsuchen"
|
msgstr "Die Bibliothek durchsuchen"
|
||||||
|
|
||||||
#: data/ui/library-toolbar.ui:192 data/ui/playlist-toolbar.ui:15
|
#: data/ui/library-toolbar.ui:208 data/ui/playlist-toolbar.ui:15
|
||||||
msgid "Select multiple albums"
|
msgid "Select multiple albums"
|
||||||
msgstr "Mehrere Alben auswählen"
|
msgstr "Mehrere Alben auswählen"
|
||||||
|
|
||||||
#: data/ui/library-toolbar.ui:214
|
#: data/ui/library-toolbar.ui:230
|
||||||
msgid "Settings and actions"
|
msgid "Settings and actions"
|
||||||
msgstr "Einstellungen und Aktionen"
|
msgstr "Einstellungen und Aktionen"
|
||||||
|
|
||||||
|
|
@ -353,11 +357,11 @@ msgstr "Zu MPD verbinden"
|
||||||
msgid "Adjust the volume"
|
msgid "Adjust the volume"
|
||||||
msgstr "Die Lautstärke anpassen"
|
msgstr "Die Lautstärke anpassen"
|
||||||
|
|
||||||
#: src/librarypanel.py:419
|
#: src/librarypanel.py:421
|
||||||
msgid "Loading albums"
|
msgid "Loading albums"
|
||||||
msgstr "Alben werden geladen"
|
msgstr "Alben werden geladen"
|
||||||
|
|
||||||
#: src/librarypanel.py:519
|
#: src/librarypanel.py:521
|
||||||
msgid "Loading images"
|
msgid "Loading images"
|
||||||
msgstr "Bilder werden geladen"
|
msgstr "Bilder werden geladen"
|
||||||
|
|
||||||
|
|
|
||||||
BIN
po/en.mo
BIN
po/en.mo
Binary file not shown.
22
po/en.po
22
po/en.po
|
|
@ -2,17 +2,17 @@ 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: 2022-09-05 15:08+0200\n"
|
"POT-Creation-Date: 2023-01-08 19:06+0100\n"
|
||||||
"PO-Revision-Date: 2020-10-24 14:40+0200\n"
|
"PO-Revision-Date: 2023-01-08 19:07+0100\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"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Generator: Poedit 2.4.1\n"
|
|
||||||
"X-Poedit-Basepath: ../../..\n"
|
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
"X-Generator: Poedit 3.2.2\n"
|
||||||
|
"X-Poedit-Basepath: ../../..\n"
|
||||||
"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"
|
||||||
|
|
||||||
|
|
@ -217,15 +217,19 @@ msgstr "by Title"
|
||||||
msgid "sort by year"
|
msgid "sort by year"
|
||||||
msgstr "by Year"
|
msgstr "by Year"
|
||||||
|
|
||||||
#: data/ui/library-toolbar.ui:169 data/ui/shortcuts-dialog.ui:115
|
#: data/ui/library-toolbar.ui:134
|
||||||
|
msgid "sort by modification"
|
||||||
|
msgstr "by Modification Date"
|
||||||
|
|
||||||
|
#: data/ui/library-toolbar.ui:185 data/ui/shortcuts-dialog.ui:115
|
||||||
msgid "Search the library"
|
msgid "Search the library"
|
||||||
msgstr "Search the library"
|
msgstr "Search the library"
|
||||||
|
|
||||||
#: data/ui/library-toolbar.ui:192 data/ui/playlist-toolbar.ui:15
|
#: data/ui/library-toolbar.ui:208 data/ui/playlist-toolbar.ui:15
|
||||||
msgid "Select multiple albums"
|
msgid "Select multiple albums"
|
||||||
msgstr "Select multiple albums"
|
msgstr "Select multiple albums"
|
||||||
|
|
||||||
#: data/ui/library-toolbar.ui:214
|
#: data/ui/library-toolbar.ui:230
|
||||||
msgid "Settings and actions"
|
msgid "Settings and actions"
|
||||||
msgstr "Settings and actions"
|
msgstr "Settings and actions"
|
||||||
|
|
||||||
|
|
@ -354,11 +358,11 @@ msgstr "Connect to MPD"
|
||||||
msgid "Adjust the volume"
|
msgid "Adjust the volume"
|
||||||
msgstr "Adjust the volume"
|
msgstr "Adjust the volume"
|
||||||
|
|
||||||
#: src/librarypanel.py:419
|
#: src/librarypanel.py:421
|
||||||
msgid "Loading albums"
|
msgid "Loading albums"
|
||||||
msgstr "Loading albums"
|
msgstr "Loading albums"
|
||||||
|
|
||||||
#: src/librarypanel.py:519
|
#: src/librarypanel.py:521
|
||||||
msgid "Loading images"
|
msgid "Loading images"
|
||||||
msgstr "Loading images"
|
msgstr "Loading images"
|
||||||
|
|
||||||
|
|
|
||||||
16
po/mcg.pot
16
po/mcg.pot
|
|
@ -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: 2022-09-05 15:08+0200\n"
|
"POT-Creation-Date: 2023-01-08 19:06+0100\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"
|
||||||
|
|
@ -215,15 +215,19 @@ msgstr ""
|
||||||
msgid "sort by year"
|
msgid "sort by year"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/ui/library-toolbar.ui:169 data/ui/shortcuts-dialog.ui:115
|
#: data/ui/library-toolbar.ui:134
|
||||||
|
msgid "sort by modification"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: data/ui/library-toolbar.ui:185 data/ui/shortcuts-dialog.ui:115
|
||||||
msgid "Search the library"
|
msgid "Search the library"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/ui/library-toolbar.ui:192 data/ui/playlist-toolbar.ui:15
|
#: data/ui/library-toolbar.ui:208 data/ui/playlist-toolbar.ui:15
|
||||||
msgid "Select multiple albums"
|
msgid "Select multiple albums"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/ui/library-toolbar.ui:214
|
#: data/ui/library-toolbar.ui:230
|
||||||
msgid "Settings and actions"
|
msgid "Settings and actions"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
@ -352,11 +356,11 @@ msgstr ""
|
||||||
msgid "Adjust the volume"
|
msgid "Adjust the volume"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/librarypanel.py:419
|
#: src/librarypanel.py:421
|
||||||
msgid "Loading albums"
|
msgid "Loading albums"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/librarypanel.py:519
|
#: src/librarypanel.py:521
|
||||||
msgid "Loading images"
|
msgid "Loading images"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ class Application(Gtk.Application):
|
||||||
self._info_dialog = Adw.AboutDialog()
|
self._info_dialog = Adw.AboutDialog()
|
||||||
self._info_dialog.set_application_icon("xyz.suruatoel.mcg")
|
self._info_dialog.set_application_icon("xyz.suruatoel.mcg")
|
||||||
self._info_dialog.set_application_name("CoverGrid")
|
self._info_dialog.set_application_name("CoverGrid")
|
||||||
self._info_dialog.set_version("3.1")
|
self._info_dialog.set_version("3.2.1")
|
||||||
self._info_dialog.set_comments("CoverGrid is a client for the Music Player Daemon, focusing on albums instead of single tracks.")
|
self._info_dialog.set_comments("CoverGrid is a client for the Music Player Daemon, focusing on albums instead of single tracks.")
|
||||||
self._info_dialog.set_website("https://www.suruatoel.xyz/codes/mcg")
|
self._info_dialog.set_website("https://www.suruatoel.xyz/codes/mcg")
|
||||||
self._info_dialog.set_license_type(Gtk.License.GPL_3_0)
|
self._info_dialog.set_license_type(Gtk.License.GPL_3_0)
|
||||||
|
|
|
||||||
135
src/client.py
135
src/client.py
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
import concurrent.futures
|
import concurrent.futures
|
||||||
import configparser
|
import configparser
|
||||||
|
import dateutil.parser
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import queue
|
import queue
|
||||||
|
|
@ -649,57 +650,25 @@ class Client(Base):
|
||||||
|
|
||||||
|
|
||||||
def _get_albumart(self, album):
|
def _get_albumart(self, album):
|
||||||
data = None
|
|
||||||
if album in self._albums:
|
if album in self._albums:
|
||||||
album = self._albums[album]
|
album = self._albums[album]
|
||||||
if not album.get_tracks():
|
|
||||||
return None
|
|
||||||
self._logger.debug("get albumart for album \"%s\"", album.get_title())
|
self._logger.debug("get albumart for album \"%s\"", album.get_title())
|
||||||
size = 1
|
|
||||||
offset = 0
|
|
||||||
index = 0
|
|
||||||
|
|
||||||
# Read data until size is reached
|
# Use "albumart" command
|
||||||
try:
|
if album.get_tracks():
|
||||||
while offset < size:
|
try:
|
||||||
self._write('albumart', args=[album.get_tracks()[0].get_file(), offset])
|
return (album, self._read_binary('albumart', album.get_tracks()[0].get_file(), False))
|
||||||
|
except CommandException as e:
|
||||||
|
# The "albumart" command throws an exception if not found
|
||||||
|
if e.get_error_number() != Client.PROTOCOL_ERROR_NOEXISTS:
|
||||||
|
raise e
|
||||||
|
# If no albumart can be found, use "readpicture" command
|
||||||
|
for track in album.get_tracks():
|
||||||
|
data = self._read_binary('readpicture', track.get_file(), True)
|
||||||
|
if data:
|
||||||
|
return (album, data)
|
||||||
|
|
||||||
# Read first line which tells us whether there is an albumart
|
return (album, None)
|
||||||
line = self._read_line()
|
|
||||||
if line.startswith(Client.PROTOCOL_ERROR):
|
|
||||||
error = line[len(Client.PROTOCOL_ERROR):].strip()
|
|
||||||
self._logger.debug("command failed: %r", error)
|
|
||||||
raise CommandException(error)
|
|
||||||
# First line is the file size
|
|
||||||
size = int(self._parse_dict([line])['size'])
|
|
||||||
self._logger.debug("size: %d", size)
|
|
||||||
# Second line is the count of bytes read
|
|
||||||
binary = int(self._parse_dict([self._read_line()])['binary'])
|
|
||||||
self._logger.debug("binary: %d", binary)
|
|
||||||
|
|
||||||
# Create new data array on the first iteration
|
|
||||||
if not data:
|
|
||||||
data = bytearray(size)
|
|
||||||
# Create a view for the current chunk of data
|
|
||||||
data_view = memoryview(data)[offset:offset+binary]
|
|
||||||
# Read actual bytes
|
|
||||||
self._read_bytes(data_view, binary)
|
|
||||||
offset += binary
|
|
||||||
# Read line break to complete previous repsonse
|
|
||||||
self._read_line()
|
|
||||||
# Read command completion
|
|
||||||
end = self._read_line()
|
|
||||||
if not end.startswith(Client.PROTOCOL_COMPLETION):
|
|
||||||
self._logger.debug("albumart not completed")
|
|
||||||
data = None
|
|
||||||
break
|
|
||||||
except CommandException as e:
|
|
||||||
# If no albumart can be found, do not throw an exception
|
|
||||||
if e.get_error_number() == Client.PROTOCOL_ERROR_NOEXISTS:
|
|
||||||
data = None
|
|
||||||
else:
|
|
||||||
raise e
|
|
||||||
return (album, data)
|
|
||||||
|
|
||||||
|
|
||||||
def _get_custom(self, name):
|
def _get_custom(self, name):
|
||||||
|
|
@ -843,6 +812,55 @@ class Client(Base):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _read_binary(self, command, filename, has_mimetype):
|
||||||
|
data = None
|
||||||
|
size = 1
|
||||||
|
offset = 0
|
||||||
|
index = 0
|
||||||
|
|
||||||
|
# Read data until size is reached
|
||||||
|
while offset < size:
|
||||||
|
self._write(command, args=[filename, offset])
|
||||||
|
|
||||||
|
# Read first line
|
||||||
|
line = self._read_line()
|
||||||
|
# Check first line for error
|
||||||
|
if line.startswith(Client.PROTOCOL_ERROR):
|
||||||
|
error = line[len(Client.PROTOCOL_ERROR):].strip()
|
||||||
|
self._logger.debug("command failed: %r", error)
|
||||||
|
raise CommandException(error)
|
||||||
|
# Check first line for completion
|
||||||
|
if line.startswith(Client.PROTOCOL_COMPLETION):
|
||||||
|
break
|
||||||
|
# First line is the file size
|
||||||
|
size = int(self._parse_dict([line])['size'])
|
||||||
|
self._logger.debug("size: %d", size)
|
||||||
|
# For some commands the second line is the mimetype
|
||||||
|
if has_mimetype:
|
||||||
|
mimetype = self._parse_dict([self._read_line()])['type']
|
||||||
|
# Next line is the count of bytes read
|
||||||
|
binary = int(self._parse_dict([self._read_line()])['binary'])
|
||||||
|
self._logger.debug("binary: %d", binary)
|
||||||
|
|
||||||
|
# Create new data array on the first iteration
|
||||||
|
if not data:
|
||||||
|
data = bytearray(size)
|
||||||
|
# Create a view for the current chunk of data
|
||||||
|
data_view = memoryview(data)[offset:offset+binary]
|
||||||
|
# Read actual bytes
|
||||||
|
self._read_bytes(data_view, binary)
|
||||||
|
offset += binary
|
||||||
|
# Read line break to complete previous repsonse
|
||||||
|
self._read_line()
|
||||||
|
# Read command completion
|
||||||
|
end = self._read_line()
|
||||||
|
if not end.startswith(Client.PROTOCOL_COMPLETION):
|
||||||
|
self._logger.debug("albumart not completed")
|
||||||
|
data = None
|
||||||
|
break
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
def _read_bytes(self, buf, nbytes):
|
def _read_bytes(self, buf, nbytes):
|
||||||
self._logger.debug("reading bytes")
|
self._logger.debug("reading bytes")
|
||||||
# Use already buffered data
|
# Use already buffered data
|
||||||
|
|
@ -937,6 +955,8 @@ class Client(Base):
|
||||||
track.set_date(song['date'])
|
track.set_date(song['date'])
|
||||||
if 'albumartist' in song:
|
if 'albumartist' in song:
|
||||||
track.set_albumartists(song['albumartist'])
|
track.set_albumartists(song['albumartist'])
|
||||||
|
if 'last-modified' in song:
|
||||||
|
track.set_last_modified(song['last-modified'])
|
||||||
return track
|
return track
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -998,6 +1018,7 @@ class MCGAlbum:
|
||||||
self._host = host
|
self._host = host
|
||||||
self._tracks = []
|
self._tracks = []
|
||||||
self._length = 0
|
self._length = 0
|
||||||
|
self._last_modified = None
|
||||||
self._id = Utils.generate_id(title)
|
self._id = Utils.generate_id(title)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1057,6 +1078,9 @@ class MCGAlbum:
|
||||||
path = os.path.dirname(track.get_file())
|
path = os.path.dirname(track.get_file())
|
||||||
if path not in self._pathes:
|
if path not in self._pathes:
|
||||||
self._pathes.append(path)
|
self._pathes.append(path)
|
||||||
|
if track.get_last_modified():
|
||||||
|
if not self._last_modified or track.get_last_modified() > self._last_modified:
|
||||||
|
self._last_modified = track.get_last_modified()
|
||||||
|
|
||||||
|
|
||||||
def get_tracks(self):
|
def get_tracks(self):
|
||||||
|
|
@ -1067,6 +1091,10 @@ class MCGAlbum:
|
||||||
return self._length
|
return self._length
|
||||||
|
|
||||||
|
|
||||||
|
def get_last_modified(self):
|
||||||
|
return self._last_modified
|
||||||
|
|
||||||
|
|
||||||
def filter(self, filter_string):
|
def filter(self, filter_string):
|
||||||
if len(filter_string) == 0:
|
if len(filter_string) == 0:
|
||||||
return True
|
return True
|
||||||
|
|
@ -1102,6 +1130,8 @@ class MCGAlbum:
|
||||||
value_function = "get_title"
|
value_function = "get_title"
|
||||||
elif criterion == SortOrder.YEAR:
|
elif criterion == SortOrder.YEAR:
|
||||||
value_function = "get_date"
|
value_function = "get_date"
|
||||||
|
elif criterion == SortOrder.MODIFIED:
|
||||||
|
value_function = "get_last_modified"
|
||||||
|
|
||||||
reverseMultiplier = -1 if reverse else 1
|
reverseMultiplier = -1 if reverse else 1
|
||||||
|
|
||||||
|
|
@ -1139,6 +1169,7 @@ class MCGTrack:
|
||||||
self._track = None
|
self._track = None
|
||||||
self._length = 0
|
self._length = 0
|
||||||
self._date = None
|
self._date = None
|
||||||
|
self._last_modified = None
|
||||||
|
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
|
|
@ -1210,6 +1241,18 @@ class MCGTrack:
|
||||||
return self._file
|
return self._file
|
||||||
|
|
||||||
|
|
||||||
|
def set_last_modified(self, date_string):
|
||||||
|
if date_string:
|
||||||
|
try:
|
||||||
|
self._last_modified = dateutil.parser.isoparse(date_string)
|
||||||
|
except ValueError as e:
|
||||||
|
self._logger.debug("Invalid date format: %s", date_string)
|
||||||
|
|
||||||
|
|
||||||
|
def get_last_modified(self):
|
||||||
|
return self._last_modified
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class MCGPlaylistTrack(MCGTrack):
|
class MCGPlaylistTrack(MCGTrack):
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,7 @@ class LibraryPanel(Adw.Bin):
|
||||||
sort_artist = Gtk.Template.Child()
|
sort_artist = Gtk.Template.Child()
|
||||||
sort_title = Gtk.Template.Child()
|
sort_title = Gtk.Template.Child()
|
||||||
sort_year = Gtk.Template.Child()
|
sort_year = Gtk.Template.Child()
|
||||||
|
sort_modified = Gtk.Template.Child()
|
||||||
grid_scale = Gtk.Template.Child()
|
grid_scale = Gtk.Template.Child()
|
||||||
# Filter/search bar
|
# Filter/search bar
|
||||||
filter_bar = Gtk.Template.Child()
|
filter_bar = Gtk.Template.Child()
|
||||||
|
|
@ -111,7 +112,8 @@ class LibraryPanel(Adw.Bin):
|
||||||
self._toolbar_sort_buttons = {
|
self._toolbar_sort_buttons = {
|
||||||
SortOrder.ARTIST: self.sort_artist,
|
SortOrder.ARTIST: self.sort_artist,
|
||||||
SortOrder.TITLE: self.sort_title,
|
SortOrder.TITLE: self.sort_title,
|
||||||
SortOrder.YEAR: self.sort_year
|
SortOrder.YEAR: self.sort_year,
|
||||||
|
SortOrder.MODIFIED: self.sort_modified
|
||||||
}
|
}
|
||||||
|
|
||||||
# Button controller for grid scale
|
# Button controller for grid scale
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,7 @@ class SortOrder:
|
||||||
ARTIST = 0
|
ARTIST = 0
|
||||||
TITLE = 1
|
TITLE = 1
|
||||||
YEAR = 2
|
YEAR = 2
|
||||||
|
MODIFIED = 3
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue