From 3dcd30eab6c17e94ff240112e0e264552702054b Mon Sep 17 00:00:00 2001 From: trav Date: Sat, 10 Oct 2026 23:42:40 -0700 Subject: [PATCH] v0.49.1: image pickers see uppercase suffixes like IMG_1897.JPG --- lintunes/__init__.py | 2 +- lintunes/gui/export_dialog.py | 3 +-- lintunes/gui/file_filters.py | 17 +++++++++++++++++ lintunes/gui/info_dialog.py | 3 ++- lintunes/gui/sync_settings_dialog.py | 3 ++- tests/test_round81.py | 10 ++++++++++ 6 files changed, 33 insertions(+), 5 deletions(-) create mode 100644 lintunes/gui/file_filters.py diff --git a/lintunes/__init__.py b/lintunes/__init__.py index 6ae27d0..0097ab3 100644 --- a/lintunes/__init__.py +++ b/lintunes/__init__.py @@ -1,3 +1,3 @@ """LinTunes — iTunes-style music library manager and player for Linux.""" -__version__ = "0.49.0" +__version__ = "0.49.1" diff --git a/lintunes/gui/export_dialog.py b/lintunes/gui/export_dialog.py index dbe8f2c..563d15b 100644 --- a/lintunes/gui/export_dialog.py +++ b/lintunes/gui/export_dialog.py @@ -19,8 +19,7 @@ from PyQt6.QtWidgets import ( from lintunes import theme from lintunes.export import exporter, upload - -_IMAGE_FILTER = "Images (*.png *.jpg *.jpeg *.gif *.webp)" +from lintunes.gui.file_filters import WEB_IMAGE_FILTER as _IMAGE_FILTER class ExportKindDialog(QDialog): diff --git a/lintunes/gui/file_filters.py b/lintunes/gui/file_filters.py new file mode 100644 index 0000000..c00d4b3 --- /dev/null +++ b/lintunes/gui/file_filters.py @@ -0,0 +1,17 @@ +"""File-dialog name filters. + +Qt matches a filter's patterns case-sensitively on Linux, so "*.jpg" hides +a phone photo called IMG_1897.JPG. Every pattern is listed in both cases. +""" + +_IMAGE_SUFFIXES = ("png", "jpg", "jpeg", "gif", "bmp", "webp") + + +def image_filter(suffixes=_IMAGE_SUFFIXES) -> str: + patterns = " ".join(f"*.{s} *.{s.upper()}" for s in suffixes) + return f"Images ({patterns})" + + +IMAGE_FILTER = image_filter() +# What a web page can show: no BMP. +WEB_IMAGE_FILTER = image_filter(("png", "jpg", "jpeg", "gif", "webp")) diff --git a/lintunes/gui/info_dialog.py b/lintunes/gui/info_dialog.py index e511540..dd64940 100644 --- a/lintunes/gui/info_dialog.py +++ b/lintunes/gui/info_dialog.py @@ -13,6 +13,7 @@ from PyQt6.QtGui import QPixmap, QImage, QKeySequence, QShortcut from lintunes import tagging from lintunes.gui.art_ops import embed_artwork from lintunes.player import VOLUME_ADJUSTMENT_MAX +from lintunes.gui.file_filters import IMAGE_FILTER log = logging.getLogger(__name__) @@ -206,7 +207,7 @@ class ArtSquare(QLabel): def choose_file(self): file_name, _filter = QFileDialog.getOpenFileName( self, "Choose Artwork", "", - "Images (*.jpg *.jpeg *.png *.gif *.bmp *.webp)") + IMAGE_FILTER) if file_name and not self._stage_file(Path(file_name)): self.hint_changed.emit("That file isn't a readable image") diff --git a/lintunes/gui/sync_settings_dialog.py b/lintunes/gui/sync_settings_dialog.py index 722a519..6907e14 100644 --- a/lintunes/gui/sync_settings_dialog.py +++ b/lintunes/gui/sync_settings_dialog.py @@ -24,6 +24,7 @@ from lintunes.cassette import syncthing_api as st from lintunes.cassette.state import ( FOLLOW_LIBRARY, FOLLOW_SEPARATE, LAYOUT_FOLDER, LAYOUT_MIXED, OPEN, in_dir, ) +from lintunes.gui.file_filters import IMAGE_FILTER AVATAR_PX = 256 AVATAR_PREVIEW_PX = 72 @@ -244,7 +245,7 @@ class GeneralPage(QWidget): def _choose_avatar(self): path, _ = QFileDialog.getOpenFileName( self, "Choose an Avatar", str(Path.home()), - "Images (*.png *.jpg *.jpeg *.gif *.bmp *.webp)") + IMAGE_FILTER) if not path: return image = load_avatar_image(Path(path)) diff --git a/tests/test_round81.py b/tests/test_round81.py index d1ed72e..f5bdb39 100644 --- a/tests/test_round81.py +++ b/tests/test_round81.py @@ -535,3 +535,13 @@ def test_several_images_land_in_order_with_a_gap(tmp_path): assert page[spots[0]:spots[1]].count("

") == 1 assert (dest / "cover 2.png").read_bytes() == b"B" assert not (dest / "placeholder.png").exists() + + +def test_image_filters_see_uppercase_suffixes(): + """Qt filters are case-sensitive on Linux; a phone's IMG_1897.JPG hid.""" + import fnmatch + from lintunes.gui.file_filters import IMAGE_FILTER, WEB_IMAGE_FILTER + for flt in (IMAGE_FILTER, WEB_IMAGE_FILTER): + patterns = flt[flt.index("(") + 1:-1].split() + for name in ("IMG_1897.JPG", "a.jpeg", "b.PNG", "c.WebP"[:-4] + "WEBP"): + assert any(fnmatch.fnmatchcase(name, p) for p in patterns), name