OverviewFilesCommitsIssuesDocsReleasesPeers

kodi-jacktook commits / 5714094

Open adaptive items once — the double open killed the audio (1.15.7)
play_video() called setResolvedUrl() and then Player().play() for every non-plugin URL, so an inputstream.adaptive item was opened twice, ~0.7s apart. The second open interrupted the first partway through opening the audio codec: Opening stream: 1002 ... Finding audio codec for: 86018 (no CDVDAudioCodecFFmpeg::Open line -- the aac decoder never opens) inputstream.adaptive: Manifest successfully parsed <- second open Video and seek worked, there was simply no sound. Measured on the Pi: two "Creating Demuxer" and two "Manifest successfully parsed" per play through Jacktook, against exactly one for the same stream driven from a .strm -- which is why the .strm test had working audio and the real client did not. Another reminder that a .strm test is not a Jacktook test. setResolvedUrl already hands Kodi the list_item carrying the ISA properties, so the extra play() is redundant there. It is skipped only when ISA is engaged; every other source keeps the existing behaviour, including the plugin:// case whose comment explains why play() must not be called on top of setResolvedUrl. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
5714094727d0822ad6828cdddc966fa522390e59
by plankatron <fleeky@gmail.com> · 2026-08-11T00:02:35.000Z

Changed files (3) +28 -4

plugin.video.jacktook/addon.xml +1 -1
……@@ -1,5 +1,5 @@
11<?xml version="1.0" encoding="utf-8"?>
2 <addon id="plugin.video.jacktook" name="Jacktook" version="1.15.6" provider-name="Sam-Max">
2 <addon id="plugin.video.jacktook" name="Jacktook" version="1.15.7" provider-name="Sam-Max">
33 <requires>
44 <import addon="xbmc.python" version="3.00.0"/>
55 <import addon="script.module.requests" version="2.25.1"/>
plugin.video.jacktook/lib/player.py +13 -3
……@@ -91,6 +91,7 @@ class JacktookPLayer(xbmc.Player):
9191 # URL without the `|header` suffix, since ISA takes headers via its
9292 # own properties instead.
9393 adaptive_url = set_adaptive_stream(self.list_item, self.data)
94 self._adaptive = bool(adaptive_url)
9495 if adaptive_url:
9596 self.url = adaptive_url
9697 self.PLAYLIST.add(self.url, self.list_item)
……@@ -172,11 +173,20 @@ class JacktookPLayer(xbmc.Player):
172173 # busy dialog, crashing Kodi with "Logic error due to two
173174 # concurrent busydialogs".
174175 is_plugin = self.url.startswith("plugin://")
176 # An inputstream.adaptive item must be opened ONCE. setResolvedUrl
177 # already hands Kodi this list_item with the ISA properties on it,
178 # so the extra Player().play() opens the SAME stream a second time
179 # ~0.7s later and interrupts the first open midway through
180 # "Finding audio codec" -- the aac decoder never opens and there is
181 # no sound, while video and seek work fine. Measured 2026-08-10:
182 # two "Creating Demuxer" + two "Manifest successfully parsed" per
183 # play, and the successful-open line present only when opened once.
184 double_open = not is_plugin and not getattr(self, "_adaptive", False)
175185 kodilog(
176 f"[PLAYER] play_video: is_plugin={is_plugin}, "
177 f"calling {'setResolvedUrl only' if is_plugin else 'setResolvedUrl + Player().play()'}"
186 f"[PLAYER] play_video: is_plugin={is_plugin}, adaptive={getattr(self, '_adaptive', False)}, "
187 f"calling {'setResolvedUrl + Player().play()' if double_open else 'setResolvedUrl only'}"
178188 )
179 if not is_plugin:
189 if double_open:
180190 self.play(self.url, list_item)
181191 self.monitor()
182192 kodilog("[PLAYER] play_video: monitor() returned")
plugin.video.jacktook/tests/unit/test_adaptive_stream.py +14 -0
……@@ -114,3 +114,17 @@ def test_a_fixed_variant_url_still_does_not_opt_in():
114114 item = MagicMock()
115115 assert set_adaptive_stream(item, {"url": "http://h/a.m3u8?h=1080"}) is None
116116 item.setProperty.assert_not_called()
117
118
119def test_adaptive_items_are_opened_once():
120 """Regression, 2026-08-10: setResolvedUrl AND Player().play() opened the
121 same ISA stream twice ~0.7s apart. The second open interrupted the first
122 mid-'Finding audio codec', the aac decoder never opened and there was no
123 sound, while video and seek worked fine. An adaptive item must be handed
124 over once, by setResolvedUrl alone."""
125 import os
126 src = open(os.path.join(os.path.dirname(os.path.abspath(__file__)),
127 '..', '..', 'lib', 'player.py')).read()
128 assert 'double_open = not is_plugin and not getattr(self, "_adaptive", False)' in src
129 assert 'if double_open:' in src
130 assert 'self._adaptive = bool(adaptive_url)' in src