aboutsummaryrefslogtreecommitdiff
path: root/youtube_dl/extractor/jwplatform.py
blob: 2a499bb771602c381c27f1cdbce5701b432b80c6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# coding: utf-8
from __future__ import unicode_literals

import re

from .common import InfoExtractor
from ..compat import compat_urlparse
from ..utils import (
    determine_ext,
    float_or_none,
    int_or_none,
    mimetype2ext,
)


class JWPlatformBaseIE(InfoExtractor):
    @staticmethod
    def _find_jwplayer_data(webpage):
        # TODO: Merge this with JWPlayer-related codes in generic.py

        mobj = re.search(
            'jwplayer\((?P<quote>[\'"])[^\'" ]+(?P=quote)\)\.setup\((?P<options>[^)]+)\)',
            webpage)
        if mobj:
            return mobj.group('options')

    def _extract_jwplayer_data(self, webpage, video_id, *args, **kwargs):
        jwplayer_data = self._parse_json(
            self._find_jwplayer_data(webpage), video_id)
        return self._parse_jwplayer_data(
            jwplayer_data, video_id, *args, **kwargs)

    def _parse_jwplayer_data(self, jwplayer_data, video_id, require_title=True, m3u8_id=None, rtmp_params=None, base_url=None):
        # JWPlayer backward compatibility: flattened playlists
        # https://github.com/jwplayer/jwplayer/blob/v7.4.3/src/js/api/config.js#L81-L96
        if 'playlist' not in jwplayer_data:
            jwplayer_data = {'playlist': [jwplayer_data]}

        entries = []
        for video_data in jwplayer_data['playlist']:
            # JWPlayer backward compatibility: flattened sources
            # https://github.com/jwplayer/jwplayer/blob/v7.4.3/src/js/playlist/item.js#L29-L35
            if 'sources' not in video_data:
                video_data['sources'] = [video_data]

            formats = []
            for source in video_data['sources']:
                source_url = self._proto_relative_url(source['file'])
                if base_url:
                    source_url = compat_urlparse.urljoin(base_url, source_url)
                source_type = source.get('type') or ''
                ext = mimetype2ext(source_type) or determine_ext(source_url)
                if source_type == 'hls' or ext == 'm3u8':
                    formats.extend(self._extract_m3u8_formats(
                        source_url, video_id, 'mp4', 'm3u8_native', m3u8_id=m3u8_id, fatal=False))
                # https://github.com/jwplayer/jwplayer/blob/master/src/js/providers/default.js#L67
                elif source_type.startswith('audio') or ext in ('oga', 'aac', 'mp3', 'mpeg', 'vorbis'):
                    formats.append({
                        'url': source_url,
                        'vcodec': 'none',
                        'ext': ext,
                    })
                else:
                    a_format = {
                        'url': source_url,
                        'width': int_or_none(source.get('width')),
                        'height': int_or_none(source.get('height')),
                        'ext': ext,
                    }
                    if source_url.startswith('rtmp'):
                        a_format['ext'] = 'flv',

                        # See com/longtailvideo/jwplayer/media/RTMPMediaProvider.as
                        # of jwplayer.flash.swf
                        rtmp_url_parts = re.split(
                            r'((?:mp4|mp3|flv):)', source_url, 1)
                        if len(rtmp_url_parts) == 3:
                            rtmp_url, prefix, play_path = rtmp_url_parts
                            a_format.update({
                                'url': rtmp_url,
                                'play_path': prefix + play_path,
                            })
                        if rtmp_params:
                            a_format.update(rtmp_params)
                    formats.append(a_format)
            self._sort_formats(formats)

            subtitles = {}
            tracks = video_data.get('tracks')
            if tracks and isinstance(tracks, list):
                for track in tracks:
                    if track.get('file') and track.get('kind') == 'captions':
                        subtitles.setdefault(track.get('label') or 'en', []).append({
                            'url': self._proto_relative_url(track['file'])
                        })

            entries.append({
                'id': video_id,
                'title': video_data['title'] if require_title else video_data.get('title'),
                'description': video_data.get('description'),
                'thumbnail': self._proto_relative_url(video_data.get('image')),
                'timestamp': int_or_none(video_data.get('pubdate')),
                'duration': float_or_none(jwplayer_data.get('duration')),
                'subtitles': subtitles,
                'formats': formats,
            })
        if len(entries) == 1:
            return entries[0]
        else:
            return self.playlist_result(entries)


class JWPlatformIE(JWPlatformBaseIE):
    _VALID_URL = r'(?:https?://content\.jwplatform\.com/(?:feeds|players|jw6)/|jwplatform:)(?P<id>[a-zA-Z0-9]{8})'
    _TEST = {
        'url': 'http://content.jwplatform.com/players/nPripu9l-ALJ3XQCI.js',
        'md5': 'fa8899fa601eb7c83a64e9d568bdf325',
        'info_dict': {
            'id': 'nPripu9l',
            'ext': 'mov',
            'title': 'Big Buck Bunny Trailer',
            'description': 'Big Buck Bunny is a short animated film by the Blender Institute. It is made using free and open source software.',
            'upload_date': '20081127',
            'timestamp': 1227796140,
        }
    }

    @staticmethod
    def _extract_url(webpage):
        mobj = re.search(
            r'<script[^>]+?src=["\'](?P<url>(?:https?:)?//content.jwplatform.com/players/[a-zA-Z0-9]{8})',
            webpage)
        if mobj:
            return mobj.group('url')

    def _real_extract(self, url):
        video_id = self._match_id(url)
        json_data = self._download_json('http://content.jwplatform.com/feeds/%s.json' % video_id, video_id)
        return self._parse_jwplayer_data(json_data, video_id)