]> gitweb @ CieloNegro.org - youtube-dl.git/commitdiff
Merge branch 'master' of github.com:rg3/youtube-dl
authorPhilipp Hagemeister <phihag@phihag.de>
Thu, 22 Jan 2015 17:21:27 +0000 (18:21 +0100)
committerPhilipp Hagemeister <phihag@phihag.de>
Thu, 22 Jan 2015 17:21:27 +0000 (18:21 +0100)
1  2 
youtube_dl/extractor/twitch.py

index 017eff74210549d92c0538d554aec8500918d991,2891c4680cfd16d6bf7611a6d0f210f02e8c76d4..14314916a5fbee50d9a6617afa54ecb99fd44ae4
@@@ -6,6 -6,7 +6,7 @@@ import r
  
  from .common import InfoExtractor
  from ..compat import (
+     compat_str,
      compat_urllib_parse,
      compat_urllib_request,
  )
@@@ -16,9 -17,10 +17,10 @@@ from ..utils import 
  
  
  class TwitchBaseIE(InfoExtractor):
-     _VALID_URL_BASE = r'http://(?:www\.)?twitch\.tv'
+     _VALID_URL_BASE = r'https?://(?:www\.)?twitch\.tv'
  
      _API_BASE = 'https://api.twitch.tv'
+     _USHER_BASE = 'http://usher.twitch.tv'
      _LOGIN_URL = 'https://secure.twitch.tv/user/login'
  
      def _handle_error(self, response):
@@@ -148,17 -150,14 +150,17 @@@ class TwitchChapterIE(TwitchItemBaseIE)
      _ITEM_TYPE = 'chapter'
      _ITEM_SHORTCUT = 'c'
  
 -    _TEST = {
 +    _TESTS = [{
          'url': 'http://www.twitch.tv/acracingleague/c/5285812',
          'info_dict': {
              'id': 'c5285812',
              'title': 'ACRL Off Season - Sports Cars @ Nordschleife',
          },
          'playlist_mincount': 3,
 -    }
 +    }, {
 +        'url': 'http://www.twitch.tv/tsm_theoddone/c/2349361',
 +        'only_matching': True,
 +    }]
  
  
  class TwitchVodIE(TwitchItemBaseIE):
              '%s/api/vods/%s/access_token' % (self._API_BASE, item_id), item_id,
              'Downloading %s access token' % self._ITEM_TYPE)
          formats = self._extract_m3u8_formats(
-             'http://usher.twitch.tv/vod/%s?nauth=%s&nauthsig=%s'
-             % (item_id, access_token['token'], access_token['sig']),
+             '%s/vod/%s?nauth=%s&nauthsig=%s'
+             % (self._USHER_BASE, item_id, access_token['token'], access_token['sig']),
              item_id, 'mp4')
          info['formats'] = formats
          return info
@@@ -257,3 -256,92 +259,92 @@@ class TwitchPastBroadcastsIE(TwitchPlay
          },
          'playlist_mincount': 54,
      }
+ class TwitchStreamIE(TwitchBaseIE):
+     IE_NAME = 'twitch:stream'
+     _VALID_URL = r'%s/(?P<id>[^/]+)/?(?:\#.*)?$' % TwitchBaseIE._VALID_URL_BASE
+     _TEST = {
+         'url': 'http://www.twitch.tv/shroomztv',
+         'info_dict': {
+             'id': '12772022048',
+             'display_id': 'shroomztv',
+             'ext': 'mp4',
+             'title': 're:^ShroomzTV [0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}$',
+             'description': 'H1Z1 - lonewolfing with ShroomzTV | A3 Battle Royale later - @ShroomzTV',
+             'is_live': True,
+             'timestamp': 1421928037,
+             'upload_date': '20150122',
+             'uploader': 'ShroomzTV',
+             'uploader_id': 'shroomztv',
+             'view_count': int,
+         },
+         'params': {
+             # m3u8 download
+             'skip_download': True,
+         },
+     }
+     def _real_extract(self, url):
+         channel_id = self._match_id(url)
+         stream = self._download_json(
+             '%s/kraken/streams/%s' % (self._API_BASE, channel_id), channel_id,
+             'Downloading stream JSON').get('stream')
+         # Fallback on profile extraction if stream is offline
+         if not stream:
+             return self.url_result(
+                 'http://www.twitch.tv/%s/profile' % channel_id,
+                 'TwitchProfile', channel_id)
+         access_token = self._download_json(
+             '%s/api/channels/%s/access_token' % (self._API_BASE, channel_id), channel_id,
+             'Downloading channel access token')
+         query = {
+             'allow_source': 'true',
+             'p': '9386337',
+             'player': 'twitchweb',
+             'segment_preference': '4',
+             'sig': access_token['sig'],
+             'token': access_token['token'],
+         }
+         formats = self._extract_m3u8_formats(
+             '%s/api/channel/hls/%s.m3u8?%s'
+             % (self._USHER_BASE, channel_id, compat_urllib_parse.urlencode(query).encode('utf-8')),
+             channel_id, 'mp4')
+         view_count = stream.get('viewers')
+         timestamp = parse_iso8601(stream.get('created_at'))
+         channel = stream['channel']
+         title = self._live_title(channel.get('display_name') or channel.get('name'))
+         description = channel.get('status')
+         thumbnails = []
+         for thumbnail_key, thumbnail_url in stream['preview'].items():
+             m = re.search(r'(?P<width>\d+)x(?P<height>\d+)\.jpg$', thumbnail_key)
+             if not m:
+                 continue
+             thumbnails.append({
+                 'url': thumbnail_url,
+                 'width': int(m.group('width')),
+                 'height': int(m.group('height')),
+             })
+         return {
+             'id': compat_str(stream['_id']),
+             'display_id': channel_id,
+             'title': title,
+             'description': description,
+             'thumbnails': thumbnails,
+             'uploader': channel.get('display_name'),
+             'uploader_id': channel.get('name'),
+             'timestamp': timestamp,
+             'view_count': view_count,
+             'formats': formats,
+             'is_live': True,
+         }