#!/usr/bin/env python3 """ {{ ansible_managed }} This Script changes the monitor in KODI's guisettings.xml to the wanted output according to the DISPLAY environment variable. It works with KODI 18 (not KODI 17!). In order to change the display we need to modify the settings/videoscreen nodes. Basic algorithm: - get the current videoscreen.monitor - check if it needs to be changed - create a backup of the videoscreen nodes in /var/lib/vdr/.kodi/.display_cache/{CONNETOR}-videoscreen.xml - check if there is an existing backup for the new CONNECTOR - parse the backup of the videoscreen nodes - replace the videoscreen nodes with the backup data """ import copy import os import sys import subprocess from lxml import etree as ET GUISETTINGS = '/var/lib/vdr/.kodi/userdata/guisettings.xml' CACHE_DIR = '/var/lib/vdr/.kodi/.display_cache' VIDEOSCREEN_TEMPLATE = """ {} """ def create_cache_dir(): try: os.makedirs(CACHE_DIR, exist_ok=True) except PermissionError: sys.exit(f"Error: insufficient permissions to create cachedir {CACHE_DIR}") except Exception as e: sys.exit(f"Unexpected Error when trying to create {CACHE_DIR}:", e) def get_output_name(): """ get display name from xrandr output for given DISPLAY environment variable """ try: xrandr_output = [ l for l in subprocess.check_output( ["xrandr"], env={"DISPLAY": os.environ["DISPLAY"]} ).decode("utf-8").splitlines() ] return next(l.split()[0] for l in xrandr_output if " connected " in l) except Exception as e: sys.exit("could not determine output name", e) def parse_template(template_path, template, output=""): """read videoscreen settings from backup or create a stub file""" try: xml_tree = ET.parse(template_path) except OSError: print(f"{template_path} not found, creating stub file", file=sys.stderr) xml_template = ET.fromstring(template.format(output)) xml_tree = ET.ElementTree(xml_template) finally: xml_tree.write(template_path) return xml_tree def main(output): guisettings = parse_template(GUISETTINGS, VIDEOSCREEN_TEMPLATE, "Default") # parse guisettings Etree for display name an backup videoscreen data root = guisettings.getroot() old_output = root.find("./setting[@id='videoscreen.monitor']").text if old_output == output: print("no changes necessary, exiting", file=sys.stderr) sys.exit() # create a minimal guisettings etree xml_path = os.path.join(CACHE_DIR, f'{old_output}-videoscreen.xml') base_tree = ET.fromstring('') xml_tree = ET.ElementTree(base_tree) backup_root = xml_tree.getroot() # copy videoscreen elements to backup etree videoscreen_elements = root.xpath( "./setting[starts-with(@id, 'videoscreen.')]") for element in videoscreen_elements: backup_root.append(copy.deepcopy(element)) element.getparent().remove(element) xml_tree.write(xml_path) print(f"written backup for {old_output} to {xml_path}", file=sys.stderr) # change videoscreen node to content of backup file xml_path = os.path.join(CACHE_DIR, f'{output}-videoscreen.xml') videodir_xml = parse_template(xml_path, VIDEOSCREEN_TEMPLATE, output) videodir_root = videodir_xml.getroot() videoscreen = videodir_root.find("./setting[@id='videoscreen.monitor']") # copy videoscreen.* elements from Backup videoscreen_elements = videodir_root.xpath( "./setting[starts-with(@id, 'videoscreen.')]") for element in videoscreen_elements: new_element = copy.deepcopy(element) root.append(new_element) guisettings.write(GUISETTINGS) if __name__ == '__main__': create_cache_dir() output = get_output_name() try: main(output) except Exception as e: print("Could not change videoscreen.* settings:", str(e), file=sys.stderr)