stream.dash: ContentProtection in Representation

This commit is contained in:
bastimeyer 2023-02-24 02:24:02 +01:00 committed by Forrest
parent 11de140658
commit e2e41987ae
3 changed files with 117 additions and 106 deletions

View File

@ -261,8 +261,10 @@ class DASHStream(Stream):
# Search for suitable video and audio representations # Search for suitable video and audio representations
for aset in mpd.periods[0].adaptationSets: for aset in mpd.periods[0].adaptationSets:
if aset.contentProtection: if aset.contentProtection:
raise PluginError("{} is protected by DRM".format(url)) raise PluginError(f"{url} is protected by DRM")
for rep in aset.representations: for rep in aset.representations:
if rep.contentProtection:
raise PluginError(f"{url} is protected by DRM")
if rep.mimeType.startswith("video"): if rep.mimeType.startswith("video"):
video.append(rep) video.append(rep)
elif rep.mimeType.startswith("audio"): elif rep.mimeType.startswith("audio"):

View File

@ -803,6 +803,7 @@ class Representation(MPDNode):
self.segmentBase = self.only_child(SegmentBase) self.segmentBase = self.only_child(SegmentBase)
self.segmentList = self.children(SegmentList) self.segmentList = self.children(SegmentList)
self.segmentTemplate = self.only_child(SegmentTemplate) self.segmentTemplate = self.only_child(SegmentTemplate)
self.contentProtection = self.children(ContentProtection)
@property @property
def bandwidth_rounded(self) -> float: def bandwidth_rounded(self) -> float:

View File

