Skip to content

Instantly share code, notes, and snippets.

@PaulWebster
Created June 8, 2022 05:19
Show Gist options
  • Save PaulWebster/1c2f5fb0a435e63cff918b9858f5e670 to your computer and use it in GitHub Desktop.
Save PaulWebster/1c2f5fb0a435e63cff918b9858f5e670 to your computer and use it in GitHub Desktop.
lms_getalbums
#!/usr/bin/env python3
from operator import itemgetter
from argparse import ArgumentParser
from argparse import RawDescriptionHelpFormatter
from datetime import datetime
import urllib.request
import json, re
html_styling = '''
<style type="text/css">
ol,ul {
list-style: none;
font-weight: bold;
}
.navbar {
overflow: visible;
background-color: #103c48;
position: fixed;
z-index: 1;
top: 0;
left: 95%;
float: right;
width: 5%;
overflow-x: hidden;
padding: 0 0 0 0;
}
.navbar a {
float: left;
display: block;
color: #adbcbc;
text-align: right;
text-decoration: none;
padding: 2px 2px;
}
.content {
width: 100%;
}
</style>
'''
'''
width: 100%;
position: fixed;
padding: 5px 5px;
.content {
margin-top: 30px;
}
'''
headers = { 'Content-Type': 'application/json'}
table_tmpl ='<tr>{}<td>{}</td><td>{}</td></tr>'
json_data_albums = {"id":1, "method":"slim.request", "params": ["-", ["albums", 0, 99999, "tags:aaly"]]}
json_data_sort = {"id":1,"method":"slim.request","params":["-", ["pref","ignoredarticles", "?"]]}
json_data_libraries = {"id":1, "method":"slim.request", "params": ["-", ["libraries",0,25000]]}
parser = ArgumentParser(description="writes all albums from LMS to a html file", formatter_class=RawDescriptionHelpFormatter)
parser.add_argument('lmshost', action='store', help='LMS host name (including port, e.g. lmssrv:9000)')
parser.add_argument('outfile', action='store', help='output file')
parser.add_argument('--no-quickjump', action='store_true', help='do no add quickjump links')
parser.add_argument('--no-sortcase', action='store_true', help='do no use case-sensitive sorting')
parser.add_argument('--sort-ignorearticles', action='store_true', help='ignore articles for sorting')
parser.add_argument('--hlcolor', action='store', default="#184956", help='highlight color')
parser.add_argument('--libraryview', action='store', help='library view name (use quotes if spaces)')
args = parser.parse_args()
url = "http://{}/jsonrpc.js".format( args.lmshost)
libraryid = ''
libraryname = ''
if args.libraryview:
post_data_libraries = json.dumps( json_data_libraries).encode('utf-8')
req = urllib.request.Request(url, post_data_libraries, headers)
with urllib.request.urlopen(req) as response:
result = response.read()
res = json.loads(result.decode("utf-8"))
if res and 'result' in res and res['result'] and 'folder_loop' in res['result']:
# example result
# {'id': 1, 'result': {'folder_loop': [{'id': 'aabbccdd', 'name': 'Name1'}, {'id': 'bbccddee', 'name': 'Name2'}, {'name': 'AnotherName', 'id': '12345678'}]}, 'method': 'slim.request', 'params': ['-', ['libraries', '0', '25000']]})
#print("Library search found (result: {})".format( repr( res)))
libraries = res['result']['folder_loop']
for library in libraries:
#print("Library:" + str(library['name']))
if library['name'] == args.libraryview:
print("Matched: " + args.libraryview)
libraryid = library['id']
libraryname = library['name']
json_data_albums["params"][1].append("library_id:" + libraryid)
#print("now set to " + str(json.dumps( json_data_albums).encode('utf-8')))
if libraryid == '':
print("Error: could not find this library view (result: {})".format( repr( res)))
else:
print("Error: could not find any library views (result: {})".format( repr( res)))
post_data_albums = json.dumps( json_data_albums).encode('utf-8')
post_data_sort = json.dumps( json_data_sort).encode('utf-8')
ignoredarticles_re = None
if args.sort_ignorearticles:
req = urllib.request.Request(url, post_data_sort, headers)
with urllib.request.urlopen(req) as response:
result = response.read()
res = json.loads(result.decode("utf-8"))
if res and 'result' in res and res['result'] and '_p2' in res['result'] and len( res['result']['_p2']) != 0:
ignoredarticles = res['result']['_p2'].split(" ")
_ignoredarticles_re = [ "^{} ".format( re.escape(x)) for x in ignoredarticles]
ignoredarticles_re = re.compile( "|".join( _ignoredarticles_re), flags=re.IGNORECASE)
#print("ignoredarticles_re={}".format( repr( ignoredarticles_re)))
else:
print("Error: couldn't get ignoredarticles from server (result: {})".format( repr( res)))
req = urllib.request.Request(url, post_data_albums, headers)
with urllib.request.urlopen(req) as response:
result = response.read()
res = json.loads(result.decode("utf-8"))
if res and 'result' in res and res['result'] and 'albums_loop' in res['result']:
albums = res['result']['albums_loop']
for i, row in enumerate(albums):
if not row['artist']:
albums[i]['artist'] = "Unknown Artist"
if not row['year'] or row['year'] == 0:
albums[i]['year'] = "?"
if not args.no_sortcase:
albums[i]['sortartist'] = albums[i]['artist'] if not args.sort_ignorearticles else re.sub( ignoredarticles_re, "", albums[i]['artist'])
albums[i]['sortalbum'] = albums[i]['album']
else:
albums[i]['sortartist'] = albums[i]['artist'].casefold() if not args.sort_ignorearticles else re.sub( ignoredarticles_re, "", albums[i]['artist']).casefold()
albums[i]['sortalbum'] = albums[i]['album'].casefold()
albums.sort( key=itemgetter('sortartist', 'sortalbum'))
#albums.sort( key=lambda el: ( el['artist'].casefold(), el['album'].casefold()))
albums.append( { 'artist': 'dummy', 'sortartist': 'dummy'})
num_albums = len( albums)
albums_flat = []
qj_labels = {}
prev_artist = None
artist_proc = []
for i, row in enumerate( albums):
curr_artist = row['sortartist']
if (curr_artist != prev_artist or i == (num_albums - 1)):
# if i == (num_albums - 1):
# print("last loop ca: {} pa: {} len ap:{}".format( curr_artist, prev_artist, artist_proc))
if not prev_artist:
prev_artist = curr_artist
sort_char = prev_artist[0:1].upper() if args.no_sortcase else prev_artist[0:1]
if not args.no_quickjump and sort_char not in qj_labels:
#print("i={} label [{}] rows: {}".format( i, prev_artist[0:1], repr( artist_proc)))
qj_labels[ sort_char] = '<li><a href="#qj_{}">[&nbsp;{}&nbsp;]</a></li>'.format( sort_char, sort_char)
albums_flat.append( '<tr id="qj_{}"><td colspan="3" style="background-color:{}; color: #cad8d9; font-weight: bold">{}</td></tr>\n'.format( sort_char, args.hlcolor, sort_char))
for j, subrow in enumerate( artist_proc):
if j == 0:
albums_flat.append( table_tmpl.format( '<td rowspan="{}" style="vertical-align:top">{}</td>'.format( len( artist_proc), subrow['artist']), subrow['album'], subrow['year']))
else:
albums_flat.append( table_tmpl.format( '', subrow['album'], subrow['year']))
artist_proc = []
prev_artist = curr_artist
artist_proc.append( row)
#albums_flat.append( table_tmpl.format( '<td rowspan=1>{}</td>'.format( curr_artist), row['album'], row['year']))
with open( args.outfile, 'w', encoding='utf-8') as fp:
page_title = '<title>LMS@{} {view}- {} albums ({})</title>'.format( args.lmshost.partition(':')[0], num_albums, datetime.now().isoformat( timespec='seconds'), view="" if not libraryname else "(" + libraryname + ") ")
fp.write( '<html><head><meta charset="UTF-8"/>{}\n{}</head><body bgcolor="#dddddd">\n'.format( page_title, html_styling if not args.no_quickjump else ""))
if not args.no_quickjump:
fp.write( '<div class="navbar"><ul>\n')
fp.write( " ".join( qj_labels[key] for key in sorted(qj_labels.keys())))
fp.write( '</ul></div>\n')
fp.write('<div class="content">\n<table border=1>\n<thead><tr><th>Artist</th><th>Album</th><th>Year</th></tr></thead>\n<tbody>{}</tbody>\n'.format( '\n'.join( albums_flat)))
fp.write( '</div></table></body></html>\n')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment