Skip to content

Commit

Permalink
make self.py3.replace helper
Browse files Browse the repository at this point in the history
  • Loading branch information
lasers committed Mar 31, 2024
1 parent 781e4f6 commit 835e047
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 69 deletions.
21 changes: 20 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,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")]})
Expand Down Expand Up @@ -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
Expand All @@ -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)

Expand Down Expand Up @@ -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:
Expand Down
104 changes: 37 additions & 67 deletions py3status/modules/spotify.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -60,7 +66,6 @@
{'color': '#FF0000', 'full_text': 'Spotify stopped'}
"""

import re
from datetime import timedelta
from time import sleep

Expand All @@ -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".
Expand All @@ -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
Expand All @@ -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):
"""
Expand Down
42 changes: 41 additions & 1 deletion py3status/py3.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os
import re
import shlex
import sys
import time
Expand Down Expand Up @@ -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 = {}
Expand Down Expand Up @@ -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):
Expand All @@ -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.
Expand Down Expand Up @@ -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,
Expand Down

0 comments on commit 835e047

Please sign in to comment.