[heise] Support videos embedded in any article.
[youtube-dl] / youtube_dl / extractor / heise.py
1 # coding: utf-8
2 from __future__ import unicode_literals
3
4 from .common import InfoExtractor
5 from ..utils import (
6     determine_ext,
7     int_or_none,
8     parse_iso8601,
9 )
10
11
12 class HeiseIE(InfoExtractor):
13     _VALID_URL = r'''(?x)
14         https?://(?:www\.)?heise\.de/.+?(?P<id>[0-9]+)\.html(?:$|[?#])
15     '''
16     _TESTS = [
17         {
18             'url': (
19                 'http://www.heise.de/video/artikel/Podcast-c-t-uplink-3-3-Owncloud-Tastaturen-Peilsender-Smartphone-2404147.html'
20             ),
21             'md5': 'ffed432483e922e88545ad9f2f15d30e',
22             'info_dict': {
23                 'id': '2404147',
24                 'ext': 'mp4',
25                 'title': (
26                     "Podcast: c't uplink 3.3 – Owncloud / Tastaturen / Peilsender Smartphone"
27                 ),
28                 'format_id': 'mp4_720p',
29                 'timestamp': 1411812600,
30                 'upload_date': '20140927',
31                 'description': 'In uplink-Episode 3.3 geht es darum, wie man sich von Cloud-Anbietern emanzipieren kann, worauf man beim Kauf einer Tastatur achten sollte und was Smartphones über uns verraten.',
32                 'thumbnail': r're:^https?://.*/gallery/$',
33             }
34         },
35         {
36             'url': (
37                 'http://www.heise.de/ct/artikel/c-t-uplink-3-3-Owncloud-Tastaturen-Peilsender-Smartphone-2403911.html'
38             ),
39             'md5': 'ffed432483e922e88545ad9f2f15d30e',
40             'info_dict': {
41                 'id': '2403911',
42                 'ext': 'mp4',
43                 'title': (
44                     "c't uplink 3.3: Owncloud, Tastaturen, Peilsender Smartphone"
45                 ),
46                 'format_id': 'mp4_720p',
47                 'timestamp': 1411803000,
48                 'upload_date': '20140927',
49                 'description': "In c't uplink erklären wir in dieser Woche, wie man mit Owncloud die Kontrolle über die eigenen Daten behält. Darüber hinaus erklären wir, dass zur Wahl der richtigen Tastatur mehr gehört, als man denkt und wie Smartphones uns weiter verraten.",
50                 'thumbnail': r're:^https?://.*/gallery/$',
51             }
52         },
53         {
54             'url': (
55                 'http://www.heise.de/newsticker/meldung/c-t-uplink-Owncloud-Tastaturen-Peilsender-Smartphone-2404251.html?wt_mc=rss.ho.beitrag.atom'
56             ),
57             'md5': 'ffed432483e922e88545ad9f2f15d30e',
58             'info_dict': {
59                 'id': '2404251',
60                 'ext': 'mp4',
61                 'title': (
62                     "c't uplink: Owncloud, Tastaturen, Peilsender Smartphone"
63                 ),
64                 'format_id': 'mp4_720p',
65                 'timestamp': 1411811400,
66                 'upload_date': '20140927',
67                 'description': 'In uplink-Episode 3.3 sprechen wir über Owncloud und wie man sich damit von Cloudanbietern emanzipieren kann. Außerdem erklären wir, woran man alles beim Kauf einer Tastatur denken sollte und was Smartphones nun über uns verraten.',
68                 'thumbnail': r're:^https?://.*/gallery/$',
69             }
70         },
71         {
72             'url': (
73                 'http://www.heise.de/ct/ausgabe/2016-12-Spiele-3214137.html'
74             ),
75             'md5': '0616c9297d9c989f9b2a23b483b408c3',
76             'info_dict': {
77                 'id': '3214137',
78                 'ext': 'mp4',
79                 'title': (
80                     "c\u2019t zockt \u201eGlitchspace\u201c, \u201eThe Mind's Eclipse\u201c und \u201eWindowframe\u201c."
81                 ),
82                 'format_id': 'mp4_720p',
83                 'timestamp': 1464011220,
84                 'upload_date': '20160523',
85                 'description': "Unsere Spiele-Tipps der Woche: Das Puzzle-Adventure Glitchspace, das Jump&Run-Spiel Windowframe und The Mind's Eclipse",
86                 'thumbnail': r're:^https?://.*/gallery/$',
87             }
88         },
89
90     ]
91
92     def _real_extract(self, url):
93         video_id = self._match_id(url)
94         webpage = self._download_webpage(url, video_id)
95
96         container_id = self._search_regex(
97             r'<div class="videoplayerjw"[^>]*data-container="([0-9]+)"',
98             webpage, 'container ID')
99         sequenz_id = self._search_regex(
100             r'<div class="videoplayerjw"[^>]*data-sequenz="([0-9]+)"',
101             webpage, 'sequenz ID')
102         data_url = 'http://www.heise.de/videout/feed?container=%s&sequenz=%s' % (container_id, sequenz_id)
103         doc = self._download_xml(data_url, video_id)
104
105         info = {
106             'id': video_id,
107             'thumbnail': doc.find('.//{http://rss.jwpcdn.com/}image').text,
108             'timestamp': parse_iso8601(
109                 self._html_search_meta('date', webpage))
110         }
111
112         title = self._html_search_meta('fulltitle', webpage, default=None)
113         if not title or title == "c't":
114             title = self._search_regex(
115                 r'<div class="videoplayerjw"[^>]*data-title="([^"]+)"',
116                 webpage, 'video title')
117         info['title'] = title
118
119         desc = self._og_search_description(webpage, default=None)
120         if not desc:
121             desc = self._html_search_meta('description', webpage)
122         info['description'] = desc
123
124         formats = []
125         for source_node in doc.findall('.//{http://rss.jwpcdn.com/}source'):
126             label = source_node.attrib['label']
127             height = int_or_none(self._search_regex(
128                 r'^(.*?_)?([0-9]+)p$', label, 'height', default=None))
129             video_url = source_node.attrib['file']
130             ext = determine_ext(video_url, '')
131             formats.append({
132                 'url': video_url,
133                 'format_note': label,
134                 'format_id': '%s_%s' % (ext, label),
135                 'height': height,
136             })
137         self._sort_formats(formats)
138         info['formats'] = formats
139
140         return info