#!/usr/bin/env python3 from __future__ import print_function import sys import os import notify2 import json import argparse import operator import fnmatch from pprint import pprint from mpd import MPDClient import configparser client = MPDClient() config = configparser.ConfigParser() config.sections() config.read(os.getenv('HOME')+'/.config/clerk/helper_config') change_db = (config['updater']['change_db']) separator = (config['global']['separator']) os.environ['separator'] = str(" "+separator+" ") mpd_host = 'localhost' mpd_port = '6600' mpd_pass = '' if 'MPD_HOST' in os.environ: mpd_connection = os.environ['MPD_HOST'].split('@') if len(mpd_connection) == 1: mpd_host = mpd_connection[0] elif len(mpd_connection) == 2: mpd_host = mpd_connection[1] mpd_pass = mpd_connection[0] else: print('Unable to parse MPD_HOST, using defaults') if 'MPD_PORT' in os.environ: mpd_port = os.environ['MPD_PORT'] client.connect(mpd_host, mpd_port) if mpd_pass: client.password(mpd_pass) def update(args): db = (client.stats()) new_db = db.get('db_update') if change_db == new_db: if (os.path.isfile(os.getenv('HOME')+'/.config/clerk/latest.cache')): quit() else: notify2.init('Clerk') n = notify2.Notification("Clerk", "Updating Cache Files") n.show() createCache(args) else: notify2.init('Clerk') n = notify2.Notification("Clerk", "Updating Cache Files") n.show() createCache(args) config['updater']['change_db'] = str(new_db) with open(os.getenv('HOME')+'/.config/clerk/helper_config', 'w') as configfile: config.write(configfile) def createCache(args): alist=client.search('filename', " ") latest=[] for i in alist: date_time=i['last-modified'] mtime=date_time[:10] if 'albumartist' in i: if isinstance(i['albumartist'], list): albumartist = i['albumartist'][0] else: albumartist = i['albumartist'] else: print("WARNING: missing albumartist tag for file: "+i['file']) if 'date' in i: date=i['date'] if isinstance(i['date'], list): date = i['date'][0] else: date = i['date'] else: print("WARNING: missing date tag for file: "+i['file']) if 'title' in i: if isinstance(i['title'], list): title = i['title'][0] else: title=i['title'] else: print("WARNING: missing title tag for file: "+i['file']) if 'track' in i: if isinstance(i['track'], list): track = i['track'][0] else: track = i['track'] else: print("WARNING: missing track tag for file: "+i['file']) if 'album' in i: if isinstance(i['album'], list): album = i['album'][0] else: album = i['album'] else: print("WARNING: missing album tag for file: "+i['file']) if 'artist' in i: if isinstance(i['artist'], list): artist = i['artist'][0] else: artist = i['artist'] else: print("WARNING: missing artist tag for file: "+i['file']) latest.append(mtime+" • "+albumartist+" • "+date+" • "+album) output=set(latest) with open(os.getenv('HOME')+'/.config/clerk/latest.cache', "w") as cache_file: for album in sorted(output, reverse=True): cache_file.write(album[13:]+'\n') os.system("mpc --format \'%artist%"+os.getenv('separator')+"%date%"+os.getenv('separator')+"%album%"+os.getenv('separator')+"%track%"+os.getenv('separator')+"%title%\' search filename \' \' >"+os.getenv('HOME')+"/.config/clerk/tracks.cache") os.system("mpc --format \'%artist%"+os.getenv('separator')+"%date%"+os.getenv('separator')+"%album%\' search filename \' \' | uniq >"+os.getenv('HOME')+"/.config/clerk/albums.cache") def track_sort(track_num): if track_num == '': return 0 else: return int(track_num.split('/')[0]) def loadjson(fname): with open(fname) as data_file: data = json.load(data_file) return data def extract_key(entry): return entry["albumartist"] + entry["album"] + entry["date"] def hashdata(jdata): hashmap = {} for i in range(len(jdata)): key = extract_key(jdata[i]) hashmap[key] = i return hashmap def has_entry(fastlist, albumartist, album, date): key = albumartist+album+date if key in fastlist[1]: return True else: return False def get_entry(fastlist, albumartist, album, date): return fastlist[0][fastlist[1][albumartist+album+date]] def load_fastlist(fname): jdata = loadjson(fname) hdata = hashdata(jdata) return (jdata, hdata) def save_fastlist(fname, fastlist): with open(fname, "w") as data_file: json.dump(fastlist[0], data_file, ensure_ascii=False) def append_entry(fastlist, entry): index = len(fastlist[0]) fastlist[0].append(entry) fastlist[1][extract_key(entry)] = index def prune_fastlist(fastlist, mpdcachefile): mpdlist = loadjson(mpdcachefile) data = fastlist[0] new = [] for e in mpdlist: key = (e["albumartist"], e["album"], e["date"]) if has_entry(fastlist, *key): entry = get_entry(fastlist, *key) new.append(entry) newhdata = hashdata(new) return (new, newhdata) def rateAlbum(args): fastlist = load_fastlist(os.getenv('HOME')+'/.config/clerk/albumratings.json') try: entry = get_entry(fastlist, args.artist, args.album, args.date) entry["rating"] = args.rating save_fastlist(os.getenv('HOME')+'/.config/clerk/albumratings.json', fastlist) uri = client.find('albumartist', args.artist, 'album', args.album, 'date', args.date, 'track', os.getenv('track'), 'disc', os.getenv('disc')) for i in uri: client.sticker_set("song", i['file'], "albumrating", args.rating) except KeyError: entry = {'albumartist': args.artist, 'album': args.album, 'date': args.date, 'disc': os.getenv('disc'), 'track': os.getenv('track'), 'rating': args.rating} append_entry(fastlist, entry) save_fastlist(os.getenv('HOME')+'/.config/clerk/albumratings.json', fastlist) uri = client.find('artist', args.artist, 'album', args.album, 'date', args.date, 'track', os.getenv('track'), 'disc', os.getenv('disc')) for i in uri: client.sticker_set("song", i['file'], "albumrating", args.rating) def loadjsonTrack(fname): with open(fname) as data_file: data = json.load(data_file) return data def extract_keyTrack(entry): return entry["artist"] + entry["album"] + entry["track"] + entry["title"] def hashdataTrack(jdata): hashmap = {} for i in range(len(jdata)): key = extract_keyTrack(jdata[i]) hashmap[key] = i return hashmap def has_entryTrack(fastlist, artist, album, track, title): key = artist+album+track+title if key in fastlist[1]: return True else: return False def get_entryTrack(fastlist, artist, album, track, title): return fastlist[0][fastlist[1][artist+album+track+title]] def load_fastlistTrack(fname): jdata = loadjsonTrack(fname) hdata = hashdataTrack(jdata) return (jdata, hdata) def save_fastlistTrack(fname, fastlist): with open(fname, "w") as data_file: json.dump(fastlist[0], data_file, ensure_ascii=False) def append_entryTrack(fastlist, entry): index = len(fastlist[0]) fastlist[0].append(entry) fastlist[1][extract_keyTrack(entry)] = index def prune_fastlistTrack(fastlist, mpdcachefile): mpdlist = loadjsonTrack(mpdcachefile) data = fastlist[0] new = [] for e in mpdlist: key = (e["artist"], e["album"], e["track"], e["title"]) if has_entryTrack(fastlist, *key): entry = get_entryTrack(fastlist, *key) new.append(entry) newhdata = hashdataTrack(new) return (new, newhdata) def rateTrack(args): fastlist = load_fastlistTrack(os.getenv('HOME')+'/.config/clerk/trackratings.json') try: entry = get_entryTrack(fastlist, args.artist, args.album, args.track, args.title) entry["rating"] = args.rating save_fastlistTrack(os.getenv('HOME')+'/.config/clerk/trackratings.json', fastlist) uri = client.find('artist', args.artist, 'album', args.album, 'track', args.track, 'title', args.title) for i in uri: client.sticker_set("song", i['file'], "rating", args.rating) except KeyError: entry = {'artist': args.artist, 'album': args.album, 'track': args.track, 'title': args.title, 'rating': args.rating} append_entryTrack(fastlist, entry) save_fastlistTrack(os.getenv('HOME')+'/.config/clerk/trackratings.json', fastlist) uri = client.find('artist', args.artist, 'album', args.album, 'track', args.track, 'title', args.title) for i in uri: client.sticker_set("song", i['file'], "rating", args.rating) def cleanRatings(args): fastlist = load_fastlist(os.getenv('HOME')+'/.config/clerk/albumratings.json') prunedlist = prune_fastlist(fastlist, os.getenv('HOME')+'/.config/clerk/albums.cache.json') save_fastlist(os.getenv('HOME')+'/.config/clerk/albumratings.json', prunedlist) fastlist = load_fastlistTrack(os.getenv('HOME')+'/.config/clerk/trackratings.json') prunedlist = prune_fastlistTrack(fastlist, os.getenv('HOME')+'/.config/clerk/tracks.cache.json') save_fastlistTrack(os.getenv('HOME')+'/.config/clerk/trackratings.json', prunedlist) def importTrackRatings(args): tracklist = client.sticker_find('song', "", 'rating') rated_tracks = [] for i in tracklist: rating_temp = i['sticker'] rating = rating_temp.split('=')[1] for x in client.find('file', i['file']): artist = x['artist'] title = x['title'] album = x['album'] if isinstance(x['track'], list): track = x['track'][0] else: track = x['track'] entry = {'artist': artist, 'album': album, 'track': track, 'title': title, 'rating': rating} rated_tracks.append(entry) with open(os.getenv('HOME')+'/.config/clerk/trackratings.json', 'w') as ratingfile: json.dump(rated_tracks, ratingfile) def importAlbumRatings(args): ratelist = client.sticker_find('song', "", 'albumrating') rated_tracks = [] for i in ratelist: rating_temp = i['sticker'] rating = rating_temp.split('=')[1] for x in client.find('file', i['file']): artist = x['albumartist'] album = x['album'] date = x['date'] if isinstance(x['track'], list): track = x['track'][0] else: track = x['track'] if 'disc' in x: if isinstance(x['disc'], list): disc = x['disc'][0] else: disc = x['disc'] else: disc = "" entry = {'albumartist': artist, 'track': track, 'album': album, 'date': date, 'disc': disc, 'rating': rating} rated_tracks.append(entry) with open(os.getenv('HOME')+'/.config/clerk/albumratings.json', 'w') as ratingfile: json.dump(rated_tracks, ratingfile) def sendStickers(args): with open(os.getenv('HOME')+'/.config/clerk/trackratings.json') as cache_file: content = json.load(cache_file) for track in content: uri = client.find('artist', track['artist'], 'album', track['album'], 'title', track['title'], 'track', track['track']) rating = track['rating'] for x in uri: client.sticker_set('song', x['file'], 'rating', rating) print("Imported Rating of "+rating+" for file "+x['file']) with open(os.getenv('HOME')+'/.config/clerk/albumratings.json') as cache_file: content = json.load(cache_file) for album in content: uri = client.find('albumartist', album['albumartist'], 'album', album['album'], 'disc', album['disc'], 'date', album['date'], 'track', album['track']) rating = album['rating'] for x in uri: client.sticker_set('song', x['file'], 'albumrating', rating) print("Imported Rating of "+rating+" for album "+album['albumartist']+" - "+album['album']) def getAlbumRatings(args): ratings = client.sticker_find('song', '', 'albumrating') albumlist = [] for x in ratings: rating = x['sticker'] albums = client.find('file', x['file']) for album in albums: entry = {'albumartist': album['albumartist'], 'date': album['date'], 'album': album['album'], 'track': album['track'], 'rating': rating} print(entry['albumartist']+os.getenv('separator')+entry['date']+os.getenv('separator')+entry['album']+os.getenv('separator')+entry['rating']) def readComments(args): args = vars(args) comments = (client.readcomments(sys.stdin.read()[:-1])) for key, value in sorted(comments.items()): print('%s : %s' % (key, value)) def prioSong(args): for line in sys.stdin.read().splitlines(): client.prio(255, line) def savetoPlaylist(args): for line in sys.stdin: if line.strip(): line = line.strip() client.playlistadd("clerk", line) # print(line, end="") def getArtistAlbums(args): albums=client.find('artist', args.artist) albumlist=[] for album in albums: albumlist.append(album['date']+os.getenv('separator')+album['album']) uni=set(albumlist) result = list(uni) sorted_results = sorted(result) for x in sorted_results: print("(Album) "+x) def getArtistTracks(args): tracks=client.find('artist', args.artist) tracklist=[] for track in tracks: entry = {'track': track['track'], 'title': track['title']} tracklist.append(entry) for x in tracklist: print("(Song) "+x['track']+os.getenv('separator')+x['title']) # create commandline arguments parser = argparse.ArgumentParser(prog='clerk_helper', description='Companion script for clerk') subparsers = parser.add_subparsers() parser_prio = subparsers.add_parser('prio', help="prioritize song") parser_prio.set_defaults(call=prioSong) parser_update = subparsers.add_parser('update', help="update cache files") parser_update.set_defaults(call=update) parser_readcomments = subparsers.add_parser('readcomments', help="show all tags of current song") parser_readcomments.set_defaults(call=readComments) parser_saveto = subparsers.add_parser('saveto', help="save stdin to playlist \"clerk\"") parser_saveto.set_defaults(call=savetoPlaylist) parser_createcache = subparsers.add_parser('createcache', help="create track cache for clerk") parser_createcache.set_defaults(call=createCache) parser_ratealbum = subparsers.add_parser('ratealbum', help="rate current Album") parser_ratealbum.add_argument('artist', action="store") parser_ratealbum.add_argument('album', action="store") parser_ratealbum.add_argument('date', action="store") parser_ratealbum.add_argument('rating', action="store") parser_ratealbum.set_defaults(call=rateAlbum) parser_ratetrack = subparsers.add_parser('ratetrack', help="rate current Track") parser_ratetrack.add_argument('artist', action="store") parser_ratetrack.add_argument('album', action="store") parser_ratetrack.add_argument('track', action="store") parser_ratetrack.add_argument('title', action="store") parser_ratetrack.add_argument('rating', action="store") parser_ratetrack.set_defaults(call=rateTrack) parser_cleanratings = subparsers.add_parser('cleanratings', help="Clean up Ratings file.") parser_cleanratings.set_defaults(call=cleanRatings) parser_importtrackratings = subparsers.add_parser('importtrackratings', help="Import track ratings from mpd stickers") parser_importtrackratings.set_defaults(call=importTrackRatings) parser_importalbumratings = subparsers.add_parser('importalbumratings', help="Import album ratings from mpd stickers") parser_importalbumratings.set_defaults(call=importAlbumRatings) parser_sendstickers = subparsers.add_parser('sendstickers', help="Send clerk ratings to mpd") parser_sendstickers.set_defaults(call=sendStickers) parser_getalbumratings = subparsers.add_parser('getalbumratings', help="Print Album Ratings") parser_getalbumratings.set_defaults(call=getAlbumRatings) parser_getartistalbums = subparsers.add_parser('getartistalbums', help="Print all albums by Artist") parser_getartistalbums.add_argument('artist', action="store") parser_getartistalbums.set_defaults(call=getArtistAlbums) parser_getartisttracks = subparsers.add_parser('getartisttracks', help="Print all tracks by Artist") parser_getartisttracks.add_argument('artist', action="store") parser_getartisttracks.set_defaults(call=getArtistTracks) # parse arguments (thanks jat) args = parser.parse_args() try: args.call(args) except AttributeError: print("No arguments given. Try clerk_helper -h")