Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

py3: add new helper: get_replacements_list #2242

Merged
merged 3 commits into from
Jan 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 21 additions & 14 deletions py3status/modules/cmus.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*(default '[\?if=is_started [\?if=is_playing > ][\?if=is_paused \|\| ]'
'[\?if=is_stopped .. ][[{artist}][\?soft - ][{title}]'
'|\?show cmus: waiting for user input]]')*
replacements: specify a list/dict of string placeholders to modify (default None)
sleep_timeout: sleep interval for this module. when cmus is not running,
this interval will be used. this allows some flexible timing where one
might want to refresh constantly with some placeholders... or to refresh
Expand Down Expand Up @@ -100,6 +101,7 @@ class Py3status:
r"[\?if=is_stopped .. ][[{artist}][\?soft - ][{title}]"
r"|\?show cmus: waiting for user input]]"
)
replacements = None
sleep_timeout = 20

def post_config_hook(self):
Expand All @@ -109,6 +111,7 @@ def post_config_hook(self):
self.color_stopped = self.py3.COLOR_STOPPED or self.py3.COLOR_BAD
self.color_paused = self.py3.COLOR_PAUSED or self.py3.COLOR_DEGRADED
self.color_playing = self.py3.COLOR_PLAYING or self.py3.COLOR_GOOD
self.replacements_init = self.py3.get_replacements_list(self.format)

def _seconds_to_time(self, value):
m, s = divmod(int(value), 60)
Expand Down Expand Up @@ -165,14 +168,14 @@ def cmus(self):
cached_until = self.sleep_timeout
color = self.py3.COLOR_BAD

is_started, data = self._get_cmus_data()
is_started, cmus_data = self._get_cmus_data()

if is_started:
cached_until = self.cache_timeout
data = self._organize_data(data)
data = self._manipulate_data(data)
cmus_data = self._organize_data(cmus_data)
cmus_data = self._manipulate_data(cmus_data)

status = data.get("status")
status = cmus_data.get("status")
if status == "playing":
is_playing = True
color = self.color_playing
Expand All @@ -183,19 +186,23 @@ def cmus(self):
is_stopped = True
color = self.color_stopped

for x in self.replacements_init:
if x in cmus_data:
cmus_data[x] = self.py3.replace(cmus_data[x], x)

cmus_data.update(
{
"is_paused": is_paused,
"is_playing": is_playing,
"is_started": is_started,
"is_stopped": is_stopped,
}
)

return {
"cached_until": self.py3.time_in(cached_until),
"color": color,
"full_text": self.py3.safe_format(
self.format,
dict(
is_paused=is_paused,
is_playing=is_playing,
is_started=is_started,
is_stopped=is_stopped,
**data,
),
),
"full_text": self.py3.safe_format(self.format, cmus_data),
}

def on_click(self, event):
Expand Down
7 changes: 7 additions & 0 deletions py3status/modules/deadbeef.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
Configuration parameters:
cache_timeout: refresh interval for this module (default 5)
format: display format for this module (default '[{artist} - ][{title}]')
replacements: specify a list/dict of string placeholders to modify (default None)
sleep_timeout: when deadbeef is not running, this interval will be used
to allow faster refreshes with time-related placeholders and/or
to refresh few times per minute rather than every few seconds
Expand Down Expand Up @@ -58,6 +59,7 @@ class Py3status:
# available configuration parameters
cache_timeout = 5
format = "[{artist} - ][{title}]"
replacements = None
sleep_timeout = 20

class Meta:
Expand Down Expand Up @@ -89,6 +91,7 @@ def post_config_hook(self):
self.color_paused = self.py3.COLOR_PAUSED or self.py3.COLOR_DEGRADED
self.color_playing = self.py3.COLOR_PLAYING or self.py3.COLOR_GOOD
self.color_stopped = self.py3.COLOR_STOPPED or self.py3.COLOR_BAD
self.replacements_init = self.py3.get_replacements_list(self.format)

def _is_running(self):
try:
Expand Down Expand Up @@ -124,6 +127,10 @@ def deadbeef(self):
else:
color = self.color_paused

for x in self.replacements_init:
if x in beef_data:
beef_data[x] = self.py3.replace(beef_data[x], x)

