| … | … | @@ -1,5 +1,5 @@ |
| 1 | 1 | <?xml version="1.0" encoding="utf-8"?> |
| 2 | <addon id="plugin.video.jacktook" name="Jacktook" version="1.16.0" provider-name="Sam-Max"> | |
| 2 | <addon id="plugin.video.jacktook" name="Jacktook" version="1.16.1" provider-name="Sam-Max"> | |
| 3 | 3 | <requires> |
| 4 | 4 | <import addon="xbmc.python" version="3.00.0"/> |
| 5 | 5 | <import addon="script.module.requests" version="2.25.1"/> |
| … | … | @@ -51,7 +51,19 @@ CHANGELOG_PATH = translate_path(f"special://home/addons/{ADDON_ID}/CHANGELOG.md" |
| 51 | 51 | # |
| 52 | 52 | # Served by the kodi-repo unit on stortron, which tracks the addon repo from the |
| 53 | 53 | # pear-git forge; the last hop is plain HTTP because that is all Kodi speaks. |
| 54 | UPDATE_HOST = "http://192.168.50.38:7221" | |
| 54 | # | |
| 55 | # The NAME, not an address. This was 192.168.50.38 until 2026-09-04, and that | |
| 56 | # address stopped existing on 2026-09-02: stortron ran two interfaces, the WiFi | |
| 57 | # carrying .38 was disconnected for good, and another host on the LAN had | |
| 58 | # already been seen claiming it. Self-update had been quietly dead ever since -- | |
| 59 | # quietly because a Kodi that cannot reach its update server simply never offers | |
| 60 | # one. The stremio addon registration was re-pointed at the name for the same | |
| 61 | # reason; this was missed because nothing fails loudly when it breaks. | |
| 62 | # | |
| 63 | # The Pi resolves it: `getent ahostsv4 stortron.local` -> 192.168.50.215. Note | |
| 64 | # `getent hosts` returns only a link-local IPv6 address and looks like a reason | |
| 65 | # to avoid the name -- it is not the lookup path that gets used. | |
| 66 | UPDATE_HOST = "http://stortron.local:7221" | |
| 55 | 67 | BASE_REPO_URL = f"{UPDATE_HOST}/packages" |
| 56 | 68 | BASE_ZIP_URL = f"{UPDATE_HOST}/zips" |
| 57 | 69 |
| … | … | @@ -156,3 +156,36 @@ def test_update_addon_installs_valid_downgrade(monkeypatch, tmp_path): |
| 156 | 156 | ) |
| 157 | 157 | assert not os.path.exists(os.path.join(str(packages_dir), f"{updater.ADDON_ID}-backup")) |
| 158 | 158 | assert not os.path.exists(os.path.join(str(packages_dir), f"{updater.ADDON_ID}-staging")) |
| 159 | ||
| 160 | ||
| 161 | class TestUpdateHostSurvivesDHCP: | |
| 162 | """UPDATE_HOST was a bare IP, and that IP went away. | |
| 163 | ||
| 164 | stortron ran two interfaces; on 2026-09-02 the WiFi carrying | |
| 165 | 192.168.50.38 was disconnected for good and another host on the LAN had | |
| 166 | already been seen claiming the address. Everything pinned to it broke | |
| 167 | silently -- the stremio addon registration, Jellyfin, and this, which had | |
| 168 | been unable to reach its own update server ever since. | |
| 169 | ||
| 170 | The name is not cosmetic. The Pi's getaddrinfo resolves stortron.local to | |
| 171 | 192.168.50.215 (`getent hosts` shows only a link-local v6 address and is the | |
| 172 | wrong thing to check), so the name follows the lease and a bare IP cannot. | |
| 173 | """ | |
| 174 | ||
| 175 | def test_the_update_host_is_not_a_bare_ip(self): | |
| 176 | import re | |
| 177 | ||
| 178 | from lib import updater | |
| 179 | ||
| 180 | host = updater.UPDATE_HOST | |
| 181 | assert not re.match(r"^https?://\d+\.\d+\.\d+\.\d+", host), ( | |
| 182 | f"UPDATE_HOST is pinned to a literal IP ({host}). It has to be a " | |
| 183 | "name, or the next DHCP change silently kills self-update again." | |
| 184 | ) | |
| 185 | ||
| 186 | def test_the_derived_urls_all_hang_off_it(self): | |
| 187 | from lib import updater | |
| 188 | ||
| 189 | for url in (updater.BASE_REPO_URL, updater.BASE_ZIP_URL, | |
| 190 | updater.VERSION_FILE, updater.CHANGELOG_FILE): | |
| 191 | assert url.startswith(updater.UPDATE_HOST), url |