Merge pull request #8374 from yan12125/facebook-dash
Facebook DASH formats
This commit is contained in:
		
						commit
						08411970d5
					
				
					 3 changed files with 152 additions and 111 deletions
				
			
		| 
						 | 
				
			
			@ -1330,6 +1330,83 @@ class InfoExtractor(object):
 | 
			
		|||
            })
 | 
			
		||||
        return entries
 | 
			
		||||
 | 
			
		||||
    def _download_dash_manifest(self, dash_manifest_url, video_id, fatal=True):
 | 
			
		||||
        return self._download_xml(
 | 
			
		||||
            dash_manifest_url, video_id,
 | 
			
		||||
            note='Downloading DASH manifest',
 | 
			
		||||
            errnote='Could not download DASH manifest',
 | 
			
		||||
            fatal=fatal)
 | 
			
		||||
 | 
			
		||||
    def _extract_dash_manifest_formats(self, dash_manifest_url, video_id, fatal=True, namespace=None, formats_dict={}):
 | 
			
		||||
        dash_doc = self._download_dash_manifest(dash_manifest_url, video_id, fatal)
 | 
			
		||||
        if dash_doc is False:
 | 
			
		||||
            return []
 | 
			
		||||
 | 
			
		||||
        return self._parse_dash_manifest(
 | 
			
		||||
            dash_doc, namespace=namespace, formats_dict=formats_dict)
 | 
			
		||||
 | 
			
		||||
    def _parse_dash_manifest(self, dash_doc, namespace=None, formats_dict={}):
 | 
			
		||||
        def _add_ns(path):
 | 
			
		||||
            return self._xpath_ns(path, namespace)
 | 
			
		||||
 | 
			
		||||
        formats = []
 | 
			
		||||
        for a in dash_doc.findall('.//' + _add_ns('AdaptationSet')):
 | 
			
		||||
            mime_type = a.attrib.get('mimeType')
 | 
			
		||||
            for r in a.findall(_add_ns('Representation')):
 | 
			
		||||
                mime_type = r.attrib.get('mimeType') or mime_type
 | 
			
		||||
                url_el = r.find(_add_ns('BaseURL'))
 | 
			
		||||
                if mime_type == 'text/vtt':
 | 
			
		||||
                    # TODO implement WebVTT downloading
 | 
			
		||||
                    pass
 | 
			
		||||
                elif mime_type.startswith('audio/') or mime_type.startswith('video/'):
 | 
			
		||||
                    segment_list = r.find(_add_ns('SegmentList'))
 | 
			
		||||
                    format_id = r.attrib['id']
 | 
			
		||||
                    video_url = url_el.text if url_el is not None else None
 | 
			
		||||
                    filesize = int_or_none(url_el.attrib.get('{http://youtube.com/yt/2012/10/10}contentLength') if url_el is not None else None)
 | 
			
		||||
                    f = {
 | 
			
		||||
                        'format_id': format_id,
 | 
			
		||||
                        'url': video_url,
 | 
			
		||||
                        'width': int_or_none(r.attrib.get('width')),
 | 
			
		||||
                        'height': int_or_none(r.attrib.get('height')),
 | 
			
		||||
                        'tbr': int_or_none(r.attrib.get('bandwidth'), 1000),
 | 
			
		||||
                        'asr': int_or_none(r.attrib.get('audioSamplingRate')),
 | 
			
		||||
                        'filesize': filesize,
 | 
			
		||||
                        'fps': int_or_none(r.attrib.get('frameRate')),
 | 
			
		||||
                    }
 | 
			
		||||
                    if segment_list is not None:
 | 
			
		||||
                        initialization_url = segment_list.find(_add_ns('Initialization')).attrib['sourceURL']
 | 
			
		||||
                        f.update({
 | 
			
		||||
                            'initialization_url': initialization_url,
 | 
			
		||||
                            'segment_urls': [segment.attrib.get('media') for segment in segment_list.findall(_add_ns('SegmentURL'))],
 | 
			
		||||
                            'protocol': 'http_dash_segments',
 | 
			
		||||
                        })
 | 
			
		||||
                        if not f.get('url'):
 | 
			
		||||
                            f['url'] = initialization_url
 | 
			
		||||
                    try:
 | 
			
		||||
                        existing_format = next(
 | 
			
		||||
                            fo for fo in formats
 | 
			
		||||
                            if fo['format_id'] == format_id)
 | 
			
		||||
                    except StopIteration:
 | 
			
		||||
                        full_info = formats_dict.get(format_id, {}).copy()
 | 
			
		||||
                        full_info.update(f)
 | 
			
		||||
                        codecs = r.attrib.get('codecs')
 | 
			
		||||
                        if codecs:
 | 
			
		||||
                            if mime_type.startswith('video/'):
 | 
			
		||||
                                vcodec, acodec = codecs, 'none'
 | 
			
		||||
                            else:  # mime_type.startswith('audio/')
 | 
			
		||||
                                vcodec, acodec = 'none', codecs
 | 
			
		||||
 | 
			
		||||
                            full_info.update({
 | 
			
		||||
                                'vcodec': vcodec,
 | 
			
		||||
                                'acodec': acodec,
 | 
			
		||||
                            })
 | 
			
		||||
                        formats.append(full_info)
 | 
			
		||||
                    else:
 | 
			
		||||
                        existing_format.update(f)
 | 
			
		||||
                else:
 | 
			
		||||
                    self.report_warning('Unknown MIME type %s in DASH manifest' % mime_type)
 | 
			
		||||
        return formats
 | 
			
		||||
 | 
			
		||||
    def _live_title(self, name):
 | 
			
		||||
        """ Generate the title for a live video """
 | 
			
		||||
        now = datetime.datetime.now()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,9 +6,11 @@ import socket
 | 
			
		|||
 | 
			
		||||
