class MPD(BarItem): def __init__(self, server="localhost",port=6600): self.mpc = MPDClient() self.server = server self.port = port BarItem.__init__(self, "MPD") self.output['name'] = "NowPlaying" self.update() def update(self): try: self.mpc.connect(self.server, self.port) status = self.mpc.status() if (status['state'] == "play"): song = self.mpc.currentsong() self.output['full_text'] = '► ' + song['artist'] + ' - ' + song['album'] + ' - ' + song['title'] elif (status['state'] == "pause"): song = self.mpc.currentsong() self.output['full_text'] = '" ' + song['artist'] + ' - ' + song['album'] + ' - ' + song['title'] else: self.output['full_text'] = status['state'] self.mpc.disconnect() except: self.output['full_text'] = "MPD disconnected"
def main(): if 1 == len(sys.argv): print("You will have to give an argument.", file=sys.stderr) return 1 # If nothing fails. return_value = 0 try: # Connect to the MPD server client = MPDClient() client.connect("localhost", 6600) title_string = parser(client, str(sys.argv[1])) except: # Failure. Probably unable to connect to the MPD server. return_value = 1 title_string = "Could not connect to the MPD server" # print( int( client.status()['time'].split(':')[0] ) ) # If we have to cut dow the string or not if LENGTH >= len(title_string): # Have to append input, if it is shorter than LENGTH len_diff = LENGTH - len(title_string) append_before = (" ") * int((len_diff / 2)) append_after = (" ") * int((len_diff / 2) + 0.5) # Prints and flushes the output print(append_before + title_string + append_after) # print(title_string) sys.stdout.flush() else: # Keep track of where to cut the long title string start = int(client.status()["time"].split(":")[0]) % (len(title_string) + len(DIVIDER)) end = start + LENGTH # Appends to the title_string, so that we can roll-over easily title_string = title_string + DIVIDER + title_string print(title_string[start:end]) sys.stdout.flush() # Closing the connection. client.close() client.disconnect() # Done! return return_value
class OrpheusLibrarySearch(QWidget): def __init__(self): super().__init__() self.initUI() self.populateList() def initUI(self): #palette = QPalette() #palette.setColor(QPalette.Background, QColor('#383C4A')) #palette.setColor(QPalette.WindowText, QColor('#C1C1C1')) #self.setPalette(palette) self.setMaximumSize(492, 653) self.setMinimumSize(492, 653) le = QLineEdit(self) le.textChanged[str].connect(self.onChanged) le.returnPressed.connect(self.onActivation) le.setClearButtonEnabled(True) le.setPlaceholderText('Start typing to search...') self.lw = QListWidget() self.visibleLw = QListWidget() #palette.setColor(QPalette.Base, QColor('#383C4A')) #self.visibleLw.setPalette(palette) self.visibleLw.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.visibleLw.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.visibleLw.itemActivated.connect(self.onActivation) self.scrollBar = QScrollBar() self.visibleLw.verticalScrollBar().valueChanged.connect(self.scrollBar.setValue) self.scrollBar.valueChanged.connect(self.visibleLw.verticalScrollBar().setValue) vbox = QVBoxLayout() vbox.setSpacing(3) #vbox.setContentsMargins(3, 3, 3, 3) vbox.setContentsMargins(0, 4, 0, 0) vbox.addWidget(le) hbox = QHBoxLayout() hbox.setSpacing(0) #hbox.setContentsMargins(0, 0, 0, 0) hbox.addWidget(self.visibleLw) hbox.addWidget(self.scrollBar) vbox.addLayout(hbox) self.setLayout(vbox) self.setWindowTitle('Music Library') icon = QIcon.fromTheme('musique') self.setWindowIcon(icon) def populateList(self): self.client = MPDClient() self.client.connect('localhost', 6600) self.playlistinfo = self.client.playlistinfo() self.client.disconnect() self.playlist = [] #backgroundColor = QColor('#383C4A') #foregroundColor = QColor('#C1C1C1') for i in self.playlistinfo: row = '' if 'album' in i: row = row + i['album'] + ' - ' if 'title' in i: if isinstance(i['title'], str): row = row + i['title'] else: row = row + i['title'][0] if 'artist' in i: row = row + ' - ' + i['artist'] self.playlist.append(row) #newRow = QListWidgetItem(row) #newRow.setBackground(backgroundColor) #newRow.setForeground(foregroundColor) #self.lw.addItem(newRow) self.visibleLw.addItems(self.playlist) self.visibleLw.setCurrentRow(0) def get_matches(self, pattern): self.visibleLw.clear() pattern = '.*' + pattern.replace(' ', '.*').lower() regexp = re.compile(pattern) for i in self.playlist: if regexp.match(i.lower()): self.visibleLw.addItem(i) def formatScrollBar(self): self.scrollBar.setMaximum(self.visibleLw.verticalScrollBar().maximum()) self.scrollBar.setPageStep(self.visibleLw.verticalScrollBar().pageStep()) def onChanged(self, text): self.get_matches(text) self.visibleLw.setCurrentRow(0) self.scrollBar.setMaximum(self.visibleLw.verticalScrollBar().maximum()) if self.visibleLw.verticalScrollBar().maximum() == 0: self.scrollBar.setVisible(False) else: self.scrollBar.setVisible(True) def onActivation(self): selected_song = self.visibleLw.currentItem().text() for i in range(0, len(self.playlist)): if selected_song == self.playlist[i]: self.client.connect('localhost', 6600) self.client.play(i) self.client.disconnect() def keyPressEvent(self, e): if e.key() == Qt.Key_Down: self.visibleLw.setFocus() elif e.key() == Qt.Key_Escape: self.close()
class KissMPD(): def handle_idle(func): def inner(self, *args, **kwargs): self.__lock.acquire() self.__client.noidle() res = func(self, *args, **kwargs) self.__client.send_idle() self.__lock.release() return res return inner def __init__(self, fp): self.__fp = fp self.__client = MPDClient() self.__lock = threading.Lock() self.__idle_client = MPDClient() def connect(self): self.__client.connect('localhost', 6600) self.__idle_client.connect('localhost', 6600) status = self.__client.status() self.__state = status['state'] self.__volume = int(status['volume']) try: self.__songid = status['songid'] except: self.__songid = None self.__client.send_idle() self.__idle_client.send_idle() def disconnect(self): self.__client.disconnect() self.__idle_client.disconnect() def process(self): canRead = select([self.__idle_client], [], [], 0)[0] if canRead: changes = self.__idle_client.fetch_idle() self.__idle_client.send_idle() # continue idling change = self.update_status() return change return None @handle_idle def refresh(self): self.display_current_song() def display_current_song(self): if (self.__songid): song = self.__client.currentsong() try: text = song['artist'] + ' - ' + song['title'] except: text = song['file'].split('/')[-1] self.__fp.set_scrolling_text(3, 1, text + ' ') try: time = divmod(int(song['time']), 60) self.__fp.set_track_clock('0{0[0]:02}{0[1]:02}'.format(time)) except: self.__fp.set_track_clock(None) else: self.__fp.clear() def display_volume(self): self.__fp.set_flash_text(8, 'Volume: ' + str(self.__volume)) def display_state(self): if (self.__state != 'play'): self.__fp.clear() self.__fp.set_text('{0:^12}'.format(self.__state)) else: self.display_current_song() @handle_idle def update_status(self): status = self.__client.status() try: songid = status['songid'] except: songid = None if (songid != self.__songid): self.__songid = songid self.display_current_song() if (int(status['volume']) != self.__volume): self.__volume = int(status['volume']) self.display_volume() state = status['state'] if (state != self.__state): self.__state = state self.display_state() return state return None @handle_idle def volume_down(self): self.__volume = self.__volume - 1 if (self.__volume < 0): self.__volume = 0 self.__client.setvol(self.__volume) self.display_volume() @handle_idle def volume_up(self): self.__volume = self.__volume + 1 if (self.__volume > 100): self.__volume = 100 self.__client.setvol(self.__volume) self.display_volume() @handle_idle def play(self, toggle=False): if (self.__state == 'play'): if toggle: self.__client.pause() else: self.__client.play() def play_pause(self): self.play(True) @handle_idle def pause(self): self.__client.pause(1) @handle_idle def previous(self): self.__client.previous() @handle_idle def next(self): self.__client.next() @handle_idle def stop(self): self.__client.stop() def handle_inputevent(self, ie): key2function = { 'volume_down': self.volume_down, 'volume_up': self.volume_up, 'stop': self.stop, 'play': self.play_pause, 'next_track': self.next, 'previous_track': self.previous, 'right': self.next, 'left': self.previous, 'down': self.volume_down, 'up': self.volume_up, 'ok': self.play_pause, 'mute': self.play_pause, } try: function = key2function[ie.key] except: function = None if not function: return False if (ie.type == InputEventType.hold): if (function == self.volume_down) or (function == self.volume_up): function() elif (ie.type == InputEventType.pressed): function() return True
def main(): # If nothing fails. return_value = 0 try: # Connect to the MPD server client = MPDClient() client.connect("localhost", 6600) # Runs the script until a new songs comes on currentTrack = client.status()['songid'] # If there is no argument, we will use "artist - title" if 1 == len(sys.argv): title_string = parser(client, "%artist% - %title%") else: title_string = parser(client, str(sys.argv[1])) except: # Failure. Probably unable to connect to the MPD server. return_value = 1 title_string = "Could not connect to the MPD server" # If we have to cut dow the string or not if LENGTH >= len(title_string): # Have to append input, if it is shorter than LENGTH len_diff = LENGTH - len(title_string) append_before = (' ') * int((len_diff/2)) append_after = (' ') * int((len_diff/2) + 0.5) # Prints and flushes the output print(append_before + title_string + append_after) sys.stdout.flush() # Now we only have to wait. #while client.status()['songid'] == currentTrack: #time.sleep(WAIT_TIME) else: # Appends to the title_string, so that we can roll-over easily title_string = title_string + DIVIDER + title_string[0:LENGTH] # Keep track of where to cut the long title string start = 0 end = LENGTH try: # While this song is playing while currentTrack == client.status()['songid']: # Print and flush print( title_string[start:end] ) sys.stdout.flush() # Wait a little bit time.sleep(WAIT_TIME) # Step through the string start +=1 end +=1 # We have are one lap through, so resert the steps if title_string[start:end] == title_string[0:LENGTH]: start = 0 end = LENGTH except: # Something failed. return_value = 1 # Closing the connection. client.close() client.disconnect() # Done! return return_value
class PlayerClient(Player): """MPD Client From python-musicpd: _fetch_nothing … _fetch_item single str _fetch_object single dict _fetch_list list of str _fetch_playlist list of str _fetch_changes list of dict _fetch_database list of dict _fetch_songs list of dict, especially tracks _fetch_plugins, TODO: handle exception in command not going through _client_wrapper() (ie. remove…) """ database = None # sima database (history, blacklist) def __init__(self, host="localhost", port="6600", password=None): super().__init__() self._comm = self._args = None self._mpd = host, port, password self._client = MPDClient() self._client.iterate = True self._cache = None def __getattr__(self, attr): command = attr wrapper = self._execute return lambda *args: wrapper(command, args) def _execute(self, command, args): self._write_command(command, args) return self._client_wrapper() def _write_command(self, command, args=None): self._comm = command self._args = list() for arg in args: self._args.append(arg) def _client_wrapper(self): func = self._client.__getattr__(self._comm) try: ans = func(*self._args) # WARNING: MPDError is an ancestor class of # CommandError except CommandError as err: raise PlayerCommandError('MPD command error: %s' % err) except (MPDError, IOError) as err: raise PlayerError(err) return self._track_format(ans) def _track_format(self, ans): """ unicode_obj = ["idle", "listplaylist", "list", "sticker list", "commands", "notcommands", "tagtypes", "urlhandlers",] """ # TODO: ain't working for "sticker find" and "sticker list" tracks_listing = ["playlistfind", "playlistid", "playlistinfo", "playlistsearch", "plchanges", "listplaylistinfo", "find", "search", "sticker find",] track_obj = ['currentsong'] if self._comm in tracks_listing + track_obj: if isinstance(ans, list): return [Track(**track) for track in ans] elif isinstance(ans, dict): return Track(**ans) return ans def __skipped_track(self, old_curr): if (self.state == 'stop' or not hasattr(old_curr, 'id') or not hasattr(self.current, 'id')): return False return self.current.id != old_curr.id # pylint: disable=no-member def _flush_cache(self): """ Both flushes and instantiates _cache """ if isinstance(self._cache, dict): self.log.info('Player: Flushing cache!') else: self.log.info('Player: Initialising cache!') self._cache = {'artists': frozenset(), 'nombid_artists': frozenset(),} self._cache['artists'] = frozenset(self._execute('list', ['artist'])) if Artist.use_mbid: self._cache['nombid_artists'] = frozenset(self._execute('list', ['artist', 'musicbrainz_artistid', ''])) @blacklist(track=True) def find_track(self, artist, title=None): tracks = set() if artist.mbid: if title: tracks |= set(self.find('musicbrainz_artistid', artist.mbid, 'title', title)) else: tracks |= set(self.find('musicbrainz_artistid', artist.mbid)) else: for name in artist.names: if title: tracks |= set(self.find('artist', name, 'title', title)) else: tracks |= set(self.find('artist', name)) return list(tracks) @bl_artist def search_artist(self, artist): """ Search artists based on a fuzzy search in the media library >>> art = Artist(name='the beatles', mbid=<UUID4>) # mbid optional >>> bea = player.search_artist(art) >>> print(bea.names) >>> ['The Beatles', 'Beatles', 'the beatles'] Returns an Artist object """ found = False if artist.mbid: # look for exact search w/ musicbrainz_artistid exact_m = self._execute('list', ['artist', 'musicbrainz_artistid', artist.mbid]) if exact_m: _ = [artist.add_alias(name) for name in exact_m] found = True else: artist = Artist(name=artist.name) # then complete with fuzzy search on artist with no musicbrainz_artistid if artist.mbid: # we already performed a lookup on artists with mbid set # search through remaining artists artists = self._cache.get('nombid_artists') else: artists = self._cache.get('artists') match = get_close_matches(artist.name, artists, 50, 0.73) if not match and not found: return if len(match) > 1: self.log.debug('found close match for "%s": %s', artist, '/'.join(match)) # Does not perform fuzzy matching on short and single word strings # Only lowercased comparison if ' ' not in artist.name and len(artist.name) < 8: for fuzz_art in match: # Regular lowered string comparison if artist.name.lower() == fuzz_art.lower(): artist.add_alias(fuzz_art) return artist fzartist = SimaStr(artist.name) for fuzz_art in match: # Regular lowered string comparison if artist.name.lower() == fuzz_art.lower(): found = True artist.add_alias(fuzz_art) if artist.name != fuzz_art: self.log.debug('"%s" matches "%s".', fuzz_art, artist) continue # SimaStr string __eq__ (not regular string comparison here) if fzartist == fuzz_art: found = True artist.add_alias(fuzz_art) self.log.info('"%s" quite probably matches "%s" (SimaStr)', fuzz_art, artist) if found: if artist.aliases: self.log.debug('Found: %s', '/'.join(list(artist.names)[:4])) return artist def fuzzy_find_track(self, artist, title): # Retrieve all tracks from artist all_tracks = self.find_track(artist, title) # Get all titles (filter missing titles set to 'None') all_artist_titles = frozenset([tr.title for tr in all_tracks if tr.title is not None]) match = get_close_matches(title, all_artist_titles, 50, 0.78) if not match: return [] for title_ in match: leven = levenshtein_ratio(title.lower(), title_.lower()) if leven == 1: pass elif leven >= 0.79: # PARAM self.log.debug('title: "%s" should match "%s" (lr=%1.3f)', title_, title, leven) else: self.log.debug('title: "%s" does not match "%s" (lr=%1.3f)', title_, title, leven) return [] return self.find('artist', artist, 'title', title_) def find_album(self, artist, album): """ Special wrapper around album search: Album lookup is made through AlbumArtist/Album instead of Artist/Album """ alb_art_search = self.find('albumartist', artist, 'album', album) if alb_art_search: return alb_art_search return self.find('artist', artist, 'album', album) @blacklist(album=True) def search_albums(self, artist): """ Fetch all albums for "AlbumArtist" == artist Filter albums returned for "artist" == artist since MPD returns any album containing at least a single track for artist """ albums = [] for name in artist.names: if len(artist.names) > 1: self.log.debug('Searching album for aliase: "%s"', name) kwalbart = {'albumartist':name, 'artist':name} for album in self.list('album', 'albumartist', artist): if album and album not in albums: albums.append(Album(name=album, **kwalbart)) for album in self.list('album', 'artist', artist): album_trks = [trk for trk in self.find('album', album)] if 'Various Artists' in [tr.albumartist for tr in album_trks]: self.log.debug('Discarding %s ("Various Artists" set)', album) continue arts = set([trk.artist for trk in album_trks]) if len(set(arts)) < 2: # TODO: better heuristic, use a ratio instead if album not in albums: albums.append(Album(name=album, **kwalbart)) elif album and album not in albums: self.log.debug('"{0}" probably not an album of "{1}"'.format( album, artist) + '({0})'.format('/'.join(arts))) return albums def monitor(self): curr = self.current try: self.send_idle('database', 'playlist', 'player', 'options') select([self._client], [], [], 60) ret = self.fetch_idle() if self.__skipped_track(curr): ret.append('skipped') if 'database' in ret: self._flush_cache() return ret except (MPDError, IOError) as err: raise PlayerError("Couldn't init idle: %s" % err) def clean(self): """Clean blocking event (idle) and pending commands """ if 'idle' in self._client._pending: self._client.noidle() elif self._client._pending: self.log.warning('pending commands: %s', self._client._pending) def remove(self, position=0): self.delete(position) def add(self, track): """Overriding MPD's add method to accept add signature with a Track object""" self._execute('add', [track.file]) @property def artists(self): return self._cache.get('artists') @property def state(self): return str(self.status().get('state')) @property def current(self): return self.currentsong() @property def queue(self): plst = self.playlist plst.reverse() return [trk for trk in plst if int(trk.pos) > int(self.current.pos)] @property def playlist(self): """ Override deprecated MPD playlist command """ return self.playlistinfo() def connect(self): host, port, password = self._mpd self.disconnect() try: self._client.connect(host, port) # Catch socket errors except IOError as err: raise PlayerError('Could not connect to "%s:%s": %s' % (host, port, err.strerror)) # Catch all other possible errors # ConnectionError and ProtocolError are always fatal. Others may not # be, but we don't know how to handle them here, so treat them as if # they are instead of ignoring them. except MPDError as err: raise PlayerError('Could not connect to "%s:%s": %s' % (host, port, err)) if password: try: self._client.password(password) except (MPDError, IOError) as err: raise PlayerError("Could not connect to '%s': %s", (host, err)) # Controls we have sufficient rights needed_cmds = ['status', 'stats', 'add', 'find', \ 'search', 'currentsong', 'ping'] available_cmd = self._client.commands() for nddcmd in needed_cmds: if nddcmd not in available_cmd: self.disconnect() raise PlayerError('Could connect to "%s", ' 'but command "%s" not available' % (host, nddcmd)) # Controls use of MusicBrainzIdentifier if Artist.use_mbid: if 'MUSICBRAINZ_ARTISTID' not in self._client.tagtypes(): self.log.warning('Use of MusicBrainzIdentifier is set but MPD is ' 'not providing related metadata') self.log.info(self._client.tagtypes()) self.log.warning('Disabling MusicBrainzIdentifier') Artist.use_mbid = False else: self.log.trace('Available metadata: %s', self._client.tagtypes()) # pylint: disable=no-member else: self.log.warning('Use of MusicBrainzIdentifier disabled!') self.log.info('Consider using MusicBrainzIdentifier for your music library') self._flush_cache() def disconnect(self): # Try to tell MPD we're closing the connection first try: self._client.noidle() self._client.close() # If that fails, don't worry, just ignore it and disconnect except (MPDError, IOError): pass try: self._client.disconnect() # Disconnecting failed, so use a new client object instead # This should never happen. If it does, something is seriously broken, # and the client object shouldn't be trusted to be re-used. except (MPDError, IOError): self._client = MPDClient()
class KissMPD(): def handle_idle(func): def inner(self, *args, **kwargs): self.__lock.acquire() self.__client.noidle() res = func(self, *args, **kwargs) self.__client.send_idle() self.__lock.release() return res return inner def __init__(self, fp): self.__fp = fp self.__client = MPDClient() self.__lock = threading.Lock() self.__idle_client = MPDClient() def connect(self): self.__client.connect('localhost', 6600) self.__idle_client.connect('localhost', 6600) status = self.__client.status() self.__state = status['state'] self.__volume = int(status['volume']) try: self.__songid = status['songid'] except: self.__songid = None self.__client.send_idle() self.__idle_client.send_idle() def disconnect(self): self.__client.disconnect() self.__idle_client.disconnect() def process(self): canRead = select([self.__idle_client], [], [], 0)[0] if canRead: changes = self.__idle_client.fetch_idle() self.__idle_client.send_idle() # continue idling change = self.update_status() return change return None @handle_idle def refresh(self): self.display_current_song() def display_current_song(self): if(self.__songid): song = self.__client.currentsong() try: text = song['artist'] + ' - ' + song['title'] except: text = song['file'].split('/')[-1] self.__fp.set_scrolling_text(3, 1, text) try: time = divmod(int(song['time']), 60) self.__fp.set_track_clock('0{0[0]:02}{0[1]:02}'.format(time)) except: self.__fp.set_track_clock(None) else: self.__fp.clear() def display_volume(self): self.__fp.set_flash_text(8, 'Volume: ' + str(self.__volume)) def display_state(self): if(self.__state != 'play'): self.__fp.clear() self.__fp.set_text('{0:^12}'.format(self.__state)) else: self.display_current_song() @handle_idle def update_status(self): status = self.__client.status() try: songid = status['songid'] except: songid = None if(songid != self.__songid): self.__songid = songid self.display_current_song() if(int(status['volume']) != self.__volume): self.__volume = int(status['volume']) self.display_volume() state = status['state'] if(state != self.__state): self.__state = state self.display_state() return state return None @handle_idle def volume_down(self): self.__volume = self.__volume - 1 if(self.__volume < 0): self.__volume = 0 self.__client.setvol(self.__volume) self.display_volume() @handle_idle def volume_up(self): self.__volume = self.__volume + 1 if(self.__volume > 100): self.__volume = 100 self.__client.setvol(self.__volume) self.display_volume() @handle_idle def play(self, toggle=False): if(self.__state == 'play'): if toggle: self.__client.pause() else: self.__client.play() def play_pause(self): self.play(True) @handle_idle def pause(self): self.__client.pause(1) @handle_idle def previous(self): self.__client.previous() @handle_idle def next(self): self.__client.next() @handle_idle def stop(self): self.__client.stop() def handle_inputevent(self, ie): key2function = { 'volume_down' : self.volume_down, 'volume_up' : self.volume_up, 'stop' : self.stop, 'play' : self.play_pause, 'next_track' : self.next, 'previous_track' : self.previous, 'right' : self.next, 'left' : self.previous, 'down' : self.volume_down, 'up' : self.volume_up, 'ok' : self.play_pause, 'mute' : self.play_pause, } try: function = key2function[ie.key] except: function = None if not function: return False if(ie.type == InputEventType.hold): if(function == self.volume_down) or (function == self.volume_up): function() elif(ie.type == InputEventType.pressed): function() return True