@ -18,15 +18,14 @@ class TestDASHStream(unittest.TestCase):
@patch("streamlink.stream.dash.MPD") @patch("streamlink.stream.dash.MPD")
def test_parse_manifest_video_only(self, mpdClass): def test_parse_manifest_video_only(self, mpdClass):
mpdClass.return_value = Mock(periods=[ adaptationset = Mock(
Mock(adaptationSets=[ contentProtection=None,
Mock(contentProtection=None, representations=[
representations=[ Mock(id="1", contentProtection=None, mimeType="video/mp4", height=720),
Mock(id=1, mimeType="video/mp4", height=720), Mock(id="2", contentProtection=None, mimeType="video/mp4", height=1080),
Mock(id=2, mimeType="video/mp4", height=1080), ],
]), )
]), mpdClass.return_value = Mock(periods=[Mock(adaptationSets=[adaptationset])])
])
streams = DASHStream.parse_manifest(self.session, self.test_url) streams = DASHStream.parse_manifest(self.session, self.test_url)
mpdClass.assert_called_with(ANY, base_url="http://test.bar", url="http://test.bar/foo.mpd") mpdClass.assert_called_with(ANY, base_url="http://test.bar", url="http://test.bar/foo.mpd")
@ -35,15 +34,14 @@ class TestDASHStream(unittest.TestCase):
@patch("streamlink.stream.dash.MPD") @patch("streamlink.stream.dash.MPD")
def test_parse_manifest_audio_only(self, mpdClass): def test_parse_manifest_audio_only(self, mpdClass):
mpdClass.return_value = Mock(periods=[ adaptationset = Mock(
Mock(adaptationSets=[ contentProtection=None,
Mock(contentProtection=None, representations=[
representations=[ Mock(id="1", contentProtection=None, mimeType="audio/mp4", bandwidth=128.0, lang="en"),
Mock(id=1, mimeType="audio/mp4", bandwidth=128.0, lang="en"), Mock(id="2", contentProtection=None, mimeType="audio/mp4", bandwidth=256.0, lang="en"),
Mock(id=2, mimeType="audio/mp4", bandwidth=256.0, lang="en"), ],
]), )
]), mpdClass.return_value = Mock(periods=[Mock(adaptationSets=[adaptationset])])
])
streams = DASHStream.parse_manifest(self.session, self.test_url) streams = DASHStream.parse_manifest(self.session, self.test_url)
mpdClass.assert_called_with(ANY, base_url="http://test.bar", url="http://test.bar/foo.mpd") mpdClass.assert_called_with(ANY, base_url="http://test.bar", url="http://test.bar/foo.mpd")
@ -52,16 +50,15 @@ class TestDASHStream(unittest.TestCase):
@patch("streamlink.stream.dash.MPD") @patch("streamlink.stream.dash.MPD")
def test_parse_manifest_audio_single(self, mpdClass): def test_parse_manifest_audio_single(self, mpdClass):
mpdClass.return_value = Mock(periods=[ adaptationset = Mock(
Mock(adaptationSets=[ contentProtection=None,
Mock(contentProtection=None, representations=[
representations=[ Mock(id="1", contentProtection=None, mimeType="video/mp4", height=720),
Mock(id=1, mimeType="video/mp4", height=720), Mock(id="2", contentProtection=None, mimeType="video/mp4", height=1080),
Mock(id=2, mimeType="video/mp4", height=1080), Mock(id="3", contentProtection=None, mimeType="audio/aac", bandwidth=128.0, lang="en"),
Mock(id=3, mimeType="audio/aac", bandwidth=128.0, lang="en"), ],
]), )
]), mpdClass.return_value = Mock(periods=[Mock(adaptationSets=[adaptationset])])
])
streams = DASHStream.parse_manifest(self.session, self.test_url) streams = DASHStream.parse_manifest(self.session, self.test_url)
mpdClass.assert_called_with(ANY, base_url="http://test.bar", url="http://test.bar/foo.mpd") mpdClass.assert_called_with(ANY, base_url="http://test.bar", url="http://test.bar/foo.mpd")
@ -70,17 +67,16 @@ class TestDASHStream(unittest.TestCase):
@patch("streamlink.stream.dash.MPD") @patch("streamlink.stream.dash.MPD")
def test_parse_manifest_audio_multi(self, mpdClass): def test_parse_manifest_audio_multi(self, mpdClass):
mpdClass.return_value = Mock(periods=[ adaptationset = Mock(
Mock(adaptationSets=[ contentProtection=None,
Mock(contentProtection=None, representations=[
representations=[ Mock(id="1", contentProtection=None, mimeType="video/mp4", height=720),
Mock(id=1, mimeType="video/mp4", height=720), Mock(id="2", contentProtection=None, mimeType="video/mp4", height=1080),
Mock(id=2, mimeType="video/mp4", height=1080), Mock(id="3", contentProtection=None, mimeType="audio/aac", bandwidth=128.0, lang="en"),
Mock(id=3, mimeType="audio/aac", bandwidth=128.0, lang="en"), Mock(id="4", contentProtection=None, mimeType="audio/aac", bandwidth=256.0, lang="en"),
Mock(id=4, mimeType="audio/aac", bandwidth=256.0, lang="en"), ],
]), )
]), mpdClass.return_value = Mock(periods=[Mock(adaptationSets=[adaptationset])])
])
streams = DASHStream.parse_manifest(self.session, self.test_url) streams = DASHStream.parse_manifest(self.session, self.test_url)
mpdClass.assert_called_with(ANY, base_url="http://test.bar", url="http://test.bar/foo.mpd") mpdClass.assert_called_with(ANY, base_url="http://test.bar", url="http://test.bar/foo.mpd")
@ -89,17 +85,16 @@ class TestDASHStream(unittest.TestCase):
@patch("streamlink.stream.dash.MPD") @patch("streamlink.stream.dash.MPD")
def test_parse_manifest_audio_multi_lang(self, mpdClass): def test_parse_manifest_audio_multi_lang(self, mpdClass):
mpdClass.return_value = Mock(periods=[ adaptationset = Mock(
Mock(adaptationSets=[ contentProtection=None,
Mock(contentProtection=None, representations=[
representations=[ Mock(id="1", contentProtection=None, mimeType="video/mp4", height=720),
Mock(id=1, mimeType="video/mp4", height=720), Mock(id="2", contentProtection=None, mimeType="video/mp4", height=1080),
Mock(id=2, mimeType="video/mp4", height=1080), Mock(id="3", contentProtection=None, mimeType="audio/aac", bandwidth=128.0, lang="en"),
Mock(id=3, mimeType="audio/aac", bandwidth=128.0, lang="en"), Mock(id="4", contentProtection=None, mimeType="audio/aac", bandwidth=128.0, lang="es"),
Mock(id=4, mimeType="audio/aac", bandwidth=128.0, lang="es"), ],
]), )
]), mpdClass.return_value = Mock(periods=[Mock(adaptationSets=[adaptationset])])
])
streams = DASHStream.parse_manifest(self.session, self.test_url) streams = DASHStream.parse_manifest(self.session, self.test_url)
mpdClass.assert_called_with(ANY, base_url="http://test.bar", url="http://test.bar/foo.mpd") mpdClass.assert_called_with(ANY, base_url="http://test.bar", url="http://test.bar/foo.mpd")
@ -111,17 +106,16 @@ class TestDASHStream(unittest.TestCase):
@patch("streamlink.stream.dash.MPD") @patch("streamlink.stream.dash.MPD")
def test_parse_manifest_audio_multi_lang_alpha3(self, mpdClass): def test_parse_manifest_audio_multi_lang_alpha3(self, mpdClass):
mpdClass.return_value = Mock(periods=[ adaptationset = Mock(
Mock(adaptationSets=[ contentProtection=None,
Mock(contentProtection=None, representations=[
representations=[ Mock(id="1", contentProtection=None, mimeType="video/mp4", height=720),
Mock(id=1, mimeType="video/mp4", height=720), Mock(id="2", contentProtection=None, mimeType="video/mp4", height=1080),
Mock(id=2, mimeType="video/mp4", height=1080), Mock(id="3", contentProtection=None, mimeType="audio/aac", bandwidth=128.0, lang="eng"),
Mock(id=3, mimeType="audio/aac", bandwidth=128.0, lang="eng"), Mock(id="4", contentProtection=None, mimeType="audio/aac", bandwidth=128.0, lang="spa"),
Mock(id=4, mimeType="audio/aac", bandwidth=128.0, lang="spa"), ],
]), )
]), mpdClass.return_value = Mock(periods=[Mock(adaptationSets=[adaptationset])])
])
streams = DASHStream.parse_manifest(self.session, self.test_url) streams = DASHStream.parse_manifest(self.session, self.test_url)
mpdClass.assert_called_with(ANY, base_url="http://test.bar", url="http://test.bar/foo.mpd") mpdClass.assert_called_with(ANY, base_url="http://test.bar", url="http://test.bar/foo.mpd")
@ -133,16 +127,15 @@ class TestDASHStream(unittest.TestCase):
@patch("streamlink.stream.dash.MPD") @patch("streamlink.stream.dash.MPD")
def test_parse_manifest_audio_invalid_lang(self, mpdClass): def test_parse_manifest_audio_invalid_lang(self, mpdClass):
mpdClass.return_value = Mock(periods=[ adaptationset = Mock(
Mock(adaptationSets=[ contentProtection=None,
Mock(contentProtection=None, representations=[
representations=[ Mock(id="1", contentProtection=None, mimeType="video/mp4", height=720),
Mock(id=1, mimeType="video/mp4", height=720), Mock(id="2", contentProtection=None, mimeType="video/mp4", height=1080),
Mock(id=2, mimeType="video/mp4", height=1080), Mock(id="3", contentProtection=None, mimeType="audio/aac", bandwidth=128.0, lang="en_no_voice"),
Mock(id=3, mimeType="audio/aac", bandwidth=128.0, lang="en_no_voice"), ],
]), )
]), mpdClass.return_value = Mock(periods=[Mock(adaptationSets=[adaptationset])])
])
streams = DASHStream.parse_manifest(self.session, self.test_url) streams = DASHStream.parse_manifest(self.session, self.test_url)
mpdClass.assert_called_with(ANY, base_url="http://test.bar", url="http://test.bar/foo.mpd") mpdClass.assert_called_with(ANY, base_url="http://test.bar", url="http://test.bar/foo.mpd")
@ -157,17 +150,16 @@ class TestDASHStream(unittest.TestCase):
self.session.localization.language.alpha2 = "es" self.session.localization.language.alpha2 = "es"
self.session.localization.explicit = True self.session.localization.explicit = True
mpdClass.return_value = Mock(periods=[ adaptationset = Mock(
Mock(adaptationSets=[ contentProtection=None,
Mock(contentProtection=None, representations=[
representations=[ Mock(id="1", contentProtection=None, mimeType="video/mp4", height=720),
Mock(id=1, mimeType="video/mp4", height=720), Mock(id="2", contentProtection=None, mimeType="video/mp4", height=1080),
Mock(id=2, mimeType="video/mp4", height=1080), Mock(id="3", contentProtection=None, mimeType="audio/aac", bandwidth=128.0, lang="en"),
Mock(id=3, mimeType="audio/aac", bandwidth=128.0, lang="en"), Mock(id="4", contentProtection=None, mimeType="audio/aac", bandwidth=128.0, lang="es"),
Mock(id=4, mimeType="audio/aac", bandwidth=128.0, lang="es"), ],
]), )
]), mpdClass.return_value = Mock(periods=[Mock(adaptationSets=[adaptationset])])
])
streams = DASHStream.parse_manifest(self.session, self.test_url) streams = DASHStream.parse_manifest(self.session, self.test_url)
mpdClass.assert_called_with(ANY, base_url="http://test.bar", url="http://test.bar/foo.mpd") mpdClass.assert_called_with(ANY, base_url="http://test.bar", url="http://test.bar/foo.mpd")
@ -178,8 +170,26 @@ class TestDASHStream(unittest.TestCase):
assert streams["1080p"].audio_representation.lang == "es" assert streams["1080p"].audio_representation.lang == "es"
@patch("streamlink.stream.dash.MPD") @patch("streamlink.stream.dash.MPD")
def test_parse_manifest_drm(self, mpdClass): def test_parse_manifest_drm_adaptationset(self, mpdClass):
mpdClass.return_value = Mock(periods=[Mock(adaptationSets=[Mock(contentProtection="DRM")])]) adaptationset = Mock(
contentProtection="DRM",
representations=[],
)
mpdClass.return_value = Mock(periods=[Mock(adaptationSets=[adaptationset])])
with pytest.raises(PluginError):
DASHStream.parse_manifest(self.session, self.test_url)
mpdClass.assert_called_with(ANY, base_url="http://test.bar", url="http://test.bar/foo.mpd")
@patch("streamlink.stream.dash.MPD")
def test_parse_manifest_drm_representation(self, mpdClass):
adaptationset = Mock(
contentProtection=None,
representations=[
Mock(id="1", contentProtection="DRM"),
],
)
mpdClass.return_value = Mock(periods=[Mock(adaptationSets=[adaptationset])])
with pytest.raises(PluginError): with pytest.raises(PluginError):
DASHStream.parse_manifest(self.session, self.test_url) DASHStream.parse_manifest(self.session, self.test_url)
@ -238,17 +248,16 @@ class TestDASHStream(unittest.TestCase):
""" """
Verify the fix for https://github.com/streamlink/streamlink/issues/3365 Verify the fix for https://github.com/streamlink/streamlink/issues/3365
""" """
mpdClass.return_value = Mock(periods=[ adaptationset = Mock(
Mock(adaptationSets=[ contentProtection=None,
Mock(contentProtection=None, representations=[
representations=[ Mock(id="1", contentProtection=None, mimeType="video/mp4", height=1080, bandwidth=128.0),
Mock(id=1, mimeType="video/mp4", height=1080, bandwidth=128.0), Mock(id="2", contentProtection=None, mimeType="video/mp4", height=1080, bandwidth=64.0),
Mock(id=2, mimeType="video/mp4", height=1080, bandwidth=64.0), Mock(id="3", contentProtection=None, mimeType="video/mp4", height=1080, bandwidth=32.0),
Mock(id=3, mimeType="video/mp4", height=1080, bandwidth=32.0), Mock(id="4", contentProtection=None, mimeType="video/mp4", height=720),
Mock(id=4, mimeType="video/mp4", height=720), ],
]), )
]), mpdClass.return_value = Mock(periods=[Mock(adaptationSets=[adaptationset])])
])
streams = DASHStream.parse_manifest(self.session, self.test_url) streams = DASHStream.parse_manifest(self.session, self.test_url)
mpdClass.assert_called_with(ANY, base_url="http://test.bar", url="http://test.bar/foo.mpd") mpdClass.assert_called_with(ANY, base_url="http://test.bar", url="http://test.bar/foo.mpd")
@ -260,16 +269,15 @@ class TestDASHStream(unittest.TestCase):
""" """
Verify the fix for https://github.com/streamlink/streamlink/issues/4217 Verify the fix for https://github.com/streamlink/streamlink/issues/4217
""" """
mpdClass.return_value = Mock(periods=[ adaptationset = Mock(
Mock(adaptationSets=[ contentProtection=None,
Mock(contentProtection=None, representations=[
representations=[ Mock(id="1", contentProtection=None, mimeType="video/mp4", height=1080, bandwidth=64.0),
Mock(id=1, mimeType="video/mp4", height=1080, bandwidth=64.0), Mock(id="2", contentProtection=None, mimeType="video/mp4", height=1080, bandwidth=128.0),
Mock(id=2, mimeType="video/mp4", height=1080, bandwidth=128.0), Mock(id="3", contentProtection=None, mimeType="video/mp4", height=1080, bandwidth=32.0),
Mock(id=3, mimeType="video/mp4", height=1080, bandwidth=32.0), ],
]), )
]), mpdClass.return_value = Mock(periods=[Mock(adaptationSets=[adaptationset])])
])
streams = DASHStream.parse_manifest(self.session, self.test_url) streams = DASHStream.parse_manifest(self.session, self.test_url)
mpdClass.assert_called_with(ANY, base_url="http://test.bar", url="http://test.bar/foo.mpd") mpdClass.assert_called_with(ANY, base_url="http://test.bar", url="http://test.bar/foo.mpd")