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