Skip to content

Commit 35a5ede

Browse files
Fix subtitles (#43)
1 parent a241bf5 commit 35a5ede

File tree

3 files changed

+48
-3
lines changed

3 files changed

+48
-3
lines changed

resources/lib/kodiutils.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,8 @@ def play(stream, title=None, art_dict=None, info_dict=None, prop_dict=None, stre
240240
play_item.setProperties(prop_dict)
241241
if stream_dict:
242242
play_item.addStreamInfo('video', stream_dict)
243+
if stream.subtitles:
244+
play_item.setSubtitles(stream.subtitles)
243245

244246
# Setup Inputstream Adaptive
245247
if kodi_version_major() >= 19:

resources/lib/play/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,23 @@
1111
class ResolvedStream:
1212
""" Defines a stream that we can play"""
1313

14-
def __init__(self, uuid=None, url=None, stream_type=None, license_url=None, license_headers=None, license_keys=None):
14+
def __init__(self, uuid=None, url=None, stream_type=None, license_url=None, license_headers=None, license_keys=None, subtitles=None):
1515
"""
1616
:type uuid: str
1717
:type url: str
1818
:type stream_type: str
1919
:type license_url: str
2020
:type license_headers: str
2121
:type license_keys: dict
22+
:type subtitles: list[str]
2223
"""
2324
self.uuid = uuid
2425
self.url = url
2526
self.stream_type = stream_type
2627
self.license_url = license_url
2728
self.license_headers = license_headers
2829
self.license_keys = license_keys
30+
self.subtitles = subtitles
2931

3032
def __repr__(self):
3133
return "%r" % self.__dict__

resources/lib/play/content.py

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,7 @@ def get_stream(self, uuid: str, content_type: str) -> ResolvedStream:
330330

331331
manifest_urls = data.get('manifestUrls') or {}
332332
manifest_url = None
333+
subtitle_url = None
333334
stream_type = None
334335

335336
# Manifest URLs (DASH or HLS)
@@ -344,11 +345,12 @@ def get_stream(self, uuid: str, content_type: str) -> ResolvedStream:
344345
elif data.get('adType') == 'SSAI' and data.get('ssai'):
345346
ssai = data['ssai']
346347
ssai_url = (
347-
f"https://pubads.g.doubleclick.net/ondemand/dash/content/"
348-
f"{ssai.get('contentSourceID')}/vid/{ssai.get('videoID')}/streams"
348+
f'https://pubads.g.doubleclick.net/ondemand/dash/content/'
349+
f'{ssai.get('contentSourceID')}/vid/{ssai.get('videoID')}/streams'
349350
)
350351
ad_data = json.loads(utils.post_url(ssai_url, data=''))
351352
manifest_url = ad_data.get('stream_manifest')
353+
subtitle_url = self.extract_subtitle_from_manifest(manifest_url)
352354
stream_type = STREAM_DASH
353355

354356
if not manifest_url or not stream_type:
@@ -380,8 +382,47 @@ def get_stream(self, uuid: str, content_type: str) -> ResolvedStream:
380382
license_url=self.LICENSE_URL,
381383
license_headers=license_headers,
382384
license_keys=license_keys,
385+
subtitles=[subtitle_url],
383386
)
384387

388+
def extract_subtitle_from_manifest(self, manifest_url):
389+
"""Extract subtitle URL from a DASH manifest"""
390+
from xml.etree.ElementTree import fromstring
391+
392+
manifest_data = utils.get_url(manifest_url)
393+
manifest = fromstring(manifest_data)
394+
395+
ns = {'mpd': 'urn:mpeg:dash:schema:mpd:2011'}
396+
397+
base_url_el = manifest.find('mpd:BaseURL', ns)
398+
if base_url_el is None or not base_url_el.text:
399+
return None
400+
401+
base_url = base_url_el.text
402+
403+
for adaption in manifest.iterfind(
404+
'.//mpd:AdaptationSet[@contentType="text"]', ns
405+
):
406+
rep = adaption.find('mpd:Representation', ns)
407+
if rep is None:
408+
continue
409+
410+
sub_base = rep.find('mpd:BaseURL', ns)
411+
if sub_base is None or not sub_base.text:
412+
continue
413+
414+
sub_path = sub_base.text
415+
if 'T888' not in sub_path:
416+
continue
417+
418+
# Strip period-specific suffix safely
419+
name, _, ext = sub_path.rpartition('.')
420+
clean_path = name.rsplit('_', 1)[0] + '.' + ext
421+
422+
return base_url + clean_path
423+
424+
return None
425+
385426
def get_program_tree(self):
386427
""" Get a content tree with information about all the programs.
387428
:rtype list[Program]

0 commit comments

Comments
 (0)