]> gitweb @ CieloNegro.org - youtube-dl.git/blob - youtube_dl/extractor/bbcnews.py
Support BBC News (bbc.com/news)
[youtube-dl.git] / youtube_dl / extractor / bbcnews.py
1 from __future__ import unicode_literals
2
3 from .common import InfoExtractor
4 from ..utils import (
5     ExtractorError,
6     int_or_none,
7 )
8 from ..compat import compat_HTTPError
9 import re
10 from .bbccouk import BBCCoUkIE
11
12 class BBCNewsIE(BBCCoUkIE):
13     IE_NAME = 'bbc.com'
14     IE_DESC = 'BBC news'
15     _VALID_URL = r'https?://(?:www\.)?(?:bbc\.co\.uk|bbc\.com)/news/(?P<id>[^/]+)'
16
17     _TESTS = [{
18         'url': 'http://www.bbc.com/news/world-europe-32668511',
19         'info_dict': {
20             'id': 'world-europe-32668511',
21             'title': 'Russia stages massive WW2 parade despite Western boycott',
22         },
23         'playlist_count': 2,
24     },{
25         'url': 'http://www.bbc.com/news/business-28299555',
26         'info_dict': {
27             'id': 'business-28299555',
28             'title': 'Farnborough Airshow: Video highlights',
29         },
30         'playlist_count': 9,
31     },{
32         'url': 'http://www.bbc.com/news/world-europe-32041533',
33         'note': 'Video',
34         'info_dict': {
35             'id': 'p02mprgb',
36             'ext': 'mp4',
37             'title': 'Aerial footage showed the site of the crash in the Alps - courtesy BFM TV',
38             'description': 'Germanwings plane crash site in aerial video - Aerial footage showed the site of the crash in the Alps - courtesy BFM TV',
39             'duration': 47,
40         },
41         'params': {
42             'skip_download': True,
43         }
44     }]
45
46     def _duration_str2int(self, str):
47         if not str:
48             return None
49         ret = re.match(r'^\d+$', str)
50         if ret:
51             return int(ret.group(0))
52         ret = re.match(r'PT((?P<h>\d+)H)?((?P<m>\d+)M)?(?P<s>\d+)S$', str)
53         if ret:
54             total=int(ret.group('s'))
55             if ret.group('m'):
56                 total+=(int(ret.group('m'))*60)
57             if ret.group('h'):
58                 total+=(int(ret.group('h'))*3600)
59             return total
60         return None
61
62     def _download_media_selector(self, programme_id):
63         # bbc news uses http://open.live.bbc.co.uk/mediaselector/4/mtis/stream/ not
64         # http://open.live.bbc.co.uk/mediaselector/5/select/version/2.0/mediaset/pc/vpid/
65         # Could add third urlspec arg to BBCCoUkIE._download_media_selector instead of duplicating it
66
67         try:
68             media_selection = self._download_xml(
69                'http://open.live.bbc.co.uk/mediaselector/4/mtis/stream/%s' % programme_id,
70                 programme_id, 'Downloading media selection XML')
71         except ExtractorError as ee:
72             if isinstance(ee.cause, compat_HTTPError) and ee.cause.code == 403:
73                 media_selection = xml.etree.ElementTree.fromstring(ee.cause.read().encode('utf-8'))
74             else:
75                 raise
76         formats = []
77         subtitles = None
78
79         for media in self._extract_medias(media_selection):
80             kind = media.get('kind')
81             if kind == 'audio':
82                 formats.extend(self._extract_audio(media, programme_id))
83             elif kind == 'video':
84                 formats.extend(self._extract_video(media, programme_id))
85             elif kind == 'captions':
86                 subtitles = self.extract_subtitles(media, programme_id)
87
88         formats = []
89         subtitles = None
90
91         for media in self._extract_medias(media_selection):
92             kind = media.get('kind')
93             if kind == 'audio':
94                 formats.extend(self._extract_audio(media, programme_id))
95             elif kind == 'video':
96                 formats.extend(self._extract_video(media, programme_id))
97             elif kind == 'captions':
98                 subtitles = self.extract_subtitles(media, programme_id)
99
100         return formats, subtitles
101
102     def _real_extract(self, url):
103         list_id = self._match_id(url)
104         webpage = self._download_webpage(url, list_id)
105
106         list_title = self._html_search_regex(r'<title>(.*?)(?:\s*-\s*BBC News)?</title>', webpage, 'list title')
107
108         pubdate = self._html_search_regex(r'"datePublished":\s*"(\d+-\d+-\d+)', webpage, 'date', default=None)
109         if pubdate:
110            pubdate = pubdate.replace('-','')
111
112         ret = []
113         # works with bbc.com/news/something-something-123456 articles
114         matches = re.findall(r"data-media-meta='({[^']+})'", webpage)
115         if not matches:
116            # stubbornly generic extractor for {json with "image":{allvideoshavethis},etc}
117            # in http://www.bbc.com/news/video_and_audio/international
118            matches = re.findall(r"({[^{}]+image\":{[^}]+}[^}]+})", webpage)
119         if not matches:
120            raise ExtractorError('No video found', expected=True)
121
122         for ent in matches:
123             jent = self._parse_json(ent,list_id)
124
125             programme_id = jent.get('externalId',None)
126             xml_url = jent.get('href', None)
127
128             title = jent['caption']
129             duration = self._duration_str2int(jent.get('duration',None))
130             description = list_title + ' - ' + jent.get('caption','')
131             thumbnail = None
132             if jent.has_key('image'):
133                thumbnail=jent['image'].get('href',None)
134
135             if programme_id:
136                formats, subtitles = self._download_media_selector(programme_id)
137             elif xml_url:
138                # Cheap fallback
139                # http://playlists.bbc.co.uk/news/(list_id)[ABC..]/playlist.sxml
140                xml = self._download_webpage(xml_url, programme_id, 'Downloading playlist.sxml for externalId (fallback)')
141                programme_id = self._search_regex(r'<mediator [^>]*identifier="(.+?)"', xml, 'playlist.sxml (externalId fallback)')
142                formats, subtitles = self._download_media_selector(programme_id)
143             else:
144                raise ExtractorError('data-media-meta entry has no externalId or href value.')
145                
146             self._sort_formats(formats)
147
148             ret.append( {
149                 'id': programme_id,
150                 'uploader': 'BBC News',
151                 'upload_date': pubdate,
152                 'title': title,
153                 'description': description,
154                 'thumbnail': thumbnail,
155                 'duration': duration,
156                 'formats': formats,
157                 'subtitles': subtitles,
158             } )
159
160         if len(ret) > 0:
161            return self.playlist_result(ret, list_id, list_title)
162         raise ExtractorError('No video found', expected=True)