1# -*- coding: utf-8 -*- 2# Copyright (C) 2015, Juan Riquelme González 3# 4# This program is free software: you can redistribute it and/or modify 5# it under the terms of the GNU General Public License as published by 6# the Free Software Foundation, either version 3 of the License, or 7# (at your option) any later version. 8# 9# This program is distributed in the hope that it will be useful, 10# but WITHOUT ANY WARRANTY; without even the implied warranty of 11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12# GNU General Public License for more details. 13# 14# You should have received a copy of the GNU General Public License 15# along with this program. If not, see <http://www.gnu.org/licenses/>. 16 17import subprocess 18from functools import partial 19 20from libqtile import pangocffi 21from libqtile.widget import base 22 23 24class Cmus(base.ThreadPoolText): 25 """A simple Cmus widget. 26 27 Show the artist and album of now listening song and allow basic mouse 28 control from the bar: 29 30 - toggle pause (or play if stopped) on left click; 31 - skip forward in playlist on scroll up; 32 - skip backward in playlist on scroll down. 33 34 Cmus (https://cmus.github.io) should be installed. 35 """ 36 orientations = base.ORIENTATION_HORIZONTAL 37 defaults = [ 38 ('play_color', '00ff00', 'Text colour when playing.'), 39 ('noplay_color', 'cecece', 'Text colour when not playing.'), 40 ('update_interval', 0.5, 'Update Time in seconds.') 41 ] 42 43 def __init__(self, **config): 44 base.ThreadPoolText.__init__(self, "", **config) 45 self.add_defaults(Cmus.defaults) 46 self.status = "" 47 self.local = None 48 49 self.add_callbacks({ 50 'Button1': self.play, 51 'Button4': partial(subprocess.Popen, ['cmus-remote', '-n']), 52 'Button5': partial(subprocess.Popen, ['cmus-remote', '-r']), 53 }) 54 55 def get_info(self): 56 """Return a dictionary with info about the current cmus status.""" 57 try: 58 output = self.call_process(['cmus-remote', '-C', 'status']) 59 except subprocess.CalledProcessError as err: 60 output = err.output 61 if output.startswith("status"): 62 output = output.splitlines() 63 info = {'status': "", 64 'file': "", 65 'artist': "", 66 'album': "", 67 'title': "", 68 'stream': ""} 69 70 for line in output: 71 for data in info: 72 if data in line: 73 index = line.index(data) 74 if index < 5: 75 info[data] = line[len(data) + index:].strip() 76 break 77 elif line.startswith("set"): 78 return info 79 return info 80 81 def now_playing(self): 82 """Return a string with the now playing info (Artist - Song Title).""" 83 info = self.get_info() 84 now_playing = "" 85 if info: 86 status = info['status'] 87 if self.status != status: 88 self.status = status 89 if self.status == "playing": 90 self.layout.colour = self.play_color 91 else: 92 self.layout.colour = self.noplay_color 93 self.local = info['file'].startswith("/") 94 title = info['title'] 95 if self.local: 96 artist = info['artist'] 97 now_playing = "{0} - {1}".format(artist, title) 98 else: 99 if info['stream']: 100 now_playing = info['stream'] 101 else: 102 now_playing = title 103 if now_playing: 104 now_playing = "♫ {0}".format(now_playing) 105 return pangocffi.markup_escape_text(now_playing) 106 107 def play(self): 108 """Play music if stopped, else toggle pause.""" 109 if self.status in ('playing', 'paused'): 110 subprocess.Popen(['cmus-remote', '-u']) 111 elif self.status == 'stopped': 112 subprocess.Popen(['cmus-remote', '-p']) 113 114 def poll(self): 115 """Poll content for the text box.""" 116 return self.now_playing() 117