diff --git a/tests/remote/unit/test_remote_service_helpers.py b/tests/remote/unit/test_remote_service_helpers.py index c8d176b8..a8f66687 100644 --- a/tests/remote/unit/test_remote_service_helpers.py +++ b/tests/remote/unit/test_remote_service_helpers.py @@ -675,3 +675,31 @@ def test_a_service_profile_option_travels_nested_and_keeps_the_credential_profil def test_no_dl_params_when_the_ctx_has_no_selection() -> None: assert "dl_params" not in _sent_create_data({}, {}) + + +def test_resolve_manifest_data_keeps_the_periods_the_server_filtered() -> None: + """A service drops periods (a mid-roll ad) on the server only. The client must skip the + same periods after it re-parses the manifest, or a remote download joins them back in.""" + from unshackle.core.api.handlers import extract_manifests + from unshackle.core.manifests import DASH + from unshackle.core.remote_service import resolve_manifest_data + + periods = "".join( + f""" + + """ + for pid in ("main1", "ad1", "main2") + ) + mpd = f"""https://x/{periods}""" + server = DASH.from_text(mpd, url="https://x/manifest.mpd").to_tracks(language="en") + for track in server: + track.data["dash"]["filtered_period_ids"] = ["ad1"] + + remote = deserialize_video(serialize_video_track(server.videos[0])) + tracks = build_tracks({"video": [], "audio": [], "subtitles": []}) + tracks.videos.append(remote) + resolve_manifest_data(tracks, extract_manifests(server)) + + assert remote.data["dash"]["filtered_period_ids"] == ["ad1"] diff --git a/unshackle/core/api/handlers.py b/unshackle/core/api/handlers.py index 382bc97a..8e4ec080 100644 --- a/unshackle/core/api/handlers.py +++ b/unshackle/core/api/handlers.py @@ -1543,6 +1543,7 @@ def extract_manifests(tracks: Tracks) -> List[Dict[str, Any]]: "type": "dash", "url": manifest_url, "data": base64.b64encode(compressed).decode("ascii"), + "filtered_period_ids": list(track.data["dash"].get("filtered_period_ids") or []), } ) elif track.data.get("ism") and track.data["ism"].get("manifest") is not None: diff --git a/unshackle/core/remote_service.py b/unshackle/core/remote_service.py index 2572206a..1d807fb9 100644 --- a/unshackle/core/remote_service.py +++ b/unshackle/core/remote_service.py @@ -556,7 +556,13 @@ def resolve_manifest_data(tracks: Tracks, manifests: list) -> None: from unshackle.core.manifests import DASH - local_tracks = DASH(etree.fromstring(raw), m_url).to_tracks(language=fallback_lang) + skip = manifest_info.get("filtered_period_ids") or [] + local_tracks = DASH(etree.fromstring(raw), m_url).to_tracks( + language=fallback_lang, period_filter=(lambda p: p.get("id") in skip) if skip else None + ) + if skip: + for local_track in local_tracks: + local_track.data["dash"]["filtered_period_ids"] = skip elif m_type == "ism": from lxml import etree