# -*- python -*- ''' xpybar – xmobar replacement written in python Copyright © 2014, 2015, 2016, 2017, 2018, 2019 Mattias Andrée (m@maandreese) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ''' import sys import subprocess from util import * class MOC: # TODO add support for waiting for events and reading settings ''' Music On Console interface @variable state The state of moc, one of: MOC.NOT_RUNNING, MOC.STOPPED, MOC.PAUSED, MOC.PLAYING ''' NOT_RUNNING = None ''' The MOC server is not running ''' STOPPED = 'STOP' ''' No file is played ''' PAUSED = 'PAUSE' ''' The current file is paused ''' PLAYING = 'PLAY' ''' The current file is playing ''' def __init__(self): ''' Constructor ''' self.__info = {} for line in MOC.__interact('--info').stdout.read().decode('utf-8', 'replace').split('\n'): if ': ' in line: line = line.split(': ') self.__info[line[0]] = ': '.join(line[1:]) self.state = self['State'] if 'State' in self else None def __contains__(self, key): ''' Get whether or not a key is available @param key:str The key @return :bool The availability ''' return key in self.__info def __getitem__(self, key): ''' Get a details @param key:str The key @return :str The value associated with the key The following are defined (may be empty) if moc is playing or paused: File, Title, Artist, SongTitle, Album, TotalTime, TimeLet, TotalSec, CurrentTime, CurrentSec, Bitrate. ''' return self.__info[key] def keys(self): ''' Get available detail keys @return :itr The keys ''' return self.__info.keys() @staticmethod def __interact(*args): ''' Run mocp @param args:*str The command line arguments, except the first @return :Popen The spawned process ''' command = ['mocp'] + list(args) return subprocess.Popen(command, stderr = subprocess.PIPE, stdout = subprocess.PIPE) @staticmethod def enqueue(files): ''' Add files to the queue @param files:itr The files @return :Popen The spawned process ''' return MOC.__interact('--enqueue', '--', *files) @staticmethod def append(files): ''' Append files, directories (recursively) and playlists to the playlist @param files:itr The files @return :Popen The spawned process ''' return MOC.__interact('--append', '--', *files) @staticmethod def clear(): ''' Clear the playlist @return :Popen The spawned process ''' return MOC.__interact('--clear') @staticmethod def play(): ''' Start playing from the first item on the playlist @return :Popen The spawned process ''' return MOC.__interact('--play') @staticmethod def next(): ''' Request playing the next song from the server's playlist @return :Popen The spawned process ''' return MOC.__interact('--next') @staticmethod def previous(): ''' Request playing the previous song from the server's playlist @return :Popen The spawned process ''' return MOC.__interact('--previous') @staticmethod def stop(): ''' Request the server to stop playing @return :Popen The spawned process ''' return MOC.__interact('--stop') @staticmethod def exit(): ''' Bring down the server @return :Popen The spawned process ''' return MOC.__interact('--exit') @staticmethod def pause(): ''' Request the server to pause playing @return :Popen The spawned process ''' return MOC.__interact('--pause') @staticmethod def unpause(): ''' Request the server to resume playing when paused @return :Popen The spawned process ''' return MOC.__interact('--unpause') @staticmethod def toggle_pause(): ''' Toggle between play and pause @return :Popen The spawned process ''' return MOC.__interact('--toggle-pause') @staticmethod def seek(seconds): ''' Seek forward (positive) or backward (negative) in the file currently being played @param seconds:int The number of seconds to seek forward, negative for backwards @return :Popen? The spawned process, `None` if nothing is spawned ''' if not seconds == 0: return MOC.__interact('--seek', ('+%i' if seconds > 0 else '%i') % (seconds)) return None @staticmethod def volume(volume, relative = True): ''' Adjust the mixer volume @param volume:int The volume or volume increment @param relative:bool Whether to adjust, otherwise set @return :Popen The spawned process ''' if relative and (not volume == 0): return MOC.__interact('--volume', ('+%i' if volume > 0 else '%i') % (volume)) return MOC.__interact('--volume', '%i' % (max(0, volume))) @staticmethod def jump(position, precents = False): ''' Jump to some position in the current file @param position:int The position either in seconds or precents @param precents:bool Whether to use precents, otherwise seconds @return :Popen The spawned process ''' return MOC.__interact('--jump', '%i%s' % (position, '%' if precents else 's')) def toggle(shuffle = False, repeat = False, autonext = False): ''' Toggle options @param shuffle:bool Whether to toggle shuffle @param repeat:bool Whether to toggle repeat @param autonext:bool Whether to toggle autonext @return :Popen? The spawned process, `None` if nothing is spawned ''' if any([shuffle, repeat, autonext]): opts = [('shuffle', shuffle), ('repeat', repeat), ('autonext', autonext)] opts = map(lambda opt_val : opt_val[0] if opt_val[1] else None, opts) opts = filter(lambda opt : opt is not None, opts) return MOC.__interact('--toggle', ','.join(opts)) return None def on(shuffle = False, repeat = False, autonext = False): ''' Turn on options @param shuffle:bool Whether to turn on shuffle @param repeat:bool Whether to turn on repeat @param autonext:bool Whether to turn on autonext @return :Popen? The spawned process, `None` if nothing is spawned ''' if any([shuffle, repeat, autonext]): opts = [('shuffle', shuffle), ('repeat', repeat), ('autonext', autonext)] opts = map(lambda opt_val : opt_val[0] if opt_val[1] else None, opts) opts = filter(lambda opt : opt is not None, opts) return MOC.__interact('--on', ','.join(opts)) return None def off(shuffle = False, repeat = False, autonext = False): ''' Turn off options @param shuffle:bool Whether to turn off shuffle @param repeat:bool Whether to turn off repeat @param autonext:bool Whether to turn off autonext @return :Popen? The spawned process, `None` if nothing is spawned ''' if any([shuffle, repeat, autonext]): opts = [('shuffle', shuffle), ('repeat', repeat), ('autonext', autonext)] opts = map(lambda opt_val : opt_val[0] if opt_val[1] else None, opts) opts = filter(lambda opt : opt is not None, opts) return MOC.__interact('--off', ','.join(opts)) return None