Skip to content

Commit

Permalink
music modules: implement sanitize_words support for all music modules
Browse files Browse the repository at this point in the history
  • Loading branch information
dpeukert committed Feb 16, 2024
1 parent 137201b commit 5d0ddcc
Show file tree
Hide file tree
Showing 9 changed files with 173 additions and 80 deletions.
13 changes: 13 additions & 0 deletions py3status/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,3 +270,16 @@
MARKUP_LANGUAGES = ["pango", "none"]

ON_ERROR_VALUES = ["hide", "show"]

DEFAULT_SANITIZE_WORDS = [
"bonus",
"demo",
"edit",
"explicit",
"extended",
"feat",
"mono",
"remaster",
"stereo",
"version",
]
12 changes: 12 additions & 0 deletions py3status/modules/cmus.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@
*(default '[\?if=is_started [\?if=is_playing > ][\?if=is_paused \|\| ]'
'[\?if=is_stopped .. ][[{artist}][\?soft - ][{title}]'
'|\?show cmus: waiting for user input]]')*
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'])*
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 @@ -83,6 +88,8 @@
{'color': '#FF0000', 'full_text': '.. cmus: waiting for user input'}
"""

from py3status.constants import DEFAULT_SANITIZE_WORDS

STRING_NOT_INSTALLED = "not installed"


Expand All @@ -100,6 +107,8 @@ class Py3status:
r"[\?if=is_stopped .. ][[{artist}][\?soft - ][{title}]"
r"|\?show cmus: waiting for user input]]"
)
sanitize_titles = True
sanitize_words = DEFAULT_SANITIZE_WORDS
sleep_timeout = 20

def post_config_hook(self):
Expand Down Expand Up @@ -149,6 +158,9 @@ def _manipulate_data(self, data):
temporary[key] = True
elif value in ("false", "disabled"):
temporary[key] = False
# sanitize album and title
elif self.sanitize_titles and key in ("album", "title"):
temporary[key] = self.py3.sanitize_title(self.sanitize_words, value)
# string not modified
else:
temporary[key] = value
Expand Down
17 changes: 17 additions & 0 deletions py3status/modules/deadbeef.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
Configuration parameters:
cache_timeout: refresh interval for this module (default 5)
format: display format for this module (default '[{artist} - ][{title}]')
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'])*
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 @@ -49,6 +54,8 @@
{'color': '#ffff00', 'full_text': 'Music For Programming - Lackluster'}
"""

from py3status.constants import DEFAULT_SANITIZE_WORDS

STRING_NOT_INSTALLED = "not installed"


Expand All @@ -58,6 +65,8 @@ class Py3status:
# available configuration parameters
cache_timeout = 5
format = "[{artist} - ][{title}]"
sanitize_titles = True
sanitize_words = DEFAULT_SANITIZE_WORDS
sleep_timeout = 20

class Meta:
Expand Down Expand Up @@ -119,6 +128,14 @@ def deadbeef(self):
beef_data = dict(zip(self.placeholders, line.split(self.separator)))
cached_until = self.cache_timeout

# Sanitize album and title
if self.sanitize_titles:
if "album" in beef_data:
beef_data["album"] = self.py3.sanitize_title(self.sanitize_words, beef_data["album"])

if "title" in beef_data:
beef_data["title"] = self.py3.sanitize_title(self.sanitize_words, beef_data["title"])

if beef_data["isplaying"]:
color = self.color_playing
else:
Expand Down
17 changes: 17 additions & 0 deletions py3status/modules/moc.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@
format: display format for this module
*(default '\?if=is_started [\?if=is_stopped \[\] moc|'
'[\?if=is_paused \|\|][\?if=is_playing >] {title}]')*
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'])*
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 @@ -72,6 +77,8 @@
{'color': '#FF0000', 'full_text': '[] moc'}
"""

from py3status.constants import DEFAULT_SANITIZE_WORDS

STRING_NOT_INSTALLED = "not installed"


Expand All @@ -88,6 +95,8 @@ class Py3status:
r"\?if=is_started [\?if=is_stopped \[\] moc|"
r"[\?if=is_paused \|\|][\?if=is_playing >] {title}]"
)
sanitize_titles = True
sanitize_words = DEFAULT_SANITIZE_WORDS
sleep_timeout = 20

def post_config_hook(self):
Expand Down Expand Up @@ -122,6 +131,14 @@ def moc(self):
category, value = line.split(": ", 1)
data[category.lower()] = value

# Sanitize album and title
if self.sanitize_titles:
if "album" in data:
data["album"] = self.py3.sanitize_title(self.sanitize_words, data["album"])

if "title" in data:
data["title"] = self.py3.sanitize_title(self.sanitize_words, data["title"])

self.state = data["state"]
if self.state == "PLAY":
is_playing = True
Expand Down
69 changes: 39 additions & 30 deletions py3status/modules/mpd_status.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@
max_width: maximum status length (default 120)
password: mpd password (default None)
port: mpd port (default '6600')
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'])*
state_pause: label to display for "paused" state (default '[pause]')
state_play: label to display for "playing" state (default '[play]')
state_stop: label to display for "stopped" state (default '[stop]')
Expand Down Expand Up @@ -74,33 +79,7 @@
from mpd import CommandError, ConnectionError, MPDClient

from py3status.composite import Composite


def song_attr(song, attr):
def parse_mtime(date_str):
return datetime.datetime.strptime(date_str, "%Y-%m-%dT%H:%M:%SZ")

if attr == "time":
try:
duration = int(song["time"])
if duration > 0:
minutes, seconds = divmod(duration, 60)
return f"{minutes:d}:{seconds:02d}"
raise ValueError
except (KeyError, ValueError):
return ""
elif attr == "position":
try:
return "{}".format(int(song["pos"]) + 1)
except (KeyError, ValueError):
return ""
elif attr == "mtime":
return parse_mtime(song["last-modified"]).strftime("%c")
elif attr == "mdate":
return parse_mtime(song["last-modified"]).strftime("%x")

return song.get(attr, "")

from py3status.constants import DEFAULT_SANITIZE_WORDS

class Py3status:
""" """
Expand All @@ -117,6 +96,8 @@ class Py3status:
max_width = 120
password = None
port = "6600"
sanitize_titles = True
sanitize_words = DEFAULT_SANITIZE_WORDS
state_pause = "[pause]"
state_play = "[play]"
state_stop = "[stop]"
Expand All @@ -133,6 +114,34 @@ def post_config_hook(self):
self.current_status = None
self.idle_thread = Thread()

def _song_attr(self, song, attr):
def parse_mtime(date_str):
return datetime.datetime.strptime(date_str, "%Y-%m-%dT%H:%M:%SZ")

if attr == "time":
try:
duration = int(song["time"])
if duration > 0:
minutes, seconds = divmod(duration, 60)
return f"{minutes:d}:{seconds:02d}"
raise ValueError
except (KeyError, ValueError):
return ""
elif attr == "position":
try:
return "{}".format(int(song["pos"]) + 1)
except (KeyError, ValueError):
return ""
elif attr == "mtime":
return parse_mtime(song["last-modified"]).strftime("%c")
elif attr == "mdate":
return parse_mtime(song["last-modified"]).strftime("%x")
# Sanitize album and title
elif self.sanitize_titles and attr in ("album", "title"):
return self.py3.sanitize_title(self.sanitize_words, song.get(attr, ""))

return song.get(attr, "")

def _get_mpd(self, disconnect=False):
if disconnect:
try:
Expand Down Expand Up @@ -229,8 +238,8 @@ def _get_status(self):

def attr_getter(attr):
if attr.startswith("next_"):
return song_attr(next_song, attr[5:])
return song_attr(song, attr)
return self._song_attr(next_song, attr[5:])
return self._song_attr(song, attr)

text = self.py3.safe_format(self.format, attr_getter=attr_getter)
if isinstance(text, Composite):
Expand Down Expand Up @@ -277,4 +286,4 @@ def kill(self):
"""
from py3status.module_test import module_test

module_test(Py3status)
module_test(Py3status, config={"format": "{artist} - {title}"})
18 changes: 17 additions & 1 deletion py3status/modules/mpris.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,18 @@
Keep in mind that the state has a higher priority than
player_priority. So when player_priority is "[mpd, bomi]" and mpd is
paused and bomi is playing than bomi wins. (default [])
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'])*
state_pause: specify icon for pause state (default u'\u25eb')
state_play: specify icon for play state (default u'\u25b7')
state_stop: specify icon for stop state (default u'\u25a1')
Format placeholders:
{album} album name
{artist} artiste name (first one)
{artist} artist name (first one)
{length} time duration of the song
{player} show name of the player
{player_shortname} show name of the player from busname (usually command line name)
Expand Down Expand Up @@ -117,6 +122,7 @@
from mpris2 import get_players_uri
from mpris2.types import Metadata_Map

from py3status.constants import DEFAULT_SANITIZE_WORDS

class STATE(IntEnum):
Playing = 0
Expand Down Expand Up @@ -282,6 +288,14 @@ def metadata(self, metadata=None):

self._metadata["nowplaying"] = metadata.get("vlc:nowplaying", None)

# Sanitize album and title
if self.parent.sanitize_titles:
if "album" in self._metadata:
self._metadata["album"] = self.parent.py3.sanitize_title(self.parent.sanitize_words, self._metadata["album"])

if "title" in self._metadata:
self._metadata["title"] = self.parent.py3.sanitize_title(self.parent.sanitize_words, self._metadata["title"])

if not self._metadata.get("title"):
self._metadata["title"] = "No Track"

Expand Down Expand Up @@ -382,6 +396,8 @@ class Py3status:
max_width = None
player_hide_non_canplay = []
player_priority = []
sanitize_titles = True
sanitize_words = DEFAULT_SANITIZE_WORDS
state_pause = "\u25eb"
state_play = "\u25b7"
state_stop = "\u25a1"
Expand Down
16 changes: 15 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,11 @@
'[\?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 [])
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'])*
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 @@ -73,6 +78,8 @@
import gi
from gi.repository import GLib, Playerctl

from py3status.constants import DEFAULT_SANITIZE_WORDS

gi.require_version("Playerctl", "2.0")


Expand All @@ -99,6 +106,8 @@ class Py3status:
)
format_player_separator = " "
players = []
sanitize_titles = True
sanitize_words = DEFAULT_SANITIZE_WORDS
seek_delta = 5
thresholds = {"status": [("Playing", "good"), ("Paused", "degraded"), ("Stopped", "bad")]}
volume_delta = 10
Expand Down Expand Up @@ -234,6 +243,11 @@ def _get_player_data(self, player):
data["status"] = player.props.status
data["volume"] = int(player.props.volume * 100)

# Sanitize album and title
if self.sanitize_titles:
data["album"] = self.py3.sanitize_title(self.sanitize_words, data["album"])
data["title"] = self.py3.sanitize_title(self.sanitize_words, data["title"])

return data

def _get_player_from_index(self, index):
Expand Down
Loading

0 comments on commit 5d0ddcc

Please sign in to comment.