def p_expression_filter(p): 'expression : FILTER' exact = True if p[1][0].isupper() else False alias = (p[1][0] if p[1][1] in '"\'' else p[1][0:2]).lower() name = filters_alias.get(alias, '') pattern = p[1][2:-1] if p[1][1] in '"\'' else p[1][3:-1] if not name: warning('Filter [{}] does not exist'.format(alias)) sys.exit(0) if name == 'lastfm_a': p[0] = OrderedSet() if exact: artists = lastfm.find_artists(pattern) else: artists = lastfm.search_artists(pattern) for artist in artists: p[0] |= mpd.find('artist', artist) elif name == 'lastfm_b': p[0] = OrderedSet() if exact: albums = lastfm.find_albums(pattern) else: albums = lastfm.search_albums(pattern) for album, artist in albums: matched_songs = mpd.find_multiple(albumartist=artist, album=album) if not matched_songs: matched_songs = mpd.find_multiple(artist=artist, album=album) p[0] |= matched_songs p[0] = mpd.set_sort(p[0]) elif exact: p[0] = OrderedSet(mpd.find(name, pattern)) else: p[0] = OrderedSet(mpd.search(name, pattern))
def main(): config = ConfigParser() config.add_section('mpd') config['mpd']['host'] = input('>> MPD host [localhost]: ') or 'localhost' config['mpd']['port'] = input('>> MPD port [6600]: ') or '6600' config['mpd']['password'] = input('>> MPD password []: ') or '' print('\n') config.add_section('mpdc') print('Later, you will propably need to store and edit your collections/' 'playlists in a specific file. Please create an empty file ' '(e.g. collections.mpdc) where you want and write its path below.') while True: path = input('>> Full path of the collections file: ') if not os.path.isfile(path): warning('Can\'t find the file: ' + path) else: break print('\n') config['mpdc']['collections'] = path colors = input('>> Enable colors [Y/n]: ').lower() or 'y' if colors == 'y': config['mpdc']['colors'] = 'red, green, blue' print('\n') filepath = os.path.expanduser('~/.mpdc') try: with open(filepath, 'w') as configfile: config.write(configfile) info('Writing configuration file in: ' + filepath) except IOError: warning('Can\'t write configuration file in: ' + filepath)
def check(args): songs = [] song_tags_dict = mpd.get_all_songs_tags() for song in parser.parse(' '.join(args.collection)): tags = song_tags_dict[song] missing_tags = [tag for tag, value in tags.items() if not value] if missing_tags: warning(colorize(song, colors[0])) print('missing tag(s): ' + colorize(', '.join(missing_tags), colors[1 % len(colors)])) else: songs.append(tuple(sorted(tags.items()))) duplicates = [dict(tags) for tags, nb in Counter(songs).items() if nb > 1] if duplicates: print('\nConflict(s) found:') print('------------------') for tags in duplicates: warning('Conflict with tags ' + colorize(repr_tags([tags['artist'], tags['album'], tags['title'], tags['track']]), colors[1 % len(colors)])) # necessary because MPDHelper's get_all_songs_tags falls back # to artist if albumartist is empty, while it's find_multiple # (mpdclient.find in fact) does not # not a solution really, so FIXME tags.pop('albumartist') files_matched = mpd.find_multiple(**tags) print('files matched:\n' + colorize('\n'.join(files_matched), colors[0]))
def __init__(self): self.timeout = 0 self.artists_tags = cache.read('lastfm_artists_tags') self.albums_tags = cache.read('lastfm_albums_tags') self.tracks_tags = cache.read('lastfm_tracks_tags') if not (self.artists_tags and self.albums_tags and self.tracks_tags): warning('You should update the LastFM database')
def remove_songs(self, alias, songs_files): if alias in self.collections and 'songs' in self.collections[alias]: remaining_songs = [s for s in self.collections[alias]['songs'] if s not in songs_files] if 'mpd_playlist' in self.collections[alias]: mpd.clear_stored_playlist(alias) mpd.add_songs_stored_playlist(alias, remaining_songs) self.collections[alias]['songs'] = remaining_songs self.need_update = True else: warning('Collection [%s] doesn\'t exist or contains no song to ' 'remove' % alias)
def change_default_profile(profile): config = ConfigParser() filepath = os.path.expanduser('~/.mpdc') if not config.read(filepath): warning('Cannot read the configuration file, run mpdc-configure') return config['profiles']['default'] = str(profile) try: with open(filepath, 'w') as configfile: config.write(configfile) info('Writing configuration file in: ' + filepath) except IOError: warning('Cannot write configuration file in: ' + filepath)
def get_album_tags(self, album, artist, update=False): if not update: if not self.albums_tags: warning('You should update the LastFM database') elif (album, artist) in self.albums_tags: return self.albums_tags[(album, artist)] return {} else: data = self.request('album_tags', artist=artist, album=album) if data is not None: if 'tag' in data.get('toptags', {}): return self.sanitize_tags(data['toptags']['tag']) return {}
def get_similar_artists(self, query): if not self.artists_tags: warning('You should update the LastFM database') return scores = {} for artist, tags in self.artists_tags.items(): if tags: score = similarity(tags, query) if score > LastfmHelper.min_similarity: scores[artist] = score scores_desc = sorted(scores.items(), key=itemgetter(1), reverse=True) for artist in scores_desc: yield artist
def p_expression_collection(p): 'expression : COLLECTION' p[0] = OrderedSet() if p[1] in collectionsmanager.c: collection = collectionsmanager.c[p[1]] if 'expression' in collection: p[0] |= parser.parse(collection['expression'], lexer=lex.lex(debug=0, reflags=re.UNICODE|re.IGNORECASE)) if 'songs' in collection: p[0] |= OrderedSet(collection['songs']) if enable_command and 'command' in collection: try: output = subprocess.check_output(collection['command'], shell=True) p[0] |= OrderedSet(format_mpc_output(output.decode())) except subprocess.CalledProcessError: warning('Error while executing `command` in collection [{}]'. format(p[1])) sys.exit(0) if 'sort' in collection: p[0] = mpd.set_sort(p[0]) elif p[1] == 'all': p[0] = OrderedSet(mpd.get_all_songs()) elif p[1] == 'c': p[0] = OrderedSet(mpd.get_playlist_songs()) elif p[1] == 'C': c_song = mpd.get_current_song() if c_song is not None: p[0] = OrderedSet([c_song]) elif p[1] == 'A': c_song = mpd.get_current_song() if c_song is not None: p[0] = OrderedSet(mpd.find('artist', mpd.get_tag(c_song, 'artist'))) elif p[1] == 'B': c_song = mpd.get_current_song() if c_song is not None: p[0] = OrderedSet(mpd.find_multiple( albumartist=mpd.get_tag(c_song, 'albumartist'), album=mpd.get_tag(c_song, 'album'))) if not p[0]: p[0] = OrderedSet(mpd.find_multiple( artist=mpd.get_tag(c_song, 'artist'), album=mpd.get_tag(c_song, 'album'))) else: warning('Collection [{}] does not exist'.format(p[1])) sys.exit(0)
def show(args): if args.alias in collections: if 'mpd_playlist' in collections[args.alias]: info('This collection is stored as a MPD playlist\n') if 'sort' in collections[args.alias]: info('This collection is sorted automatically\n') if 'expression' in collections[args.alias]: print(collections[args.alias]['expression']) if 'command' in collections[args.alias]: print('command: ' + collections[args.alias]['command']) print('--------\n') if 'songs' in collections[args.alias]: print('songs:') print('------') display_songs(collections[args.alias]['songs'], args.m) else: warning('Stored collection [%s] doesn\'t exist' % args.alias)
def raw_to_optimized(collections_raw): collections = OrderedDict() alias = '' for line in collections_raw: if line.startswith('--'): alias = (line[2:] if line[2] not in '@#' else line[3:]).strip() collections[alias] = {} if line[2] == '@': collections[alias]['sort'] = True elif line[2] == '#': collections[alias]['special'] = True elif alias: if line.startswith('command:'): collections[alias]['command'] = line[8:].strip() elif line.startswith('songs:'): collections[alias]['songs'] = [] elif line.strip(): if ('songs' in collections[alias] and (line.startswith(' ') or line.startswith('\t'))): tags = ast.literal_eval('({})'.format(line.strip())) artist, album, title, track = tags matched_songs = mpd.find_multiple(artist=artist, album=album, title=title, track=track) if matched_songs: collections[alias]['songs'].append(matched_songs[0]) else: warning('In collection [{}], these tags do not match ' 'any song: {}'.format(alias, repr_tags(tags))) else: if 'expression' not in collections[alias]: collections[alias]['expression'] = line else: collections[alias]['expression'] += line # add MPD native playlists for playlist in mpd.get_stored_playlists(): if playlist not in collections: collections[playlist] = {'mpd_playlist': True, 'songs': mpd.get_stored_playlist_songs(playlist)} else: warning('MPD playlist [{}] was ignored because a collection with ' 'the same name already exists'.format(playlist)) return collections
def add_songs(self, alias, songs_files): if not alias in self.c or not 'mpd_playlist' in self.c[alias]: for song in songs_files[:]: if not all(mpd.get_tags(song)): warning('[{}] was not added (missing tags)'.format(song)) songs_files.remove(song) if alias in self.c: if 'songs' in self.c[alias]: self.collections[alias]['songs'].extend(songs_files) else: self.collections[alias]['songs'] = songs_files if 'mpd_playlist' in self.c[alias]: mpd.add_songs_stored_playlist(alias, songs_files) else: info('Collection [{}] will be created'.format(alias)) self.collections[alias] = {} self.collections[alias]['songs'] = songs_files self.need_update = True
def add_songs(self, alias, songs_files): if (not alias in self.collections or not 'mpd_playlist' in self.collections[alias]): for song in songs_files[:]: if not all(mpd.get_tags(song)): warning('File not added, missing tag(s): [%s]' % song) songs_files.remove(song) if alias in self.collections: if 'songs' in self.collections[alias]: self.collections[alias]['songs'].extend(songs_files) else: self.collections[alias]['songs'] = songs_files if 'mpd_playlist' in self.collections[alias]: mpd.add_songs_stored_playlist(alias, songs_files) else: info('Collection [%s] will be created' % alias) self.collections[alias] = {} self.collections[alias]['songs'] = songs_files self.need_update = True
def show(args): if args.alias in collectionsmanager.c: collection = collectionsmanager.c[args.alias] if "mpd_playlist" in collection: info("This collection is stored as a MPD playlist\n") elif "sort" in collection: info("This collection is sorted automatically\n") elif "special" in collection: info("This is a special collection\n") if "expression" in collection: print(collection["expression"]) if "command" in collection: print("command: " + collection["command"]) print("--------\n") if "songs" in collection: print("songs:") print("------") display_songs(collection["songs"], args.f) else: warning("Stored collection [{}] does not exist".format(args.alias))
def show(args): if args.alias in collectionsmanager.c: collection = collectionsmanager.c[args.alias] if 'mpd_playlist' in collection: info('This collection is stored as a MPD playlist\n') elif 'sort' in collection: info('This collection is sorted automatically\n') elif 'special' in collection: info('This is a special collection\n') if 'expression' in collection: print(collection['expression']) if 'command' in collection: print('command: ' + collection['command']) print('--------\n') if 'songs' in collection: print('songs:') print('------') display_songs(collection['songs'], args.f) else: warning('Stored collection [{}] does not exist'.format(args.alias))
def request(self, method, **args): while LastfmHelper.last_request + LastfmHelper.delay > datetime.now(): sleep(0.1) args_ = {key: quote(value) for (key, value) in args.items()} url = LastfmHelper.url + LastfmHelper.methods[method].format(**args_) try: raw_json = urlopen(url, timeout=15).read() except (socket.timeout, URLError): if self.timeout == 3: warning('Can\'t send the request after 4 attempts') self.timeout = 0 return None self.timeout += 1 warning('Time out... attempt n°' + str(self.timeout + 1)) return self.request(method, **args) else: LastfmHelper.last_request = datetime.now() response = json.loads(raw_json.decode('utf-8')) if 'error' in response: warning('LastFM error: {}'.format(response['message'])) return None self.timeout = 0 return response
def request(self, method, **args): if self.last_request + self.delay > datetime.now(): time_to_sleep = self.last_request + self.delay - datetime.now() time.sleep(time_to_sleep.total_seconds()) args_ = {key: urllib.parse.quote(value) for key, value in args.items()} url = self.url + self.methods[method].format(**args_) try: raw_json = urllib.request.urlopen(url, timeout=15).read() except (socket.timeout, urllib.error.URLError): if self.timeout == 3: warning('Cannot send the request after 4 attempts') self.timeout = 0 return None self.timeout += 1 warning('Time out... attempt n°' + str(self.timeout + 1)) return self.request(method, **args) else: self.last_request = datetime.now() response = json.loads(raw_json.decode('utf-8')) if 'error' in response: warning('LastFM error: {}'.format(response['message'])) return None self.timeout = 0 return response
def configure(args): if args.switch: change_default_profile(args.switch) return config = ConfigParser() config.add_section('profiles') config['profiles']['host[1]'] = input('>> MPD host [localhost]: ') or \ 'localhost' config['profiles']['port[1]'] = input('>> MPD port [6600]: ') or '6600' config['profiles']['password[1]'] = input('>> MPD password []: ') or '' print('\n') config.add_section('mpdc') print('Later, you will propably need to store and edit your collections/' 'playlists in a specific file. Please create an empty file ' '(e.g. collections.mpdc) where you want and write its path below.') while True: path = input('>> Full path of the collections file: ') if os.path.isfile(path): break warning('Cannot find the file: ' + path) print('\n') config['mpdc']['collections'] = path colors = input('>> Enable colors [Y/n]: ').lower() or 'y' if colors == 'y': config['mpdc']['colors'] = 'green, red, blue' print('\n') config['mpdc']['columns'] = 'artist, title, album' filepath = os.path.expanduser('~/.mpdc') try: with open(filepath, 'w') as configfile: config.write(configfile) info('Writing configuration file in: ' + filepath) except IOError: warning('Cannot write configuration file in: ' + filepath)
def p_expression_collection(p): 'expression : COLLECTION' if p[1] in collections: collection = collections[p[1]] p[0] = OrderedSet() if 'expression' in collection: p[0] |= p.parser.parse(collection['expression'], lexer=lex.lex(debug=0, reflags=re.UNICODE)) if 'songs' in collection: p[0] |= OrderedSet(collection['songs']) if enable_command and 'command' in collection: try: output = check_output(collection['command'], shell=True) p[0] |= OrderedSet(format_mpc_output(output.decode())) except CalledProcessError: warning('Error while executing `command` in collection [%s]' % p[1]) sys.exit(0) if 'sort' in collection: p[0] = mpd.set_sort(p[0]) elif p[1] == 'all': p[0] = OrderedSet(mpd.get_all_songs()) elif p[1] == 'c': p[0] = OrderedSet(mpd.get_playlist_songs()) elif p[1] == 'C': p[0] = OrderedSet([mpd.get_current_song()]) elif p[1] == 'A': c_song = mpd.get_current_song() p[0] = OrderedSet(mpd.find('artist', mpd.get_tag(c_song, 'artist'))) elif p[1] == 'B': c_song = mpd.get_current_song() p[0] = OrderedSet(mpd.find_multiple( artist=mpd.get_tag(c_song, 'artist'), album=mpd.get_tag(c_song, 'album'))) else: warning('Collection [%s] doesn\'t exist' % p[1]) sys.exit(0)
def check(args): songs = [] for song, tags in mpd.get_all_songs_tags().items(): missing_tags = [tag for tag, value in tags.items() if not value] if missing_tags: warning('You should tag [%s]' % colorize(song, colors[0])) print('missing tag(s): %s' % colorize(', '.join(missing_tags), colors[1])) else: songs.append(tuple(sorted(tags.items()))) duplicates = [dict(tags) for tags, nb in Counter(songs).items() if nb > 1] if duplicates: print('\nConflict(s) found:') print('------------------') for tags in duplicates: warning('Conflict with tags ' + colorize(repr_tags([tags['artist'], tags['album'], tags['title'], tags['track']]), colors[1])) files_matched = mpd.find_multiple(**tags) print('files matched: \n%s\n' % colorize('\n'.join(files_matched), colors[0]))
def p_expression_modifier(p): 'expression : expression MODIFIER' modifier = (p[2][1:]).lstrip() # Sorting modifier if modifier == 's': p[0] = mpd.set_sort(p[1]) # N-random songs modifier elif re.match(r'^r[0-9]+$', modifier): p[1] = exclude_songs(p[1]) try: p[0] = OrderedSet(random.sample(p[1], int(modifier[1:]))) except ValueError: p[0] = p[1] # N-random artists modifier elif re.match(r'^ra[0-9]+$', modifier): p[1] = exclude_songs(p[1]) artists = OrderedSet() for song in p[1]: artists.add(mpd.get_tag(song, 'artist')) try: r_artists = OrderedSet(random.sample(artists, int(modifier[2:]))) except ValueError: p[0] = p[1] else: songs = [] for artist in r_artists: songs.extend(mpd.find('artist', artist)) p[0] = OrderedSet([song for song in p[1] if song in songs]) # N-random albums modifier elif re.match(r'^rb[0-9]+$', modifier): p[1] = exclude_songs(p[1]) albums = OrderedSet() for song in p[1]: albums.add(mpd.get_tags(song, ('album', 'albumartist'))) try: r_albums = OrderedSet(random.sample(albums, int(modifier[2:]))) except ValueError: p[0] = p[1] else: songs = [] for album, artist in r_albums: matched_songs = mpd.find_multiple(album=album, albumartist=artist) if not matched_songs: matched_songs = mpd.find_multiple(album=album, artist=artist) songs.extend(matched_songs) p[0] = OrderedSet([song for song in p[1] if song in songs]) # N-minutes-long modifier elif re.match(r'^d[0-9]+$', modifier): p[1] = exclude_songs(p[1]) total_duration = int(modifier[1:]) * 60 d = 0 p[0] = OrderedSet() p[1] = list(p[1]) random.shuffle(p[1]) for song in p[1]: if d < total_duration: p[0].add(song) d += int(mpd.get_tag(song, 'time')) else: break # N-similar artists modifier elif re.match(r'^i?sa[0-9]+$', modifier): include = True if modifier[0] == 'i' else False limit = int(modifier[3:] if include else modifier[2:]) w_tags = collections.defaultdict(int) for song in p[1]: tags = lastfm.get_artist_tags(mpd.get_tag(song, 'artist')) for tag, w in tags.items(): w_tags[tag] += w if not w_tags: p[0] = p[1] if include else OrderedSet() else: songs = [] for artist, _ in lastfm.get_similar_artists(w_tags): if not limit: break matched_songs = mpd.find('artist', artist) if not include: matched_songs = OrderedSet(matched_songs) - p[1] if matched_songs: songs.extend(matched_songs) limit -= 1 p[0] = OrderedSet(songs) # N-similar albums modifier elif re.match(r'^i?sb[0-9]+$', modifier): include = True if modifier[0] == 'i' else False limit = int(modifier[3:] if include else modifier[2:]) w_tags = collections.defaultdict(int) for song in p[1]: tags = lastfm.get_album_tags(mpd.get_tag(song, 'album'), mpd.get_tag(song, 'albumartist')) for tag, w in tags.items(): w_tags[tag] += w if not w_tags: p[0] = p[1] if include else OrderedSet() else: songs = [] for (album, artist), score in lastfm.get_similar_albums(w_tags): if not limit: break matched_songs = mpd.find_multiple(album=album, albumartist=artist) if not matched_songs: matched_songs = mpd.find_multiple(album=album, artist=artist) if not include: matched_songs = OrderedSet(matched_songs) - p[1] if matched_songs: songs.extend(matched_songs) limit -= 1 p[0] = OrderedSet(songs) # N-top tracks modifier elif re.match(r'^p[0-9]+$', modifier): p[0] = OrderedSet() artists = OrderedSet(mpd.get_tag(song, 'artist') for song in p[1]) for artist in artists: limit = int(modifier[1:]) for track in lastfm.get_artist_top_tracks(artist): if not limit: break matched_songs = mpd.search_multiple(artist=artist, title=track) if len(matched_songs) > 1: matched_songs = mpd.find_multiple(artist=artist, title=track) \ or matched_songs if matched_songs: p[0].add(matched_songs[0]) limit -= 1 else: warning('Modifier [{}] does not exist'.format(modifier)) sys.exit(0)
def t_error(t): warning('Illegal character `{}`'.format(t.value[0])) sys.exit(0)
def p_expression_modifier(p): 'expression : expression MODIFIER' modifier = (p[2][1:]).lstrip() # Sorting modifier if modifier == 's': p[0] = mpd.set_sort(p[1]) # N-random songs modifier elif re.match(r'^r[0-9]+$', modifier): try: p[0] = OrderedSet(random.sample(p[1], int(modifier[1:]))) except ValueError: p[0] = p[1] # N-random artists modifier elif re.match(r'^ra[0-9]+$', modifier): artists = OrderedSet() for song in p[1]: artists.add(mpd.get_tag(song, 'artist')) try: r_artists = OrderedSet(random.sample(artists, int(modifier[2:]))) except ValueError: p[0] = p[1] else: songs = [] for artist in r_artists: songs.extend(mpd.find('artist', artist)) p[0] = OrderedSet([song for song in p[1] if song in songs]) # N-random albums modifier elif re.match(r'^rb[0-9]+$', modifier): albums = OrderedSet() for song in p[1]: albums.add(mpd.get_tags(song, ('album', 'artist'))) try: r_albums = OrderedSet(random.sample(albums, int(modifier[2:]))) except ValueError: p[0] = p[1] else: songs = [] for album, artist in r_albums: songs.extend(mpd.find_multiple(album=album, artist=artist)) p[0] = OrderedSet([song for song in p[1] if song in songs]) # N-minutes-long modifier elif re.match(r'^d[0-9]+$', modifier): total_duration = int(modifier[1:]) * 60 d = 0 p[0] = OrderedSet() p[1] = list(p[1]) random.shuffle(p[1]) for song in p[1]: if d < total_duration: p[0].add(song) d += int(mpd.get_tag(song, 'time')) else: break # N-similar artists modifier elif re.match(r'^i?sa[0-9]+$', modifier): include = True if modifier[0] == 'i' else False limit = int((modifier[3:] if include else modifier[2:])) w_tags = defaultdict(int) for song in p[1]: tags = lastfm.get_artist_tags(mpd.get_tag(song, 'artist')) for tag in tags: w_tags[tag] += tags[tag] if not w_tags: p[0] = p[1] if include else OrderedSet() else: songs = [] similar_artists = lastfm.get_similar_artists(w_tags) for artist, score in similar_artists: if not limit: break matched_songs = mpd.find('artist', artist) if not include: matched_songs = OrderedSet(matched_songs) - p[1] if matched_songs: songs.extend(matched_songs) limit -= 1 p[0] = OrderedSet(songs) # N-similar albums modifier elif re.match(r'^i?sb[0-9]+$', modifier): include = True if modifier[0] == 'i' else False limit = int((modifier[3:] if include else modifier[2:])) w_tags = defaultdict(int) for song in p[1]: tags = lastfm.get_album_tags(mpd.get_tag(song, 'album'), mpd.get_tag(song, 'artist')) for tag in tags: w_tags[tag] += tags[tag] if not w_tags: p[0] = p[1] if include else OrderedSet() else: songs = [] for (album, artist), score in lastfm.get_similar_albums(w_tags): if not limit: break matched_songs = mpd.find_multiple(album=album, artist=artist) if not include: matched_songs = OrderedSet(matched_songs) - p[1] if matched_songs: songs.extend(matched_songs) limit -= 1 p[0] = OrderedSet(songs) else: warning('Modifier [%s] doesn\'t exist' % modifier) sys.exit(0)
def p_error(t): warning('Syntax error') sys.exit(0)
def t_error(t): warning('Illegal character `%s`' % t.value[0]) sys.exit(0)
# coding: utf-8 import os import sys from configparser import ConfigParser from mpdc.libs.utils import Cache, warning, colors_c, columns_w config = ConfigParser() if not config.read(os.path.expanduser('~/.mpdc')): warning('Cannot read the configuration file, please run mpdc-configure') sys.exit(0) try: config['profiles'] config['profiles']['host[1]'] config['profiles']['port[1]'] config['profiles']['password[1]'] config['mpdc']['collections'] except KeyError: warning('Invalid configuration file') sys.exit(0) profiles = {} for key in config['profiles']: if key == 'default': continue num = int(''.join(filter(str.isdigit, key))) if num not in profiles: profiles[num] = {
# coding: utf-8 import os import sys from configparser import ConfigParser from mpdc.libs.utils import is_cached, cache_last_modified, read_cache, \ write_cache, warning, available_colors config = ConfigParser() if not config.read(os.path.expanduser('~/.mpdc')): warning('Can\'t read the configuration file, please run mpdc-configure') sys.exit(0) try: config['mpd']['host'] config['mpd']['password'] config['mpd']['port'] config['mpdc']['collections'] except KeyError: warning('Invalid configuration file') sys.exit(0) colors = ['none', 'none', 'none'] if 'colors' in config['mpdc']: user_colors = [s.strip() for s in config['mpdc']['colors'].split(',')] if (len(user_colors) == 3 and all(color in available_colors for color in user_colors)): colors = user_colors