Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e9314ea1ee |
No files matched your search
@@ -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}>")
|
||||
|
||||
|
||||
@@ -13,9 +13,12 @@
|
||||
# 3. a downloaded plugin artifact is accepted only on its pinned sha256, its
|
||||
# official guid and its pinned version (a swapped or downgraded artifact
|
||||
# is refused before anything lands in plugins/);
|
||||
# 4. the TMDb key is written into the EXISTING config with every other
|
||||
# setting preserved — Jellyfin owns that file, and clobbering it would
|
||||
# silently reset country, language and adult settings;
|
||||
# 4. a key is written into the EXISTING config with every other setting
|
||||
# preserved, under whatever root that file's owner chose — Jellyfin's core
|
||||
# configs say `<Configuration>`, TheTVDB writes `<PluginConfiguration>` and
|
||||
# omits a null setting outright, so the element to fill can be absent and
|
||||
# the anchor has to come from the file; clobbering the file would silently
|
||||
# reset country, language and adult settings;
|
||||
# 5. a TVDB config the operator already saved through the UI is kept;
|
||||
# 6. install is idempotent: a second run changes no byte;
|
||||
# 7. whatever is replaced is copied to the trash first (fleet law: never
|
||||
@@ -254,6 +257,38 @@ XML
|
||||
grep -qF '<EnableSeasonPathMatching>true</EnableSeasonPathMatching>' "$cfg"
|
||||
}
|
||||
|
||||
@test "the key lands in a plugin-written config with no ApiKey element" {
|
||||
# What TheTVDB actually writes on a fresh install: its own root element and
|
||||
# no <ApiKey /> at all, because its serializer drops null settings. The
|
||||
# installer used to anchor on </Configuration> and refuse this file.
|
||||
cat >"$JF/plugins/configurations/Jellyfin.Plugin.Tvdb.xml" <<XML
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PluginConfiguration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
||||
<SubscriberPIN />
|
||||
<CacheDurationInHours>1</CacheDurationInHours>
|
||||
<OriginalTitle>true</OriginalTitle>
|
||||
</PluginConfiguration>
|
||||
XML
|
||||
run prov install
|
||||
[ "$status" -eq 0 ]
|
||||
printf '%s\n' "${lines[@]}" | grep -qiF 'filled the ApiKey'
|
||||
local cfg="$JF/plugins/configurations/Jellyfin.Plugin.Tvdb.xml"
|
||||
grep -qF "<ApiKey>$TVDB_KEY</ApiKey>" "$cfg"
|
||||
grep -qF '<CacheDurationInHours>1</CacheDurationInHours>' "$cfg"
|
||||
grep -qF '<OriginalTitle>true</OriginalTitle>' "$cfg"
|
||||
}
|
||||
|
||||
@test "a config file with no root element is refused by name" {
|
||||
# The guard still has to fire when there is genuinely nowhere to write —
|
||||
# a refusal naming the file beats inventing a document shape.
|
||||
printf '<?xml version="1.0" encoding="utf-8"?>\n' \
|
||||
>"$JF/plugins/configurations/Jellyfin.Plugin.Tvdb.xml"
|
||||
run prov install
|
||||
[ "$status" -ne 0 ]
|
||||
printf '%s\n' "${lines[@]}" | grep -qF 'Jellyfin.Plugin.Tvdb.xml'
|
||||
printf '%s\n' "${lines[@]}" | grep -qiF 'no place to write'
|
||||
}
|
||||
|
||||
@test "a pin in the TVDB secret lands in the plugin config" {
|
||||
# A subscriber-shaped key needs the account's PIN beside it; a licensed key
|
||||
# does not, so the element appears only when the secret carries one.
|
||||
|
||||
Reference in new issue
Block a user