fix(jellyfin): anchor the provider-key write on the file's real root

TheTVDB writes its own configuration file and its serializer omits a
null-valued setting entirely, so a fresh install has no <ApiKey> element to
fill, under a root element called <PluginConfiguration>. xml_secret's
fallback insertion was hardcoded to </Configuration>, matched nothing, and
install refused with "no place to write <ApiKey>" — measured on Dankserver
2026-11-02, where the TVDB key has been present in the secret file since
October and has never been installable.

The anchor now comes from the document's own root, so both shapes Jellyfin
actually produces work (the core plugins' <Configuration> and a plugin's
<PluginConfiguration>), and the refusal is kept for a file with no root at
all. Tests: the plugin-written shape is red before this commit and green
after; every other setting in the file is asserted preserved.
This commit is contained in:
Frank committed 2026-11-02 04:38:40 -08:00
1 parent 7a8c6bd2e3
commit e9314ea1ee
2 files changed
+50 -4

No files matched your search

+12 -1
View File
@@ -135,7 +135,18 @@ text = escape(val)
if pat.search(src):
out = pat.sub(lambda m: f"{m.group(1)}<{elem}>{text}</{elem}>", src, count=1)
else:
out = src.replace("</Configuration>", f" <{elem}>{text}</{elem}>\n</Configuration>", 1)
# The plugin owns this file and writes it under ITS OWN root element
# (TheTVDB's is <PluginConfiguration>), and its serializer omits a
# null-valued setting entirely — so the element being filled can be
# absent outright, and a hardcoded </Configuration> anchor then matches
# nothing. Anchor on the document's real root instead.
out = src
root = re.search(r"<([A-Za-z_][\w.:-]*)[^>]*>", src)
if root and ("</%s>" % root.group(1)) in src:
name = root.group(1)
out = src.replace(
"</%s>" % name, f" <{elem}>{text}</{elem}>\n</{name}>", 1
)
if out == src:
raise SystemExit(f"{xml_path}: no place to write <{elem}>")