1from __future__ import unicode_literals
2
3
4from .common import InfoExtractor
5from ..compat import compat_str
6from ..utils import (
7    determine_ext,
8    int_or_none,
9    parse_iso8601,
10    parse_duration,
11)
12
13
14class NHLBaseIE(InfoExtractor):
15    def _real_extract(self, url):
16        site, tmp_id = self._match_valid_url(url).groups()
17        video_data = self._download_json(
18            'https://%s/%s/%sid/v1/%s/details/web-v1.json'
19            % (self._CONTENT_DOMAIN, site[:3], 'item/' if site == 'mlb' else '', tmp_id), tmp_id)
20        if video_data.get('type') != 'video':
21            video_data = video_data['media']
22            video = video_data.get('video')
23            if video:
24                video_data = video
25            else:
26                videos = video_data.get('videos')
27                if videos:
28                    video_data = videos[0]
29
30        video_id = compat_str(video_data['id'])
31        title = video_data['title']
32
33        formats = []
34        for playback in video_data.get('playbacks', []):
35            playback_url = playback.get('url')
36            if not playback_url:
37                continue
38            ext = determine_ext(playback_url)
39            if ext == 'm3u8':
40                m3u8_formats = self._extract_m3u8_formats(
41                    playback_url, video_id, 'mp4', 'm3u8_native',
42                    m3u8_id=playback.get('name', 'hls'), fatal=False)
43                self._check_formats(m3u8_formats, video_id)
44                formats.extend(m3u8_formats)
45            else:
46                height = int_or_none(playback.get('height'))
47                formats.append({
48                    'format_id': playback.get('name', 'http' + ('-%dp' % height if height else '')),
49                    'url': playback_url,
50                    'width': int_or_none(playback.get('width')),
51                    'height': height,
52                    'tbr': int_or_none(self._search_regex(r'_(\d+)[kK]', playback_url, 'bitrate', default=None)),
53                })
54        self._sort_formats(formats)
55
56        thumbnails = []
57        cuts = video_data.get('image', {}).get('cuts') or []
58        if isinstance(cuts, dict):
59            cuts = cuts.values()
60        for thumbnail_data in cuts:
61            thumbnail_url = thumbnail_data.get('src')
62            if not thumbnail_url:
63                continue
64            thumbnails.append({
65                'url': thumbnail_url,
66                'width': int_or_none(thumbnail_data.get('width')),
67                'height': int_or_none(thumbnail_data.get('height')),
68            })
69
70        return {
71            'id': video_id,
72            'title': title,
73            'description': video_data.get('description'),
74            'timestamp': parse_iso8601(video_data.get('date')),
75            'duration': parse_duration(video_data.get('duration')),
76            'thumbnails': thumbnails,
77            'formats': formats,
78        }
79
80
81class NHLIE(NHLBaseIE):
82    IE_NAME = 'nhl.com'
83    _VALID_URL = r'https?://(?:www\.)?(?P<site>nhl|wch2016)\.com/(?:[^/]+/)*c-(?P<id>\d+)'
84    _CONTENT_DOMAIN = 'nhl.bamcontent.com'
85    _TESTS = [{
86        # type=video
87        'url': 'https://www.nhl.com/video/anisimov-cleans-up-mess/t-277752844/c-43663503',
88        'md5': '0f7b9a8f986fb4b4eeeece9a56416eaf',
89        'info_dict': {
90            'id': '43663503',
91            'ext': 'mp4',
92            'title': 'Anisimov cleans up mess',
93            'description': 'md5:a02354acdfe900e940ce40706939ca63',
94            'timestamp': 1461288600,
95            'upload_date': '20160422',
96        },
97    }, {
98        # type=article
99        'url': 'https://www.nhl.com/news/dennis-wideman-suspended/c-278258934',
100        'md5': '1f39f4ea74c1394dea110699a25b366c',
101        'info_dict': {
102            'id': '40784403',
103            'ext': 'mp4',
104            'title': 'Wideman suspended by NHL',
105            'description': 'Flames defenseman Dennis Wideman was banned 20 games for violation of Rule 40 (Physical Abuse of Officials)',
106            'upload_date': '20160204',
107            'timestamp': 1454544904,
108        },
109    }, {
110        # Some m3u8 URLs are invalid (https://github.com/ytdl-org/youtube-dl/issues/10713)
111        'url': 'https://www.nhl.com/predators/video/poile-laviolette-on-subban-trade/t-277437416/c-44315003',
112        'md5': '50b2bb47f405121484dda3ccbea25459',
113        'info_dict': {
114            'id': '44315003',
115            'ext': 'mp4',
116            'title': 'Poile, Laviolette on Subban trade',
117            'description': 'General manager David Poile and head coach Peter Laviolette share their thoughts on acquiring P.K. Subban from Montreal (06/29/16)',
118            'timestamp': 1467242866,
119            'upload_date': '20160629',
120        },
121    }, {
122        'url': 'https://www.wch2016.com/video/caneur-best-of-game-2-micd-up/t-281230378/c-44983703',
123        'only_matching': True,
124    }, {
125        'url': 'https://www.wch2016.com/news/3-stars-team-europe-vs-team-canada/c-282195068',
126        'only_matching': True,
127    }]
128