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