[gnome-music/wip/jfelder/improve-song-album-sorting: 5/5] Use natural order to sort album, artists and song names
- From: Jean Felder <jfelder src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-music/wip/jfelder/improve-song-album-sorting: 5/5] Use natural order to sort album, artists and song names
- Date: Mon, 6 Jan 2020 11:58:16 +0000 (UTC)
commit 9429d60c38c056f0f2e18baf9b9834c11315b647
Author: Jean Felder <jfelder src gnome org>
Date: Tue Dec 17 20:32:45 2019 +0100
Use natural order to sort album, artists and song names
A natural order is an alphabetical order which takes into account digit
numbers. For example, the list ["Album 10", "Album 3"] should be
sorted ["Album 3", "Album 10"] in an natural order.
This can happen for compilation albums. There can be
naming schemes like: Album, Album 2, Album 3,..., Album 10.
This issue is fixed by replacing the alphabetical sort function of the
main models by a natural sort function. This new sort function splits
the names by the occurences of a digit. The digits are then replaced by
an integer which allows to make a direct list comparison.
Closes: #22
gnomemusic/coremodel.py | 8 +++++---
gnomemusic/songliststore.py | 6 +++---
gnomemusic/utils.py | 17 ++++++++++++++---
3 files changed, 22 insertions(+), 9 deletions(-)
---
diff --git a/gnomemusic/coremodel.py b/gnomemusic/coremodel.py
index c795c2bc..a8579159 100644
--- a/gnomemusic/coremodel.py
+++ b/gnomemusic/coremodel.py
@@ -160,16 +160,18 @@ class CoreModel(GObject.GObject):
return coresong.props.selected
def _albums_sort(self, album_a, album_b):
- return utils.sort_names(album_a.props.title, album_b.props.title)
+ return utils.natural_sort_names(
+ album_a.props.title, album_b.props.title)
def _artist_sort(self, artist_a, artist_b):
- return utils.sort_names(artist_a.props.artist, artist_b.props.artist)
+ return utils.natural_sort_names(
+ artist_a.props.artist, artist_b.props.artist)
def _playlists_sort(self, playlist_a, playlist_b):
if playlist_a.props.is_smart:
if not playlist_b.props.is_smart:
return -1
- return utils.sort_names(
+ return utils.natural_sort_names(
playlist_a.props.title, playlist_b.props.title)
if playlist_b.props.is_smart:
diff --git a/gnomemusic/songliststore.py b/gnomemusic/songliststore.py
index 4a8e13b0..baf80810 100644
--- a/gnomemusic/songliststore.py
+++ b/gnomemusic/songliststore.py
@@ -71,16 +71,16 @@ class SongListStore(Gtk.ListStore):
song_cmp = (utils.normalize_caseless(title_a)
== utils.normalize_caseless(title_b))
if not song_cmp:
- return utils.sort_names(title_a, title_b)
+ return utils.natural_sort_names(title_a, title_b)
artist_a = song_a.props.artist
artist_b = song_b.props.artist
artist_cmp = (utils.normalize_caseless(artist_a)
== utils.normalize_caseless(artist_b))
if not artist_cmp:
- return utils.sort_names(artist_a, artist_b)
+ return utils.natural_sort_names(artist_a, artist_b)
- return utils.sort_names(song_a.props.album, song_b.props.album)
+ return utils.natural_sort_names(song_a.props.album, song_b.props.album)
def _on_items_changed(self, model, position, removed, added):
if removed > 0:
diff --git a/gnomemusic/utils.py b/gnomemusic/utils.py
index 405a41e4..8090cf27 100644
--- a/gnomemusic/utils.py
+++ b/gnomemusic/utils.py
@@ -23,6 +23,7 @@
# delete this exception statement from your version.
from enum import Enum, IntEnum
+import re
import unicodedata
from gettext import gettext as _
@@ -147,12 +148,22 @@ def normalize_caseless(text):
return unicodedata.normalize("NFKD", text.casefold())
-def sort_names(name_a, name_b):
- """Caseless comparison of two strings.
+def natural_sort_names(name_a, name_b):
+ """Natural order comparison of two strings.
+
+ A natural order is an alphabetical order which takes into account
+ digit numbers. For example, it returns ["Album 3", "Album 10"]
+ instead of ["Album 10", "Album 3"] for an alphabetical order.
+ The names are also normalized to properly take into account names
+ which contain accents.
:param str name_a: first string to compare
:param str name_b: second string to compare
:returns: False if name_a should be before name_b. True otherwise.
:rtype: boolean
"""
- return normalize_caseless(name_b) < normalize_caseless(name_a)
+ def _extract_numbers(text):
+ return [int(tmp) if tmp.isdigit() else tmp
+ for tmp in re.split(r"(\d+)", normalize_caseless(text))]
+
+ return _extract_numbers(name_b) < _extract_numbers(name_a)
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]