return {
"cached_until": self.py3.time_in(cached_until),
"full_text": self.py3.safe_format(self.format, beef_data),
Expand Down
27 changes: 17 additions & 10 deletions py3status/modules/moc.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
format: display format for this module
*(default '\?if=is_started [\?if=is_stopped \[\] moc|'
'[\?if=is_paused \|\|][\?if=is_playing >] {title}]')*
replacements: specify a list/dict of string placeholders to modify (default None)
sleep_timeout: when moc is not running, this interval will be used to
allow one to refresh constantly with time placeholders and/or
to refresh once every minute rather than every few seconds
Expand Down Expand Up @@ -88,6 +89,7 @@ class Py3status:
r"\?if=is_started [\?if=is_stopped \[\] moc|"
r"[\?if=is_paused \|\|][\?if=is_playing >] {title}]"
)
replacements = None
sleep_timeout = 20

def post_config_hook(self):
Expand All @@ -97,6 +99,7 @@ def post_config_hook(self):
self.color_stopped = self.py3.COLOR_STOPPED or self.py3.COLOR_BAD
self.color_paused = self.py3.COLOR_PAUSED or self.py3.COLOR_DEGRADED
self.color_playing = self.py3.COLOR_PLAYING or self.py3.COLOR_GOOD
self.replacements_init = self.py3.get_replacements_list(self.format)

def _get_moc_data(self):
try:
Expand Down Expand Up @@ -133,19 +136,23 @@ def moc(self):
is_stopped = True
color = self.color_stopped

for x in self.replacements_init:
if x in moc_data:
moc_data[x] = self.py3.replace(moc_data[x], x)

moc_data.update(
{
"is_paused": is_paused,
"is_playing": is_playing,
"is_started": is_started,
"is_stopped": is_stopped,
}
)

return {
"cached_until": self.py3.time_in(cached_until),
"color": color,
"full_text": self.py3.safe_format(
self.format,
dict(
is_paused=is_paused,
is_playing=is_playing,
is_started=is_started,
is_stopped=is_stopped,
**data,
),
),
"full_text": self.py3.safe_format(self.format, moc_data),
}

def on_click(self, event):
Expand Down
10 changes: 9 additions & 1 deletion py3status/modules/playerctl.py
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -25,6 +25,7 @@
'[\?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 a list/dict of string placeholders to modify (default None)
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")]})
Expand Down Expand Up @@ -100,6 +101,7 @@ class Py3status:
)
format_player_separator = " "
players = []
replacements = None
seek_delta = 5
thresholds = {"status": [("Playing", "good"), ("Paused", "degraded"), ("Stopped", "bad")]}
volume_delta = 10
Expand All @@ -117,6 +119,7 @@ class Meta:

def post_config_hook(self):
self.thresholds_init = self.py3.get_color_names_list(self.format_player)
self.replacements_init = self.py3.get_replacements_list(self.format_player)
self.position = self.py3.format_contains(self.format_player, "position")
self.cache_timeout = getattr(self, "cache_timeout", 1)

Expand Down Expand Up @@ -283,6 +286,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.replacements_init:
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:
Expand Down
91 changes: 24 additions & 67 deletions py3status/modules/spotify.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,8 @@
(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 a list/dict of string placeholders to modify
(default None)

Format placeholders:
{album} album name
Expand All @@ -45,6 +42,12 @@
button_previous = 5
format = "{title} by {artist} -> {time}"
format_down = "no Spotify"

# sanitize
replacements = {
"album": [("\s?[\(\[\-,;/][^)\],;/]*?(bonus|demo|edit|explicit|extended|feat|mono|remaster|stereo|version)[^)\],;/]*[\)\]]?", "")],
"title": [("\s?[\(\[\-,;/][^)\],;/]*?(bonus|demo|edit|explicit|extended|feat|mono|remaster|stereo|version)[^)\],;/]*[\)\]]?", "")]
}
}
```

Expand All @@ -60,7 +63,6 @@
{'color': '#FF0000', 'full_text': 'Spotify stopped'}
"""

import re
from datetime import timedelta
from time import sleep

Expand All @@ -82,47 +84,11 @@ 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 = None

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".
Expand All @@ -145,10 +111,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
Expand All @@ -160,29 +122,24 @@ 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,
}

for x in self.replacements_init:
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.replacements_init = self.py3.get_replacements_list(self.format)

def spotify(self):
"""
Expand Down
Loading
Loading