diff --git a/.gitignore b/.gitignore index ed8ebf5..5979577 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -__pycache__ \ No newline at end of file +__pycache__ +*~ \ No newline at end of file diff --git a/__pycache__/config.cpython-313.pyc b/__pycache__/config.cpython-313.pyc deleted file mode 100644 index b785046..0000000 Binary files a/__pycache__/config.cpython-313.pyc and /dev/null differ diff --git a/__pycache__/spotify.cpython-313.pyc b/__pycache__/spotify.cpython-313.pyc deleted file mode 100644 index 4e1c71d..0000000 Binary files a/__pycache__/spotify.cpython-313.pyc and /dev/null differ diff --git a/spotify.py~ b/spotify.py~ deleted file mode 100644 index c5de735..0000000 --- a/spotify.py~ +++ /dev/null @@ -1,204 +0,0 @@ -# The MIT License (MIT) - -# Copyright(c) 2022 Ben Hunt - -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files(the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: - -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -from subprocess import CompletedProcess, run -from typing import List - -from libqtile.group import _Group -from libqtile.config import Screen - -from libqtile.widget import base -from libqtile.log_utils import logger - -SPOTIFY = "Spotify" - - -class Spotify(base.ThreadPoolText): - """ - A widget to interact with spotify via dbus. - """ - - defaults = [ - ("play_icon", "", "icon to display when playing music"), - ("pause_icon", "", "icon to display when music paused"), - ("update_interval", 0.5, "polling rate in seconds"), - ("format", "{icon} {artist}:{album} - {track}", "Spotify display format"), - ] - - def __init__(self, **config) -> None: - # init base class - super().__init__(text="", **config) - self.add_defaults(Spotify.defaults) - self.add_callbacks( - { - "Button1": self.toggle_music, - } - ) - - def _is_proc_running(self, proc_name: str) -> bool: - # create regex pattern to search for to avoid similar named processes - pattern = f"{proc_name}$" - # pgrep will return a string of pids for matching processes - cmd = ["pgrep", "-fli", pattern] - proc_out = run(cmd, capture_output=True).stdout.decode( - "utf-8" - ) - - return proc_out != "" - - def toggle_between_groups(self) -> None: - """ - remember which group you were on before you switched to spotify - so you can toggle between the 2 groups - """ - current_screen: Screen = self.qtile.current_screen - current_group_info = self.qtile.current_group.info() - logger.warning(f"current group info: {current_group_info}") - windows = current_group_info["windows"] - if SPOTIFY in windows: - # go to previous group - logger.warning("going to previous group") - current_screen.group.get_previous_group().toscreen() - logger.warning("went to previous group") - else: - self.go_to_spotify() - - def go_to_spotify(self) -> None: - """ - Switch to whichever group has the current spotify instance - if none exists then we will spawn an instance on the current group - """ - # spawn spotify if not already running - if not self._is_proc_running("spotify"): - self.qtile.spawn("spotify", shell=True) - return - - all_groups: List[_Group] = self.qtile.groups - # we need to find the group that has spotify in it - for group in all_groups: - info = group.info() - # get a list of windows for the group. We look for 'Spotify here' - windows = info["windows"] - if SPOTIFY in windows: - name = group.name - # switch to 'name' group - spotify_group = self.qtile.groups_map[name] - spotify_group.toscreen() - break - - def poll(self) -> str: # type: ignore - """Poll content for the text box""" - vars = { - "icon": self.pause_icon if self.playing else self.play_icon, - "artist": self.artist, - "track": self.song_title, - "album": self.album, - } - - return self.format.format(**vars) # type: ignore - - def toggle_music(self) -> None: - cmd = """ - dbus-send --print-reply --dest=org.mpris.MediaPlayer2.spotify \ - /org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player.PlayPause - """ - run(cmd, shell=True) - - - def get_proc_output(self, proc: CompletedProcess) -> str: - stdout = proc.stdout.decode("utf-8") - no_spotify = "Error" in stdout - return ( - "" - if no_spotify - else stdout.rstrip() - ) - - - @property - def _meta(self) -> str: - cmd = """dbus-send --print-reply --dest=org.mpris.MediaPlayer2.spotify \ - /org/mpris/MediaPlayer2 \ - org.freedesktop.DBus.Properties.Get \ - string:'org.mpris.MediaPlayer2.Player' \ - string:'Metadata' - """ - proc = run( cmd, shell=True, capture_output=True) - - output: str = proc.stdout.decode("utf-8").replace("'", "ʼ").rstrip() - return "" if ("org.mpris.MediaPlayer2.spotify" in output) else output - - @property - def artist(self) -> str: - cmd =""" - dbus-send --print-reply --dest=org.mpris.MediaPlayer2.spotify \ - /org/mpris/MediaPlayer2 \ - org.freedesktop.DBus.Properties.Get string:'org.mpris.MediaPlayer2.Player' \ - string:'Metadata' | grep -m1 'xesam:artist' -b2 | tail -n 1 | grep -o '\".*\"' | \ - sed 's/\"//g' | sed -e 's/&/and/g' - """ - proc: CompletedProcess = run(cmd, - shell=True, - capture_output=True, - ) - - return self.get_proc_output(proc) - - @property - def song_title(self) -> str: - cmd = f""" - echo '{self._meta}' | grep -m1 'xesam:title' -b1 | tail -n1 | grep -o '\".*\"' | \ - sed 's/\"//g' | sed -e 's/&/and/g' - """ - proc: CompletedProcess = run(cmd, - shell=True, - capture_output=True - ) - - return self.get_proc_output(proc) - - @property - def album(self) -> str: - cmd = f""" - echo '{self._meta}' | grep -m1 'xesam:album' -b1 | tail -n1 | grep -o '\".*\"' | \ - sed 's/\"//g' | sed -e 's/&/and/g' - """ - proc = run(cmd, - shell=True, - capture_output=True, - ) - - return self.get_proc_output(proc) - - @property - def playing(self) -> bool: - cmd = """ - dbus-send --print-reply --dest=org.mpris.MediaPlayer2.spotify \ - /org/mpris/MediaPlayer2 org.freedesktop.DBus.Properties.Get \ - string:'org.mpris.MediaPlayer2.Player' string:'PlaybackStatus' | grep -o Playing - """ - play = run(cmd, - shell=True, - capture_output=True, - ).stdout.decode("utf-8") - - return play != ""