Skip to content

Instantly share code, notes, and snippets.

@seahawk1986
Last active August 26, 2019 08:56
Show Gist options
  • Save seahawk1986/6e964973c9e0bac1bc40ab1b915362f6 to your computer and use it in GitHub Desktop.
Save seahawk1986/6e964973c9e0bac1bc40ab1b915362f6 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
"""
*** ANSIBLE MANAGED FILE ***
template: /home/alexander/yavdr-ansible/roles/kodi/templates/set-kodi-display.j2
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 logging
import os
import sys
import subprocess
import traceback
from lxml import etree as ET
logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.DEBUG)
GUISETTINGS = '/var/lib/vdr/.kodi/userdata/guisettings.xml'
CACHE_DIR = '/var/lib/vdr/.kodi/.display_cache'
VIDEOSCREEN_TEMPLATE = """<settings version="2">
<setting id="videoscreen.monitor">{}</setting>
</settings>"""
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:
logging.exception(f"Unexpected Error when trying to create {CACHE_DIR}:")
sys.exit(1)
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:
logging.exception("could not determine output name")
sys.exit("output name unknown, exiting early")
def parse_template(template_path, template, output=""):
"""read videoscreen settings from backup or create a stub file"""
def xml_tree_from_template(template, output):
xml_template = ET.fromstring(template.format(output))
xml_tree = ET.ElementTree(xml_template)
return xml_tree
try:
xml_tree = ET.parse(template_path)
except OSError:
logging.debug(f"{template_path} not found, creating stub file")
xml_tree = xml_tree_from_template(template, output)
except ET.XMLSyntaxError as e:
if e.msg.startswith('Document is empty'):
logging.info(f"{template_path} is empty, creating stub file")
xml_tree = xml_tree_from_template(template, output)
else:
sys.exit(f"Could not parse {template_path}, please fix file or remove it")
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:
logging.debug("no changes necessary, exiting")
sys.exit()
# create a minimal guisettings etree
xml_path = os.path.join(CACHE_DIR, f'{old_output}-videoscreen.xml')
base_tree = ET.fromstring('<settings version="2"></settings>')
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)
logging.debug(f"written backup for {old_output} to {xml_path}")
# 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:
logging.exception("Could not change videoscreen.* settings")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment