2016-03-22 03:12:12 +00:00
|
|
|
#!/usr/bin/env python
|
2016-03-21 04:26:02 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
"""
|
|
|
|
Find all attributes for each library type
|
|
|
|
"""
|
|
|
|
import pickle, os, sys
|
|
|
|
from collections import defaultdict
|
|
|
|
from plexapi.server import PlexServer
|
2016-03-22 03:12:12 +00:00
|
|
|
from xml.etree import ElementTree
|
2016-03-21 04:26:02 +00:00
|
|
|
|
|
|
|
MAX_SEEN = 9999
|
2016-03-22 03:12:12 +00:00
|
|
|
MAX_EXAMPLES = 10
|
|
|
|
PICKLE_PATH = '/tmp/findattrs.pickle'
|
|
|
|
ETYPES = ['artist', 'album', 'track', 'movie', 'show', 'season', 'episode']
|
2016-03-21 04:26:02 +00:00
|
|
|
|
|
|
|
|
|
|
|
def find_attrs(plex, key, result, examples, seen, indent=0):
|
|
|
|
for elem in plex.query(key):
|
|
|
|
attrs = sorted(elem.attrib.keys())
|
|
|
|
etype = elem.attrib.get('type')
|
2016-03-22 03:12:12 +00:00
|
|
|
if not etype or seen[etype] >= MAX_SEEN:
|
|
|
|
continue
|
2016-03-21 04:26:02 +00:00
|
|
|
seen[etype] += 1
|
|
|
|
sys.stdout.write('.'); sys.stdout.flush()
|
2016-03-22 03:12:12 +00:00
|
|
|
# find all attriutes in this element
|
2016-03-21 04:26:02 +00:00
|
|
|
for attr in attrs:
|
|
|
|
result[attr].add(etype)
|
2016-03-22 03:12:12 +00:00
|
|
|
if elem.attrib[attr] and len(examples[attr]) <= MAX_EXAMPLES:
|
2016-03-21 04:26:02 +00:00
|
|
|
examples[attr].add(elem.attrib[attr])
|
2016-03-22 03:12:12 +00:00
|
|
|
# iterate all subelements
|
2016-03-21 04:26:02 +00:00
|
|
|
for subelem in elem:
|
2016-03-22 03:12:12 +00:00
|
|
|
if subelem.tag not in ETYPES:
|
2016-03-21 04:26:02 +00:00
|
|
|
subattr = subelem.tag + '[]'
|
|
|
|
result[subattr].add(etype)
|
2016-03-22 03:12:12 +00:00
|
|
|
if len(examples[subattr]) <= MAX_EXAMPLES:
|
|
|
|
xmlstr = ElementTree.tostring(subelem, encoding='utf8').split('\n')[1]
|
|
|
|
examples[subattr].add(xmlstr)
|
|
|
|
# recurse into the main element (loading its key)
|
|
|
|
if etype in ETYPES:
|
2016-03-21 04:26:02 +00:00
|
|
|
subkey = elem.attrib['key']
|
2016-03-22 03:12:12 +00:00
|
|
|
if key != subkey and seen[etype] < MAX_SEEN:
|
2016-03-21 04:26:02 +00:00
|
|
|
find_attrs(plex, subkey, result, examples, seen, indent+2)
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
|
def find_all_attrs():
|
|
|
|
try:
|
|
|
|
result = defaultdict(set)
|
|
|
|
examples = defaultdict(set)
|
|
|
|
seen = defaultdict(int)
|
|
|
|
plex = PlexServer()
|
|
|
|
find_attrs(plex, '/status/sessions', result, examples, seen)
|
|
|
|
for section in plex.library.sections():
|
|
|
|
for elem in section.all():
|
|
|
|
# check weve seen too many of this type of elem
|
|
|
|
if seen[elem.type] >= MAX_SEEN:
|
|
|
|
continue
|
|
|
|
seen[elem.type] += 1
|
|
|
|
# fetch the xml for this elem
|
|
|
|
key = elem.key.replace('/children', '')
|
|
|
|
find_attrs(plex, key, result, examples, seen)
|
|
|
|
except KeyboardInterrupt:
|
|
|
|
pass
|
|
|
|
return result, examples, seen
|
|
|
|
|
|
|
|
def print_results(result, examples, seen):
|
|
|
|
print_general_summary(result, examples)
|
|
|
|
print_summary_by_etype(result, examples)
|
|
|
|
print_seen_etypes(seen)
|
|
|
|
|
|
|
|
def print_general_summary(result, examples):
|
|
|
|
print('\n--- general summary ---')
|
2016-03-22 03:12:12 +00:00
|
|
|
for attr, etypes in sorted(result.items(), key=lambda x: x[0].lower()):
|
|
|
|
args = [attr]
|
|
|
|
args += [etype if etype in etypes else '--' for etype in ETYPES]
|
|
|
|
args.append(list(examples[attr])[0][:30] if examples[attr] else '_NA_')
|
|
|
|
print('%-23s %-8s %-8s %-8s %-8s %-8s %-8s %-8s %s' % tuple(args))
|
2016-03-21 04:26:02 +00:00
|
|
|
|
|
|
|
def print_summary_by_etype(result, examples):
|
|
|
|
# find super attributes (in all etypes)
|
2016-03-22 03:12:12 +00:00
|
|
|
result['super'] = set(['librarySectionID', 'index', 'titleSort'])
|
2016-03-21 04:26:02 +00:00
|
|
|
for attr, etypes in result.items():
|
|
|
|
if len(etypes) == 7:
|
2016-03-22 03:12:12 +00:00
|
|
|
result['super'].add(attr)
|
2016-03-21 04:26:02 +00:00
|
|
|
# print the summary
|
2016-03-22 03:12:12 +00:00
|
|
|
for etype in ['super'] + ETYPES:
|
2016-03-21 04:26:02 +00:00
|
|
|
print('\n--- %s ---' % etype)
|
2016-03-22 03:12:12 +00:00
|
|
|
for attr in sorted(result.keys(), key=lambda x:x[0].lower()):
|
|
|
|
if (etype in result[attr] and attr not in result['super']) or (etype == 'super' and attr in result['super']):
|
|
|
|
example = list(examples[attr])[0][:80] if examples[attr] else '_NA_'
|
2016-03-21 04:26:02 +00:00
|
|
|
print('%-23s %s' % (attr, example))
|
|
|
|
|
|
|
|
def print_seen_etypes(seen):
|
|
|
|
print('\n')
|
|
|
|
for etype, count in seen.items():
|
|
|
|
print('%-8s %s' % (etype, count))
|
|
|
|
|
|
|
|
|
|
|
|
def save_pickle(result, examples, seen):
|
|
|
|
with open(PICKLE_PATH, 'w') as handle:
|
|
|
|
pickle.dump({'result':result, 'examples':examples, 'seen':seen}, handle)
|
|
|
|
|
|
|
|
|
|
|
|
def load_pickle():
|
|
|
|
if not os.path.exists(PICKLE_PATH):
|
|
|
|
return None, None, None
|
|
|
|
with open(PICKLE_PATH, 'r') as handle:
|
|
|
|
data = pickle.load(handle)
|
|
|
|
return data['result'], data['examples'], data['seen']
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
result, examples, seen = load_pickle()
|
|
|
|
if not result:
|
|
|
|
result, examples, seen = find_all_attrs()
|
|
|
|
save_pickle(result, examples, seen)
|
|
|
|
print_results(result, examples, seen)
|