from .common import InfoExtractor
 | 
			
		||||
from ..compat import (
 | 
			
		||||
    compat_etree_fromstring,
 | 
			
		||||
    compat_http_client,
 | 
			
		||||
    compat_urllib_error,
 | 
			
		||||
    compat_urllib_parse_unquote,
 | 
			
		||||
    compat_urllib_parse_unquote_plus,
 | 
			
		||||
)
 | 
			
		||||
from ..utils import (
 | 
			
		||||
    error_to_compat_str,
 | 
			
		||||
| 
						 | 
				
			
			@ -44,6 +46,9 @@ class FacebookIE(InfoExtractor):
 | 
			
		|||
    _CHECKPOINT_URL = 'https://www.facebook.com/checkpoint/?next=http%3A%2F%2Ffacebook.com%2Fhome.php&_fb_noscript=1'
 | 
			
		||||
    _NETRC_MACHINE = 'facebook'
 | 
			
		||||
    IE_NAME = 'facebook'
 | 
			
		||||
 | 
			
		||||
    _CHROME_USER_AGENT = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.97 Safari/537.36'
 | 
			
		||||
 | 
			
		||||
    _TESTS = [{
 | 
			
		||||
        'url': 'https://www.facebook.com/video.php?v=637842556329505&fref=nf',
 | 
			
		||||
        'md5': '6a40d33c0eccbb1af76cf0485a052659',
 | 
			
		||||
| 
						 | 
				
			
			@ -65,6 +70,16 @@ class FacebookIE(InfoExtractor):
 | 
			
		|||
        'expected_warnings': [
 | 
			
		||||
            'title'
 | 
			
		||||
        ]
 | 
			
		||||
    }, {
 | 
			
		||||
        'note': 'Video with DASH manifest',
 | 
			
		||||
        'url': 'https://www.facebook.com/video.php?v=957955867617029',
 | 
			
		||||
        'md5': '54706e4db4f5ad58fbad82dde1f1213f',
 | 
			
		||||
        'info_dict': {
 | 
			
		||||
            'id': '957955867617029',
 | 
			
		||||
            'ext': 'mp4',
 | 
			
		||||
            'title': 'When you post epic content on instagram.com/433 8 million followers, this is ...',
 | 
			
		||||
            'uploader': 'Demy de Zeeuw',
 | 
			
		||||
        },
 | 
			
		||||
    }, {
 | 
			
		||||
        'url': 'https://www.facebook.com/video.php?v=10204634152394104',
 | 
			
		||||
        'only_matching': True,
 | 
			
		||||
| 
						 | 
				
			
			@ -147,8 +162,9 @@ class FacebookIE(InfoExtractor):
 | 
			
		|||
 | 
			
		||||
    def _real_extract(self, url):
 | 
			
		||||
        video_id = self._match_id(url)
 | 
			
		||||
        url = 'https://www.facebook.com/video/video.php?v=%s' % video_id
 | 
			
		||||
        webpage = self._download_webpage(url, video_id)
 | 
			
		||||
        req = sanitized_Request('https://www.facebook.com/video/video.php?v=%s' % video_id)
 | 
			
		||||
        req.add_header('User-Agent', self._CHROME_USER_AGENT)
 | 
			
		||||
        webpage = self._download_webpage(req, video_id)
 | 
			
		||||
 | 
			
		||||
        video_data = None
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -197,9 +213,16 @@ class FacebookIE(InfoExtractor):
 | 
			
		|||
                            'url': src,
 | 
			
		||||
                            'preference': -10 if format_id == 'progressive' else 0,
 | 
			
		||||
                        })
 | 
			
		||||
            dash_manifest = f[0].get('dash_manifest')
 | 
			
		||||
            if dash_manifest:
 | 
			
		||||
                formats.extend(self._parse_dash_manifest(
 | 
			
		||||
                    compat_etree_fromstring(compat_urllib_parse_unquote_plus(dash_manifest)),
 | 
			
		||||
                    namespace='urn:mpeg:dash:schema:mpd:2011'))
 | 
			
		||||
        if not formats:
 | 
			
		||||
            raise ExtractorError('Cannot find video formats')
 | 
			
		||||
 | 
			
		||||
        self._sort_formats(formats)
 | 
			
		||||
 | 
			
		||||
        video_title = self._html_search_regex(
 | 
			
		||||
            r'<h2\s+[^>]*class="uiHeaderTitle"[^>]*>([^<]*)</h2>', webpage, 'title',
 | 
			
		||||
            default=None)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -316,55 +316,55 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
 | 
			
		|||
        '151': {'ext': 'mp4', 'height': 72, 'format_note': 'HLS', 'acodec': 'aac', 'abr': 24, 'vcodec': 'h264', 'preference': -10},
 | 
			
		||||
 | 
			
		||||
        # DASH mp4 video
 | 
			
		||||
        '133': {'ext': 'mp4', 'height': 240, 'format_note': 'DASH video', 'acodec': 'none', 'vcodec': 'h264', 'preference': -40},
 | 
			
		||||
        '134': {'ext': 'mp4', 'height': 360, 'format_note': 'DASH video', 'acodec': 'none', 'vcodec': 'h264', 'preference': -40},
 | 
			
		||||
        '135': {'ext': 'mp4', 'height': 480, 'format_note': 'DASH video', 'acodec': 'none', 'vcodec': 'h264', 'preference': -40},
 | 
			
		||||
        '136': {'ext': 'mp4', 'height': 720, 'format_note': 'DASH video', 'acodec': 'none', 'vcodec': 'h264', 'preference': -40},
 | 
			
		||||
        '137': {'ext': 'mp4', 'height': 1080, 'format_note': 'DASH video', 'acodec': 'none', 'vcodec': 'h264', 'preference': -40},
 | 
			
		||||
        '138': {'ext': 'mp4', 'format_note': 'DASH video', 'acodec': 'none', 'vcodec': 'h264', 'preference': -40},  # Height can vary (https://github.com/rg3/youtube-dl/issues/4559)
 | 
			
		||||
        '160': {'ext': 'mp4', 'height': 144, 'format_note': 'DASH video', 'acodec': 'none', 'vcodec': 'h264', 'preference': -40},
 | 
			
		||||
        '264': {'ext': 'mp4', 'height': 1440, 'format_note': 'DASH video', 'acodec': 'none', 'vcodec': 'h264', 'preference': -40},
 | 
			
		||||
        '298': {'ext': 'mp4', 'height': 720, 'format_note': 'DASH video', 'acodec': 'none', 'vcodec': 'h264', 'fps': 60, 'preference': -40},
 | 
			
		||||
        '299': {'ext': 'mp4', 'height': 1080, 'format_note': 'DASH video', 'acodec': 'none', 'vcodec': 'h264', 'fps': 60, 'preference': -40},
 | 
			
		||||
        '266': {'ext': 'mp4', 'height': 2160, 'format_note': 'DASH video', 'acodec': 'none', 'vcodec': 'h264', 'preference': -40},
 | 
			
		||||
        '133': {'ext': 'mp4', 'height': 240, 'format_note': 'DASH video', 'vcodec': 'h264', 'preference': -40},
 | 
			
		||||
        '134': {'ext': 'mp4', 'height': 360, 'format_note': 'DASH video', 'vcodec': 'h264', 'preference': -40},
 | 
			
		||||
        '135': {'ext': 'mp4', 'height': 480, 'format_note': 'DASH video', 'vcodec': 'h264', 'preference': -40},
 | 
			
		||||
        '136': {'ext': 'mp4', 'height': 720, 'format_note': 'DASH video', 'vcodec': 'h264', 'preference': -40},
 | 
			
		||||
        '137': {'ext': 'mp4', 'height': 1080, 'format_note': 'DASH video', 'vcodec': 'h264', 'preference': -40},
 | 
			
		||||
        '138': {'ext': 'mp4', 'format_note': 'DASH video', 'vcodec': 'h264', 'preference': -40},  # Height can vary (https://github.com/rg3/youtube-dl/issues/4559)
 | 
			
		||||
        '160': {'ext': 'mp4', 'height': 144, 'format_note': 'DASH video', 'vcodec': 'h264', 'preference': -40},
 | 
			
		||||
        '264': {'ext': 'mp4', 'height': 1440, 'format_note': 'DASH video', 'vcodec': 'h264', 'preference': -40},
 | 
			
		||||
        '298': {'ext': 'mp4', 'height': 720, 'format_note': 'DASH video', 'vcodec': 'h264', 'fps': 60, 'preference': -40},
 | 
			
		||||
        '299': {'ext': 'mp4', 'height': 1080, 'format_note': 'DASH video', 'vcodec': 'h264', 'fps': 60, 'preference': -40},
 | 
			
		||||
        '266': {'ext': 'mp4', 'height': 2160, 'format_note': 'DASH video', 'vcodec': 'h264', 'preference': -40},
 | 
			
		||||
 | 
			
		||||
        # Dash mp4 audio
 | 
			
		||||
        '139': {'ext': 'm4a', 'format_note': 'DASH audio', 'acodec': 'aac', 'vcodec': 'none', 'abr': 48, 'preference': -50, 'container': 'm4a_dash'},
 | 
			
		||||
        '140': {'ext': 'm4a', 'format_note': 'DASH audio', 'acodec': 'aac', 'vcodec': 'none', 'abr': 128, 'preference': -50, 'container': 'm4a_dash'},
 | 
			
		||||
        '141': {'ext': 'm4a', 'format_note': 'DASH audio', 'acodec': 'aac', 'vcodec': 'none', 'abr': 256, 'preference': -50, 'container': 'm4a_dash'},
 | 
			
		||||
        '139': {'ext': 'm4a', 'format_note': 'DASH audio', 'acodec': 'aac', 'abr': 48, 'preference': -50, 'container': 'm4a_dash'},
 | 
			
		||||
        '140': {'ext': 'm4a', 'format_note': 'DASH audio', 'acodec': 'aac', 'abr': 128, 'preference': -50, 'container': 'm4a_dash'},
 | 
			
		||||
        '141': {'ext': 'm4a', 'format_note': 'DASH audio', 'acodec': 'aac', 'abr': 256, 'preference': -50, 'container': 'm4a_dash'},
 | 
			
		||||
 | 
			
		||||
        # Dash webm
 | 
			
		||||
        '167': {'ext': 'webm', 'height': 360, 'width': 640, 'format_note': 'DASH video', 'acodec': 'none', 'container': 'webm', 'vcodec': 'vp8', 'preference': -40},
 | 
			
		||||
        '168': {'ext': 'webm', 'height': 480, 'width': 854, 'format_note': 'DASH video', 'acodec': 'none', 'container': 'webm', 'vcodec': 'vp8', 'preference': -40},
 | 
			
		||||
        '169': {'ext': 'webm', 'height': 720, 'width': 1280, 'format_note': 'DASH video', 'acodec': 'none', 'container': 'webm', 'vcodec': 'vp8', 'preference': -40},
 | 
			
		||||
        '170': {'ext': 'webm', 'height': 1080, 'width': 1920, 'format_note': 'DASH video', 'acodec': 'none', 'container': 'webm', 'vcodec': 'vp8', 'preference': -40},
 | 
			
		||||
        '218': {'ext': 'webm', 'height': 480, 'width': 854, 'format_note': 'DASH video', 'acodec': 'none', 'container': 'webm', 'vcodec': 'vp8', 'preference': -40},
 | 
			
		||||
        '219': {'ext': 'webm', 'height': 480, 'width': 854, 'format_note': 'DASH video', 'acodec': 'none', 'container': 'webm', 'vcodec': 'vp8', 'preference': -40},
 | 
			
		||||
        '278': {'ext': 'webm', 'height': 144, 'format_note': 'DASH video', 'acodec': 'none', 'container': 'webm', 'vcodec': 'vp9', 'preference': -40},
 | 
			
		||||
        '242': {'ext': 'webm', 'height': 240, 'format_note': 'DASH video', 'acodec': 'none', 'vcodec': 'vp9', 'preference': -40},
 | 
			
		||||
        '243': {'ext': 'webm', 'height': 360, 'format_note': 'DASH video', 'acodec': 'none', 'vcodec': 'vp9', 'preference': -40},
 | 
			
		||||
        '244': {'ext': 'webm', 'height': 480, 'format_note': 'DASH video', 'acodec': 'none', 'vcodec': 'vp9', 'preference': -40},
 | 
			
		||||
        '245': {'ext': 'webm', 'height': 480, 'format_note': 'DASH video', 'acodec': 'none', 'vcodec': 'vp9', 'preference': -40},
 | 
			
		||||
        '246': {'ext': 'webm', 'height': 480, 'format_note': 'DASH video', 'acodec': 'none', 'vcodec': 'vp9', 'preference': -40},
 | 
			
		||||
        '247': {'ext': 'webm', 'height': 720, 'format_note': 'DASH video', 'acodec': 'none', 'vcodec': 'vp9', 'preference': -40},
 | 
			
		||||
        '248': {'ext': 'webm', 'height': 1080, 'format_note': 'DASH video', 'acodec': 'none', 'vcodec': 'vp9', 'preference': -40},
 | 
			
		||||
        '271': {'ext': 'webm', 'height': 1440, 'format_note': 'DASH video', 'acodec': 'none', 'vcodec': 'vp9', 'preference': -40},
 | 
			
		||||
        '167': {'ext': 'webm', 'height': 360, 'width': 640, 'format_note': 'DASH video', 'container': 'webm', 'vcodec': 'vp8', 'preference': -40},
 | 
			
		||||
        '168': {'ext': 'webm', 'height': 480, 'width': 854, 'format_note': 'DASH video', 'container': 'webm', 'vcodec': 'vp8', 'preference': -40},
 | 
			
		||||
        '169': {'ext': 'webm', 'height': 720, 'width': 1280, 'format_note': 'DASH video', 'container': 'webm', 'vcodec': 'vp8', 'preference': -40},
 | 
			
		||||
        '170': {'ext': 'webm', 'height': 1080, 'width': 1920, 'format_note': 'DASH video', 'container': 'webm', 'vcodec': 'vp8', 'preference': -40},
 | 
			
		||||
        '218': {'ext': 'webm', 'height': 480, 'width': 854, 'format_note': 'DASH video', 'container': 'webm', 'vcodec': 'vp8', 'preference': -40},
 | 
			
		||||
        '219': {'ext': 'webm', 'height': 480, 'width': 854, 'format_note': 'DASH video', 'container': 'webm', 'vcodec': 'vp8', 'preference': -40},
 | 
			
		||||
        '278': {'ext': 'webm', 'height': 144, 'format_note': 'DASH video', 'container': 'webm', 'vcodec': 'vp9', 'preference': -40},
 | 
			
		||||
        '242': {'ext': 'webm', 'height': 240, 'format_note': 'DASH video', 'vcodec': 'vp9', 'preference': -40},
 | 
			
		||||
        '243': {'ext': 'webm', 'height': 360, 'format_note': 'DASH video', 'vcodec': 'vp9', 'preference': -40},
 | 
			
		||||
        '244': {'ext': 'webm', 'height': 480, 'format_note': 'DASH video', 'vcodec': 'vp9', 'preference': -40},
 | 
			
		||||
        '245': {'ext': 'webm', 'height': 480, 'format_note': 'DASH video', 'vcodec': 'vp9', 'preference': -40},
 | 
			
		||||
        '246': {'ext': 'webm', 'height': 480, 'format_note': 'DASH video', 'vcodec': 'vp9', 'preference': -40},
 | 
			
		||||
        '247': {'ext': 'webm', 'height': 720, 'format_note': 'DASH video', 'vcodec': 'vp9', 'preference': -40},
 | 
			
		||||
        '248': {'ext': 'webm', 'height': 1080, 'format_note': 'DASH video', 'vcodec': 'vp9', 'preference': -40},
 | 
			
		||||
        '271': {'ext': 'webm', 'height': 1440, 'format_note': 'DASH video', 'vcodec': 'vp9', 'preference': -40},
 | 
			
		||||
        # itag 272 videos are either 3840x2160 (e.g. RtoitU2A-3E) or 7680x4320 (sLprVF6d7Ug)
 | 
			
		||||
        '272': {'ext': 'webm', 'height': 2160, 'format_note': 'DASH video', 'acodec': 'none', 'vcodec': 'vp9', 'preference': -40},
 | 
			
		||||
        '302': {'ext': 'webm', 'height': 720, 'format_note': 'DASH video', 'acodec': 'none', 'vcodec': 'vp9', 'fps': 60, 'preference': -40},
 | 
			
		||||
        '303': {'ext': 'webm', 'height': 1080, 'format_note': 'DASH video', 'acodec': 'none', 'vcodec': 'vp9', 'fps': 60, 'preference': -40},
 | 
			
		||||
        '308': {'ext': 'webm', 'height': 1440, 'format_note': 'DASH video', 'acodec': 'none', 'vcodec': 'vp9', 'fps': 60, 'preference': -40},
 | 
			
		||||
        '313': {'ext': 'webm', 'height': 2160, 'format_note': 'DASH video', 'acodec': 'none', 'vcodec': 'vp9', 'preference': -40},
 | 
			
		||||
        '315': {'ext': 'webm', 'height': 2160, 'format_note': 'DASH video', 'acodec': 'none', 'vcodec': 'vp9', 'fps': 60, 'preference': -40},
 | 
			
		||||
        '272': {'ext': 'webm', 'height': 2160, 'format_note': 'DASH video', 'vcodec': 'vp9', 'preference': -40},
 | 
			
		||||
        '302': {'ext': 'webm', 'height': 720, 'format_note': 'DASH video', 'vcodec': 'vp9', 'fps': 60, 'preference': -40},
 | 
			
		||||
        '303': {'ext': 'webm', 'height': 1080, 'format_note': 'DASH video', 'vcodec': 'vp9', 'fps': 60, 'preference': -40},
 | 
			
		||||
        '308': {'ext': 'webm', 'height': 1440, 'format_note': 'DASH video', 'vcodec': 'vp9', 'fps': 60, 'preference': -40},
 | 
			
		||||
        '313': {'ext': 'webm', 'height': 2160, 'format_note': 'DASH video', 'vcodec': 'vp9', 'preference': -40},
 | 
			
		||||
        '315': {'ext': 'webm', 'height': 2160, 'format_note': 'DASH video', 'vcodec': 'vp9', 'fps': 60, 'preference': -40},
 | 
			
		||||
 | 
			
		||||
        # Dash webm audio
 | 
			
		||||
        '171': {'ext': 'webm', 'acodec': 'vorbis', 'vcodec': 'none', 'format_note': 'DASH audio', 'abr': 128, 'preference': -50},
 | 
			
		||||
        '172': {'ext': 'webm', 'acodec': 'vorbis', 'vcodec': 'none', 'format_note': 'DASH audio', 'abr': 256, 'preference': -50},
 | 
			
		||||
        '171': {'ext': 'webm', 'acodec': 'vorbis', 'format_note': 'DASH audio', 'abr': 128, 'preference': -50},
 | 
			
		||||
        '172': {'ext': 'webm', 'acodec': 'vorbis', 'format_note': 'DASH audio', 'abr': 256, 'preference': -50},
 | 
			
		||||
 | 
			
		||||
        # Dash webm audio with opus inside
 | 
			
		||||
        '249': {'ext': 'webm', 'vcodec': 'none', 'format_note': 'DASH audio', 'acodec': 'opus', 'abr': 50, 'preference': -50},
 | 
			
		||||
        '250': {'ext': 'webm', 'vcodec': 'none', 'format_note': 'DASH audio', 'acodec': 'opus', 'abr': 70, 'preference': -50},
 | 
			
		||||
        '251': {'ext': 'webm', 'vcodec': 'none', 'format_note': 'DASH audio', 'acodec': 'opus', 'abr': 160, 'preference': -50},
 | 
			
		||||
        '249': {'ext': 'webm', 'format_note': 'DASH audio', 'acodec': 'opus', 'abr': 50, 'preference': -50},
 | 
			
		||||
        '250': {'ext': 'webm', 'format_note': 'DASH audio', 'acodec': 'opus', 'abr': 70, 'preference': -50},
 | 
			
		||||
        '251': {'ext': 'webm', 'format_note': 'DASH audio', 'acodec': 'opus', 'abr': 160, 'preference': -50},
 | 
			
		||||
 | 
			
		||||
        # RTMP (unnamed)
 | 
			
		||||
        '_rtmp': {'protocol': 'rtmp'},
 | 
			
		||||
| 
						 | 
				
			
			@ -1035,73 +1035,6 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
 | 
			
		|||
        url = 'https://www.youtube.com/annotations_invideo?features=1&legacy=1&video_id=%s' % video_id
 | 
			
		||||
        return self._download_webpage(url, video_id, note='Searching for annotations.', errnote='Unable to download video annotations.')
 | 
			
		||||
 | 
			
		||||
    def _parse_dash_manifest(
 | 
			
		||||
            self, video_id, dash_manifest_url, player_url, age_gate, fatal=True):
 | 
			
		||||
        def decrypt_sig(mobj):
 | 
			
		||||
            s = mobj.group(1)
 | 
			
		||||
            dec_s = self._decrypt_signature(s, video_id, player_url, age_gate)
 | 
			
		||||
            return '/signature/%s' % dec_s
 | 
			
		||||
        dash_manifest_url = re.sub(r'/s/([a-fA-F0-9\.]+)', decrypt_sig, dash_manifest_url)
 | 
			
		||||
        dash_doc = self._download_xml(
 | 
			
		||||
            dash_manifest_url, video_id,
 | 
			
		||||
            note='Downloading DASH manifest',
 | 
			
		||||
            errnote='Could not download DASH manifest',
 | 
			
		||||
            fatal=fatal)
 | 
			
		||||
 | 
			
		||||
        if dash_doc is False:
 | 
			
		||||
            return []
 | 
			
		||||
 | 
			
		||||
        formats = []
 | 
			
		||||
        for a in dash_doc.findall('.//{urn:mpeg:DASH:schema:MPD:2011}AdaptationSet'):
 | 
			
		||||
            mime_type = a.attrib.get('mimeType')
 | 
			
		||||
            for r in a.findall('{urn:mpeg:DASH:schema:MPD:2011}Representation'):
 | 
			
		||||
                url_el = r.find('{urn:mpeg:DASH:schema:MPD:2011}BaseURL')
 | 
			
		||||
                if url_el is None:
 | 
			
		||||
                    continue
 | 
			
		||||
                if mime_type == 'text/vtt':
 | 
			
		||||
                    # TODO implement WebVTT downloading
 | 
			
		||||
                    pass
 | 
			
		||||
                elif mime_type.startswith('audio/') or mime_type.startswith('video/'):
 | 
			
		||||
                    segment_list = r.find('{urn:mpeg:DASH:schema:MPD:2011}SegmentList')
 | 
			
		||||
                    format_id = r.attrib['id']
 | 
			
		||||
                    video_url = url_el.text
 | 
			
		||||
                    filesize = int_or_none(url_el.attrib.get('{http://youtube.com/yt/2012/10/10}contentLength'))
 | 
			
		||||
                    f = {
 | 
			
		||||
                        'format_id': format_id,
 | 
			
		||||
                        'url': video_url,
 | 
			
		||||
                        'width': int_or_none(r.attrib.get('width')),
 | 
			
		||||
                        'height': int_or_none(r.attrib.get('height')),
 | 
			
		||||
                        'tbr': int_or_none(r.attrib.get('bandwidth'), 1000),
 | 
			
		||||
                        'asr': int_or_none(r.attrib.get('audioSamplingRate')),
 | 
			
		||||
                        'filesize': filesize,
 | 
			
		||||
                        'fps': int_or_none(r.attrib.get('frameRate')),
 | 
			
		||||
                    }
 | 
			
		||||
                    if segment_list is not None:
 | 
			
		||||
                        f.update({
 | 
			
		||||
                            'initialization_url': segment_list.find('{urn:mpeg:DASH:schema:MPD:2011}Initialization').attrib['sourceURL'],
 | 
			
		||||
                            'segment_urls': [segment.attrib.get('media') for segment in segment_list.findall('{urn:mpeg:DASH:schema:MPD:2011}SegmentURL')],
 | 
			
		||||
                            'protocol': 'http_dash_segments',
 | 
			
		||||
                        })
 | 
			
		||||
                    try:
 | 
			
		||||
                        existing_format = next(
 | 
			
		||||
                            fo for fo in formats
 | 
			
		||||
                            if fo['format_id'] == format_id)
 | 
			
		||||
                    except StopIteration:
 | 
			
		||||
                        full_info = self._formats.get(format_id, {}).copy()
 | 
			
		||||
                        full_info.update(f)
 | 
			
		||||
                        codecs = r.attrib.get('codecs')
 | 
			
		||||
                        if codecs:
 | 
			
		||||
                            if full_info.get('acodec') == 'none':
 | 
			
		||||
                                full_info['vcodec'] = codecs
 | 
			
		||||
                            elif full_info.get('vcodec') == 'none':
 | 
			
		||||
                                full_info['acodec'] = codecs
 | 
			
		||||
                        formats.append(full_info)
 | 
			
		||||
                    else:
 | 
			
		||||
                        existing_format.update(f)
 | 
			
		||||
                else:
 | 
			
		||||
                    self.report_warning('Unknown MIME type %s in DASH manifest' % mime_type)
 | 
			
		||||
        return formats
 | 
			
		||||
 | 
			
		||||
    def _real_extract(self, url):
 | 
			
		||||
        url, smuggled_data = unsmuggle_url(url, {})
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1533,8 +1466,16 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
 | 
			
		|||
            for dash_manifest_url in dash_mpds:
 | 
			
		||||
                dash_formats = {}
 | 
			
		||||
                try:
 | 
			
		||||
                    for df in self._parse_dash_manifest(
 | 
			
		||||
                            video_id, dash_manifest_url, player_url, age_gate, dash_mpd_fatal):
 | 
			
		||||
                    def decrypt_sig(mobj):
 | 
			
		||||
                        s = mobj.group(1)
 | 
			
		||||
                        dec_s = self._decrypt_signature(s, video_id, player_url, age_gate)
 | 
			
		||||
                        return '/signature/%s' % dec_s
 | 
			
		||||
 | 
			
		||||
                    dash_manifest_url = re.sub(r'/s/([a-fA-F0-9\.]+)', decrypt_sig, dash_manifest_url)
 | 
			
		||||
 | 
			
		||||
                    for df in self._extract_dash_manifest_formats(
 | 
			
		||||
                            dash_manifest_url, video_id, fatal=dash_mpd_fatal,
 | 
			
		||||
                            namespace='urn:mpeg:DASH:schema:MPD:2011', formats_dict=self._formats):
 | 
			
		||||
                        # Do not overwrite DASH format found in some previous DASH manifest
 | 
			
		||||
                        if df['format_id'] not in dash_formats:
 | 
			
		||||
                            dash_formats[df['format_id']] = df
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue