diff --git a/sublime_music/adapters/configure_server_form.py b/sublime_music/adapters/configure_server_form.py index c82fcaa..47427b1 100644 --- a/sublime_music/adapters/configure_server_form.py +++ b/sublime_music/adapters/configure_server_form.py @@ -8,6 +8,8 @@ from pathlib import Path from time import sleep from typing import Any, Callable, cast, Dict, Iterable, Optional, Tuple, Type, Union +import bleach + from gi.repository import GLib, GObject, Gtk, Pango from . import ConfigurationStore @@ -252,8 +254,6 @@ class ConfigureServerForm(Gtk.Box): def _set_verification_status( self, verifying: bool, is_valid: bool = False, error_text: str = None ): - from sublime_music.ui import util - if verifying: if not self.verifying_in_progress: for c in self.config_verification_box.get_children(): @@ -288,7 +288,7 @@ class ConfigureServerForm(Gtk.Box): set_icon_and_label( "config-ok-symbolic", "Configuration is valid" ) - elif escaped := util.esc(error_text): + elif escaped := bleach.clean(error_text or ""): set_icon_and_label("config-error-symbolic", escaped) self.config_verification_box.show_all() diff --git a/sublime_music/app.py b/sublime_music/app.py index d4e9e10..c0c258a 100644 --- a/sublime_music/app.py +++ b/sublime_music/app.py @@ -1191,14 +1191,12 @@ class SublimeMusicApp(Gtk.Application): if glib_notify_exists: notification_lines = [] if album := song.album: - notification_lines.append( - f"{bleach.clean(album.name)}" - ) + notification_lines.append(f"{album.name}") if artist := song.artist: - notification_lines.append(bleach.clean(artist.name)) + notification_lines.append(artist.name) song_notification = Notify.Notification.new( song.title, - "\n".join(notification_lines), + bleach.clean("\n".join(notification_lines)), ) song_notification.add_action( "clicked", diff --git a/sublime_music/ui/artists.py b/sublime_music/ui/artists.py index cfec212..32a54de 100644 --- a/sublime_music/ui/artists.py +++ b/sublime_music/ui/artists.py @@ -3,6 +3,8 @@ from functools import partial from random import randint from typing import cast, List, Sequence +import bleach + from gi.repository import Gio, GLib, GObject, Gtk, Pango from ..adapters import ( @@ -93,10 +95,9 @@ class ArtistList(Gtk.Box): list_scroll_window = Gtk.ScrolledWindow(min_content_width=250) def create_artist_row(model: _ArtistModel) -> Gtk.ListBoxRow: - label_text = [f"{util.esc(model.name)}"] + label_text = [f"{model.name}"] - album_count = model.album_count - if album_count: + if album_count := model.album_count: label_text.append( "{} {}".format(album_count, util.pluralize("album", album_count)) ) @@ -107,7 +108,7 @@ class ArtistList(Gtk.Box): ) row.add( Gtk.Label( - label="\n".join(label_text), + label=bleach.clean("\n".join(label_text)), use_markup=True, margin=12, halign=Gtk.Align.START, @@ -368,7 +369,7 @@ class ArtistDetailPanel(Gtk.Box): "Collapse" if self.artist_details_expanded else "Expand" ) - self.artist_name.set_markup(util.esc(f"{artist.name}")) + self.artist_name.set_markup(bleach.clean(f"{artist.name}")) self.artist_name.set_tooltip_text(artist.name) if self.artist_details_expanded: @@ -378,7 +379,7 @@ class ArtistDetailPanel(Gtk.Box): self.artist_stats.set_markup(self.format_stats(artist)) if artist.biography: - self.artist_bio.set_markup(util.esc(artist.biography)) + self.artist_bio.set_markup(bleach.clean(artist.biography)) self.artist_bio.show() else: self.artist_bio.hide() diff --git a/sublime_music/ui/browse.py b/sublime_music/ui/browse.py index f95f8ae..21d2fe1 100644 --- a/sublime_music/ui/browse.py +++ b/sublime_music/ui/browse.py @@ -1,6 +1,8 @@ from functools import partial from typing import Any, cast, List, Optional, Tuple +import bleach + from gi.repository import Gdk, Gio, GLib, GObject, Gtk, Pango from ..adapters import AdapterManager, api_objects as API, CacheMissError, Result @@ -383,7 +385,7 @@ class MusicDirectoryList(Gtk.Box): in ("folder-download-symbolic", "view-pin-symbolic") ), status_icon, - util.esc(song.title), + bleach.clean(song.title), util.format_song_duration(song.duration), song.id, ] @@ -442,7 +444,7 @@ class MusicDirectoryList(Gtk.Box): rowbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) rowbox.add( Gtk.Label( - label=f"{util.esc(model.name)}", + label=bleach.clean(f"{model.name}"), use_markup=True, margin=8, halign=Gtk.Align.START, diff --git a/sublime_music/ui/main.py b/sublime_music/ui/main.py index f7b1e51..ab858df 100644 --- a/sublime_music/ui/main.py +++ b/sublime_music/ui/main.py @@ -1,6 +1,8 @@ from functools import partial from typing import Any, Callable, Dict, Optional, Set, Tuple +import bleach + from gi.repository import Gdk, GLib, GObject, Gtk, Pango from ..adapters import ( @@ -1089,13 +1091,13 @@ class MainWindow(Gtk.ApplicationWindow): self._remove_all_from_widget(self.song_results) for song in search_results.songs: label_text = util.dot_join( - f"{util.esc(song.title)}", - util.esc(song.artist.name if song.artist else None), + f"{song.title}", + song.artist.name if song.artist else None, ) assert song.album and song.album.id self.song_results.add( self._create_search_result_row( - label_text, "album", song.album.id, song.cover_art + bleach.clean(label_text), "album", song.album.id, song.cover_art ) ) @@ -1106,13 +1108,13 @@ class MainWindow(Gtk.ApplicationWindow): self._remove_all_from_widget(self.album_results) for album in search_results.albums: label_text = util.dot_join( - f"{util.esc(album.name)}", - util.esc(album.artist.name if album.artist else None), + f"{album.name}", + album.artist.name if album.artist else None, ) assert album.id self.album_results.add( self._create_search_result_row( - label_text, "album", album.id, album.cover_art + bleach.clean(label_text), "album", album.id, album.cover_art ) ) @@ -1122,11 +1124,13 @@ class MainWindow(Gtk.ApplicationWindow): if search_results.artists is not None: self._remove_all_from_widget(self.artist_results) for artist in search_results.artists: - label_text = util.esc(artist.name) assert artist.id self.artist_results.add( self._create_search_result_row( - label_text, "artist", artist.id, artist.artist_image_url + bleach.clean(artist.name), + "artist", + artist.id, + artist.artist_image_url, ) ) @@ -1136,10 +1140,12 @@ class MainWindow(Gtk.ApplicationWindow): if search_results.playlists: self._remove_all_from_widget(self.playlist_results) for playlist in search_results.playlists: - label_text = util.esc(playlist.name) self.playlist_results.add( self._create_search_result_row( - label_text, "playlist", playlist.id, playlist.cover_art + bleach.clean(playlist.name), + "playlist", + playlist.id, + playlist.cover_art, ) ) @@ -1182,10 +1188,10 @@ class DownloadStatusBox(Gtk.Box): ) self.add(image) - artist = util.esc(self.song.artist.name if self.song.artist else None) - label_text = util.dot_join(f"{util.esc(self.song.title)}", artist) + artist = self.song.artist.name if self.song.artist else None + label_text = util.dot_join(f"{self.song.title}", artist) self.song_label = Gtk.Label( - label=label_text, + label=bleach.clean(label_text), ellipsize=Pango.EllipsizeMode.END, max_width_chars=30, name="currently-downloading-song-title", diff --git a/sublime_music/ui/player_controls.py b/sublime_music/ui/player_controls.py index cb80756..9a880f3 100644 --- a/sublime_music/ui/player_controls.py +++ b/sublime_music/ui/player_controls.py @@ -4,6 +4,8 @@ from datetime import timedelta from functools import partial from typing import Any, Callable, Dict, Optional, Set, Tuple +import bleach + from gi.repository import Gdk, GdkPixbuf, GLib, GObject, Gtk, Pango from . import util @@ -168,18 +170,20 @@ class PlayerControls(Gtk.ActionBar): order_token=self.cover_art_update_order_token, ) - self.song_title.set_markup(util.esc(app_config.state.current_song.title)) + self.song_title.set_markup( + bleach.clean(app_config.state.current_song.title) + ) # TODO (#71): use walrus once MYPY gets its act together album = app_config.state.current_song.album artist = app_config.state.current_song.artist if album: - self.album_name.set_markup(util.esc(album.name)) + self.album_name.set_markup(bleach.clean(album.name)) self.artist_name.show() else: self.album_name.set_markup("") self.album_name.hide() if artist: - self.artist_name.set_markup(util.esc(artist.name)) + self.artist_name.set_markup(bleach.clean(artist.name)) self.artist_name.show() else: self.artist_name.set_markup("") @@ -228,13 +232,13 @@ class PlayerControls(Gtk.ActionBar): new_store = [] def calculate_label(song_details: Song) -> str: - title = util.esc(song_details.title) - # TODO (#71): use walrus once MYPY works with this - # album = util.esc(album.name if (album := song_details.album) else None) - # artist = util.esc(artist.name if (artist := song_details.artist) else None) # noqa - album = util.esc(song_details.album.name if song_details.album else None) - artist = util.esc(song_details.artist.name if song_details.artist else None) - return f"{title}\n{util.dot_join(album, artist)}" + title = song_details.title + # TODO (#71): use walrus once MYPY gets its act together + # album = a.name if (a := song_details.album) else None + # artist = a.name if (a := song_details.artist) else None + album = song_details.album.name if song_details.album else None + artist = song_details.artist.name if song_details.artist else None + return bleach.clean(f"{title}\n{util.dot_join(album, artist)}") def make_idle_index_capturing_function( idx: int, diff --git a/sublime_music/ui/util.py b/sublime_music/ui/util.py index c49cd6b..ead25e7 100644 --- a/sublime_music/ui/util.py +++ b/sublime_music/ui/util.py @@ -94,18 +94,6 @@ def format_sequence_duration(duration: Optional[timedelta]) -> str: return ", ".join(format_components) -def esc(string: Optional[str]) -> str: - """ - >>> esc("test & test") - "test & test" - >>> esc(None) - '' - """ - if string is None: - return "" - return string.replace("&", "&").replace(" target='_blank'", "") - - def dot_join(*items: Any) -> str: """ Joins the given strings with a dot character. Filters out ``None`` values.