| … | … | @@ -1,5 +1,5 @@ |
| 1 | 1 | <?xml version="1.0" encoding="utf-8"?> |
| 2 | <addon id="plugin.video.jacktook" name="Jacktook" version="1.15.7" provider-name="Sam-Max"> | |
| 2 | <addon id="plugin.video.jacktook" name="Jacktook" version="1.15.8" 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"/> |
| … | … | @@ -109,22 +109,23 @@ def set_adaptive_stream(list_item, data: Dict[str, Any]) -> Optional[str]: |
| 109 | 109 | if not kind: |
| 110 | 110 | return None |
| 111 | 111 | |
| 112 | # Exactly two properties, matching the .strm configuration that was | |
| 113 | # confirmed by ear to have working audio on this content. | |
| 114 | # | |
| 115 | # Deliberately NOT set, because that .strm did not set them and the version | |
| 116 | # that did was silent: | |
| 117 | # * mimetype -- ISA already knows the type from manifest_type. | |
| 118 | # * manifest_headers / stream_headers -- measured 2026-08-10, this CDN | |
| 119 | # serves master, variant AND segments with no headers at all (200/200/ | |
| 120 | # 206), so there is nothing for them to buy here. | |
| 121 | # Neither could be exonerated from the logs: audio opens and stalls once on | |
| 122 | # startup in every variant, so the log cannot distinguish audible from | |
| 123 | # silent. Rather than keep guessing, reproduce the known-good setup exactly. | |
| 124 | # If a future source genuinely needs headers, add them back behind that | |
| 125 | # source rather than for everything. | |
| 112 | 126 | list_item.setProperty("inputstream", "inputstream.adaptive") |
| 113 | 127 | list_item.setProperty("inputstream.adaptive.manifest_type", kind) |
| 114 | ||
| 115 | headers = data.get("proxyHeaders") or {} | |
| 116 | encoded = "&".join( | |
| 117 | f"{key}={quote(str(value), safe='')}" | |
| 118 | for key, value in headers.items() | |
| 119 | if key and value | |
| 120 | ) | |
| 121 | if encoded: | |
| 122 | list_item.setProperty("inputstream.adaptive.manifest_headers", encoded) | |
| 123 | list_item.setProperty("inputstream.adaptive.stream_headers", encoded) | |
| 124 | ||
| 125 | list_item.setMimeType(ADAPTIVE_MIMETYPES[kind]) | |
| 126 | list_item.setContentLookup(False) | |
| 127 | kodilog(f"[PLAYER] inputstream.adaptive engaged ({kind}), headers={bool(encoded)}") | |
| 128 | kodilog(f"[PLAYER] inputstream.adaptive engaged ({kind}), minimal props") | |
| 128 | 129 | return url.split("|", 1)[0] |
| 129 | 130 | |
| 130 | 131 |
| … | … | @@ -57,39 +57,6 @@ def test_strips_the_header_suffix_from_the_played_url(): |
| 57 | 57 | assert "|" not in url |
| 58 | 58 | |
| 59 | 59 | |
| 60 | def test_headers_move_to_isa_properties(): | |
| 61 | item = MagicMock() | |
| 62 | set_adaptive_stream( | |
| 63 | item, | |
| 64 | { | |
| 65 | "adaptive": True, | |
| 66 | "url": "http://h/a.m3u8", | |
| 67 | "proxyHeaders": {"Referer": "https://vidcore.net/", "User-Agent": "UA/1.0"}, | |
| 68 | }, | |
| 69 | ) | |
| 70 | props = dict(c.args for c in item.setProperty.call_args_list) | |
| 71 | for key in ("inputstream.adaptive.manifest_headers", "inputstream.adaptive.stream_headers"): | |
| 72 | assert "Referer=https%3A%2F%2Fvidcore.net%2F" in props[key] | |
| 73 | assert "User-Agent=UA%2F1.0" in props[key] | |
| 74 | ||
| 75 | ||
| 76 | def test_no_header_properties_when_there_are_none(): | |
| 77 | item = MagicMock() | |
| 78 | set_adaptive_stream(item, {"adaptive": True, "url": "http://h/a.m3u8"}) | |
| 79 | props = dict(c.args for c in item.setProperty.call_args_list) | |
| 80 | assert "inputstream.adaptive.manifest_headers" not in props | |
| 81 | ||
| 82 | ||
| 83 | def test_mimetype_is_set_per_manifest_kind(): | |
| 84 | for url, mime in ( | |
| 85 | ("http://h/a.m3u8", "application/vnd.apple.mpegurl"), | |
| 86 | ("http://h/a.mpd", "application/dash+xml"), | |
| 87 | ): | |
| 88 | item = MagicMock() | |
| 89 | set_adaptive_stream(item, {"adaptive": True, "url": url}) | |
| 90 | item.setMimeType.assert_called_once_with(mime) | |
| 91 | ||
| 92 | ||
| 93 | 60 | def test_isa_is_off_unless_a_source_opts_in(): |
| 94 | 61 | """Regression, 2026-08-10: engaging ISA for every .m3u8 captured the |
| 95 | 62 | single-variant rows too, and ISA stalls the audio on muxed HLS -- the aac |
| … | … | @@ -128,3 +95,13 @@ def test_adaptive_items_are_opened_once(): |
| 128 | 95 | assert 'double_open = not is_plugin and not getattr(self, "_adaptive", False)' in src |
| 129 | 96 | assert 'if double_open:' in src |
| 130 | 97 | assert 'self._adaptive = bool(adaptive_url)' in src |
| 98 | ||
| 99 | def test_only_the_two_known_good_properties_are_set(): | |
| 100 | """Audio was silent with mimetype and header properties set, and audible | |
| 101 | without them on the same stream. Keep the property set minimal.""" | |
| 102 | item = MagicMock() | |
| 103 | set_adaptive_stream(item, {"adaptive": True, "url": "http://h/a.m3u8", | |
| 104 | "proxyHeaders": {"Referer": "https://vidcore.net/"}}) | |
| 105 | props = dict(c.args for c in item.setProperty.call_args_list) | |
| 106 | assert set(props) == {"inputstream", "inputstream.adaptive.manifest_type"} | |
| 107 | item.setMimeType.assert_not_called() |