From 835e0478c1b46a7ead95f3474df87671d5e6f9e9 Mon Sep 17 00:00:00 2001 From: lasers Date: Wed, 27 Mar 2024 00:44:39 -0500 Subject: [PATCH] make self.py3.replace helper --- py3status/modules/playerctl.py | 21 ++++++- py3status/modules/spotify.py | 104 ++++++++++++--------------------- py3status/py3.py | 42 ++++++++++++- 3 files changed, 98 insertions(+), 69 deletions(-) diff --git a/py3status/modules/playerctl.py b/py3status/modules/playerctl.py index 605317a884..e94c6a06b6 100644 --- a/py3status/modules/playerctl.py +++ b/py3status/modules/playerctl.py @@ -1,7 +1,7 @@ r""" Display song/video and control players supported by playerctl -Playerctl is a command-line utility for controlling media players +Playerctl is a command-line utility for controlling media players that implement the MPRIS D-Bus Interface Specification. With Playerctl you can bind player actions to keys and get metadata about the currently playing song or video. @@ -25,6 +25,13 @@ '[\?if=status=Stopped .. ][[{artist}][\?soft - ][{title}|{player}]]]')* format_player_separator: show separator if more than one player (default ' ') players: list of players to track. An empty list tracks all players (default []) + replacements: specify string replacements to use on placeholders + *(default { + "artist": [ + "([\(\[][^)\]]*?(bonus|demo|edit|explicit|extended|feat|mono|remaster|stereo|version)[^)\]]*?[\)\]])", + "([\-,;/])([^\-,;/])*(bonus|demo|edit|explicit|extended|feat|mono|remaster|stereo|version).*" + ] + })* seek_delta: time (in seconds) to change the playback's position by (default 5) thresholds: specify color thresholds to use for different placeholders (default {"status": [("Playing", "good"), ("Paused", "degraded"), ("Stopped", "bad")]}) @@ -99,6 +106,12 @@ class Py3status: ) format_player_separator = " " players = [] + replacements = { + "artist": [ + "([\(\[][^)\]]*?(bonus|demo|edit|explicit|extended|feat|mono|remaster|stereo|version)[^)\]]*?[\)\]])", + "([\-,;/])([^\-,;/])*(bonus|demo|edit|explicit|extended|feat|mono|remaster|stereo|version).*", + ] + } seek_delta = 5 thresholds = {"status": [("Playing", "good"), ("Paused", "degraded"), ("Stopped", "bad")]} volume_delta = 10 @@ -116,6 +129,7 @@ class Meta: def post_config_hook(self): self.thresholds_init = self.py3.get_color_names_list(self.format_player) + self.placeholders = self.py3.get_placeholders_list(self.format_player) self.position = self.py3.format_contains(self.format_player, "position") self.cache_timeout = getattr(self, "cache_timeout", 1) @@ -279,6 +293,11 @@ def playerctl(self): if self.position and player_data["status"] == "Playing" and player_data["position"]: cached_until = self.cache_timeout + # Replace the values + for x in self.placeholders: + if x in player_data: + player_data[x] = self.py3.replace(player_data[x], x) + # Set the color of a player for key in self.thresholds_init: if key in player_data: diff --git a/py3status/modules/spotify.py b/py3status/modules/spotify.py index 29b87e7a8a..3d9fdf533b 100644 --- a/py3status/modules/spotify.py +++ b/py3status/modules/spotify.py @@ -15,11 +15,17 @@ (default 'Spotify not running') format_stopped: define output if spotify is not playing (default 'Spotify stopped') - sanitize_titles: whether to remove meta data from album/track title - (default True) - sanitize_words: which meta data to remove - *(default ['bonus', 'demo', 'edit', 'explicit', 'extended', - 'feat', 'mono', 'remaster', 'stereo', 'version'])* + replacements: specify string replacements to use on placeholders + *(default { + "album": [ + "([\(\[][^)\]]*?(bonus|demo|edit|explicit|extended|feat|mono|remaster|stereo|version)[^)\]]*?[\)\]])", + "([\-,;/])([^\-,;/])*(bonus|demo|edit|explicit|extended|feat|mono|remaster|stereo|version).*" + ], + "artist": [ + "([\(\[][^)\]]*?(bonus|demo|edit|explicit|extended|feat|mono|remaster|stereo|version)[^)\]]*?[\)\]])", + "([\-,;/])([^\-,;/])*(bonus|demo|edit|explicit|extended|feat|mono|remaster|stereo|version).*" + ], + })* Format placeholders: {album} album name @@ -60,7 +66,6 @@ {'color': '#FF0000', 'full_text': 'Spotify stopped'} """ -import re from datetime import timedelta from time import sleep @@ -82,47 +87,20 @@ class Py3status: format = "{artist} : {title}" format_down = "Spotify not running" format_stopped = "Spotify stopped" - sanitize_titles = True - sanitize_words = [ - "bonus", - "demo", - "edit", - "explicit", - "extended", - "feat", - "mono", - "remaster", - "stereo", - "version", - ] + replacements = { + "album": [ + "([\(\[][^)\]]*?(bonus|demo|edit|explicit|extended|feat|mono|remaster|stereo|version)[^)\]]*?[\)\]])", + "([\-,;/])([^\-,;/])*(bonus|demo|edit|explicit|extended|feat|mono|remaster|stereo|version).*", + ], + "artist": [ + "([\(\[][^)\]]*?(bonus|demo|edit|explicit|extended|feat|mono|remaster|stereo|version)[^)\]]*?[\)\]])", + "([\-,;/])([^\-,;/])*(bonus|demo|edit|explicit|extended|feat|mono|remaster|stereo|version).*", + ], + } def _spotify_cmd(self, action): return SPOTIFY_CMD.format(dbus_client=self.dbus_client, cmd=action) - def post_config_hook(self): - """ """ - # Match string after hyphen, comma, semicolon or slash containing any metadata word - # examples: - # - Remastered 2012 - # / Radio Edit - # ; Remastered - self.after_delimiter = self._compile_re(r"([\-,;/])([^\-,;/])*(META_WORDS_HERE).*") - - # Match brackets with their content containing any metadata word - # examples: - # (Remastered 2017) - # [Single] - # (Bonus Track) - self.inside_brackets = self._compile_re(r"([\(\[][^)\]]*?(META_WORDS_HERE)[^)\]]*?[\)\]])") - - def _compile_re(self, expression): - """ - Compile given regular expression for current sanitize words - """ - meta_words = "|".join(self.sanitize_words) - expression = expression.replace("META_WORDS_HERE", meta_words) - return re.compile(expression, re.IGNORECASE) - def _get_playback_status(self): """ Get the playback status. One of: "Playing", "Paused" or "Stopped". @@ -145,10 +123,6 @@ def _get_text(self): microtime = metadata.get("mpris:length") rtime = str(timedelta(seconds=microtime // 1_000_000)) title = metadata.get("xesam:title") - if self.sanitize_titles: - album = self._sanitize_title(album) - title = self._sanitize_title(title) - playback_status = self._get_playback_status() if playback_status == "Playing": color = self.py3.COLOR_PLAYING or self.py3.COLOR_GOOD @@ -160,29 +134,25 @@ def _get_text(self): self.py3.COLOR_PAUSED or self.py3.COLOR_DEGRADED, ) - return ( - self.py3.safe_format( - self.format, - dict( - title=title, - artist=artist, - album=album, - time=rtime, - playback=playback_status, - ), - ), - color, - ) + spotify_data = { + "title": title, + "artist": artist, + "album": album, + "time": rtime, + "playback": playback_status, + } + + # Replace the values + for x in self.placeholders: + if x in spotify_data: + spotify_data[x] = self.py3.replace(spotify_data[x], x) + + return (self.py3.safe_format(self.format, spotify_data), color) except Exception: return (self.format_down, self.py3.COLOR_OFFLINE or self.py3.COLOR_BAD) - def _sanitize_title(self, title): - """ - Remove redundant metadata from title and return it - """ - title = re.sub(self.inside_brackets, "", title) - title = re.sub(self.after_delimiter, "", title) - return title.strip() + def post_config_hook(self): + self.placeholders = self.py3.get_placeholders_list(self.format) def spotify(self): """ diff --git a/py3status/py3.py b/py3status/py3.py index 8550f89f1b..b60a6e3f93 100644 --- a/py3status/py3.py +++ b/py3status/py3.py @@ -1,4 +1,5 @@ import os +import re import shlex import sys import time @@ -102,6 +103,7 @@ def __init__(self, module=None): self._format_placeholders = {} self._format_placeholders_cache = {} self._module = module + self._replacements = None self._report_exception_cache = set() self._thresholds = None self._threshold_gradients = {} @@ -177,7 +179,7 @@ def _thresholds_init(self): except TypeError: pass self._thresholds[None] = [(x[0], self._get_color(x[1])) for x in thresholds] - return + elif isinstance(thresholds, dict): for key, value in thresholds.items(): if isinstance(value, list): @@ -187,6 +189,21 @@ def _thresholds_init(self): pass self._thresholds[key] = [(x[0], self._get_color(x[1])) for x in value] + def _replacements_init(self): + """ + Initiate and check any replacements set + """ + replacements = getattr(self._py3status_module, "replacements", []) + self._replacements = {} + + if isinstance(replacements, list): + self._replacements[None] = [re.compile(x, re.IGNORECASE) for x in replacements] + + elif isinstance(replacements, dict): + for key, value in replacements.items(): + if isinstance(value, list): + self._replacements[key] = [re.compile(x, re.IGNORECASE) for x in value] + def _get_module_info(self, module_name): """ THIS IS PRIVATE AND UNSUPPORTED. @@ -1214,6 +1231,29 @@ def threshold_get_color(self, value, name=None): return color + def replace(self, value, name=None): + """ + Replace string using replacements. + + :param value: string value to be replaced + :param name: accepts a name + """ + # If first run, then process the replacements data. + if self._replacements is None: + self._replacements_init() + + if not value or not isinstance(value, str): + return value + + name_used = name + if name_used not in self._replacements: + name_used = None + + for pattern in self._replacements.get(name_used, []): + value = re.sub(pattern, "", value) + + return value + def request( self, url,