diff -Nru youtube-dl-2015.07.04/debian/changelog youtube-dl-2015.07.07/debian/changelog --- youtube-dl-2015.07.04/debian/changelog 2015-07-06 09:16:43.000000000 +0000 +++ youtube-dl-2015.07.07/debian/changelog 2015-07-07 10:00:15.000000000 +0000 @@ -1,3 +1,9 @@ +youtube-dl (2015.07.07-1~webupd8~precise0) precise; urgency=medium + + * New upstream release (automated upload) + + -- Alin Andrei Tue, 07 Jul 2015 13:00:15 +0300 + youtube-dl (2015.07.04-1~webupd8~precise0) precise; urgency=medium * New upstream release (automated upload) diff -Nru youtube-dl-2015.07.04/docs/supportedsites.md youtube-dl-2015.07.07/docs/supportedsites.md --- youtube-dl-2015.07.04/docs/supportedsites.md 2015-07-04 07:24:00.000000000 +0000 +++ youtube-dl-2015.07.07/docs/supportedsites.md 2015-07-07 08:36:07.000000000 +0000 @@ -384,6 +384,7 @@ - **Pyvideo** - **qqmusic** - **qqmusic:album** + - **qqmusic:playlist** - **qqmusic:singer** - **qqmusic:toplist** - **QuickVid** diff -Nru youtube-dl-2015.07.04/youtube_dl/extractor/common.py youtube-dl-2015.07.07/youtube_dl/extractor/common.py --- youtube-dl-2015.07.04/youtube_dl/extractor/common.py 2015-06-30 22:23:28.000000000 +0000 +++ youtube-dl-2015.07.07/youtube_dl/extractor/common.py 2015-07-07 08:36:00.000000000 +0000 @@ -846,7 +846,8 @@ def _extract_m3u8_formats(self, m3u8_url, video_id, ext=None, entry_protocol='m3u8', preference=None, - m3u8_id=None, note=None, errnote=None): + m3u8_id=None, note=None, errnote=None, + fatal=True): formats = [{ 'format_id': '-'.join(filter(None, [m3u8_id, 'meta'])), @@ -866,7 +867,10 @@ m3u8_doc = self._download_webpage( m3u8_url, video_id, note=note or 'Downloading m3u8 information', - errnote=errnote or 'Failed to download m3u8 information') + errnote=errnote or 'Failed to download m3u8 information', + fatal=fatal) + if m3u8_doc is False: + return m3u8_doc last_info = None last_media = None kv_rex = re.compile( diff -Nru youtube-dl-2015.07.04/youtube_dl/extractor/crunchyroll.py youtube-dl-2015.07.07/youtube_dl/extractor/crunchyroll.py --- youtube-dl-2015.07.04/youtube_dl/extractor/crunchyroll.py 2015-06-05 09:50:19.000000000 +0000 +++ youtube-dl-2015.07.07/youtube_dl/extractor/crunchyroll.py 2015-07-07 08:36:00.000000000 +0000 @@ -27,7 +27,7 @@ class CrunchyrollIE(InfoExtractor): - _VALID_URL = r'https?://(?:(?Pwww|m)\.)?(?Pcrunchyroll\.(?:com|fr)/(?:[^/]*/[^/?&]*?|media/\?id=)(?P[0-9]+))(?:[/?&]|$)' + _VALID_URL = r'https?://(?:(?Pwww|m)\.)?(?Pcrunchyroll\.(?:com|fr)/(?:media(?:-|/\?id=)|[^/]*/[^/?&]*?)(?P[0-9]+))(?:[/?&]|$)' _NETRC_MACHINE = 'crunchyroll' _TESTS = [{ 'url': 'http://www.crunchyroll.com/wanna-be-the-strongest-in-the-world/episode-1-an-idol-wrestler-is-born-645513', @@ -46,6 +46,22 @@ 'skip_download': True, }, }, { + 'url': 'http://www.crunchyroll.com/media-589804/culture-japan-1', + 'info_dict': { + 'id': '589804', + 'ext': 'flv', + 'title': 'Culture Japan Episode 1 – Rebuilding Japan after the 3.11', + 'description': 'md5:fe2743efedb49d279552926d0bd0cd9e', + 'thumbnail': 're:^https?://.*\.jpg$', + 'uploader': 'Danny Choo Network', + 'upload_date': '20120213', + }, + 'params': { + # rtmp + 'skip_download': True, + }, + + }, { 'url': 'http://www.crunchyroll.fr/girl-friend-beta/episode-11-goodbye-la-mode-661697', 'only_matching': True, }] @@ -251,16 +267,17 @@ for fmt in re.findall(r'showmedia\.([0-9]{3,4})p', webpage): stream_quality, stream_format = self._FORMAT_IDS[fmt] video_format = fmt + 'p' - streamdata_req = compat_urllib_request.Request('http://www.crunchyroll.com/xml/') - # urlencode doesn't work! - streamdata_req.data = 'req=RpcApiVideoEncode%5FGetStreamInfo&video%5Fencode%5Fquality=' + stream_quality + '&media%5Fid=' + stream_id + '&video%5Fformat=' + stream_format + streamdata_req = compat_urllib_request.Request( + 'http://www.crunchyroll.com/xml/?req=RpcApiVideoPlayer_GetStandardConfig&media_id=%s&video_format=%s&video_quality=%s' + % (stream_id, stream_format, stream_quality), + compat_urllib_parse.urlencode({'current_page': url}).encode('utf-8')) streamdata_req.add_header('Content-Type', 'application/x-www-form-urlencoded') - streamdata_req.add_header('Content-Length', str(len(streamdata_req.data))) streamdata = self._download_xml( streamdata_req, video_id, note='Downloading media info for %s' % video_format) - video_url = streamdata.find('./host').text - video_play_path = streamdata.find('./file').text + stream_info = streamdata.find('./{default}preload/stream_info') + video_url = stream_info.find('./host').text + video_play_path = stream_info.find('./file').text formats.append({ 'url': video_url, 'play_path': video_play_path, diff -Nru youtube-dl-2015.07.04/youtube_dl/extractor/gfycat.py youtube-dl-2015.07.07/youtube_dl/extractor/gfycat.py --- youtube-dl-2015.07.04/youtube_dl/extractor/gfycat.py 2015-06-05 09:50:19.000000000 +0000 +++ youtube-dl-2015.07.07/youtube_dl/extractor/gfycat.py 2015-07-07 08:36:00.000000000 +0000 @@ -6,12 +6,13 @@ int_or_none, float_or_none, qualities, + ExtractorError, ) class GfycatIE(InfoExtractor): - _VALID_URL = r'https?://(?:www\.)?gfycat\.com/(?P[^/?#]+)' - _TEST = { + _VALID_URL = r'https?://(?:www\.)?gfycat\.com/(?:ifr/)?(?P[^/?#]+)' + _TESTS = [{ 'url': 'http://gfycat.com/DeadlyDecisiveGermanpinscher', 'info_dict': { 'id': 'DeadlyDecisiveGermanpinscher', @@ -27,14 +28,33 @@ 'categories': list, 'age_limit': 0, } - } + }, { + 'url': 'http://gfycat.com/ifr/JauntyTimelyAmazontreeboa', + 'info_dict': { + 'id': 'JauntyTimelyAmazontreeboa', + 'ext': 'mp4', + 'title': 'JauntyTimelyAmazontreeboa', + 'timestamp': 1411720126, + 'upload_date': '20140926', + 'uploader': 'anonymous', + 'duration': 3.52, + 'view_count': int, + 'like_count': int, + 'dislike_count': int, + 'categories': list, + 'age_limit': 0, + } + }] def _real_extract(self, url): video_id = self._match_id(url) gfy = self._download_json( 'http://gfycat.com/cajax/get/%s' % video_id, - video_id, 'Downloading video info')['gfyItem'] + video_id, 'Downloading video info') + if 'error' in gfy: + raise ExtractorError('Gfycat said: ' + gfy['error'], expected=True) + gfy = gfy['gfyItem'] title = gfy.get('title') or gfy['gfyName'] description = gfy.get('description') diff -Nru youtube-dl-2015.07.04/youtube_dl/extractor/__init__.py youtube-dl-2015.07.07/youtube_dl/extractor/__init__.py --- youtube-dl-2015.07.04/youtube_dl/extractor/__init__.py 2015-07-04 07:13:16.000000000 +0000 +++ youtube-dl-2015.07.07/youtube_dl/extractor/__init__.py 2015-07-07 08:36:00.000000000 +0000 @@ -432,6 +432,7 @@ QQMusicSingerIE, QQMusicAlbumIE, QQMusicToplistIE, + QQMusicPlaylistIE, ) from .quickvid import QuickVidIE from .r7 import R7IE diff -Nru youtube-dl-2015.07.04/youtube_dl/extractor/qqmusic.py youtube-dl-2015.07.07/youtube_dl/extractor/qqmusic.py --- youtube-dl-2015.07.04/youtube_dl/extractor/qqmusic.py 2015-06-09 20:24:51.000000000 +0000 +++ youtube-dl-2015.07.07/youtube_dl/extractor/qqmusic.py 2015-07-07 08:36:00.000000000 +0000 @@ -9,6 +9,7 @@ from ..utils import ( strip_jsonp, unescapeHTML, + clean_html, ) from ..compat import compat_urllib_request @@ -26,6 +27,20 @@ 'upload_date': '20141227', 'creator': '林俊杰', 'description': 'md5:d327722d0361576fde558f1ac68a7065', + 'thumbnail': 're:^https?://.*\.jpg$', + } + }, { + 'note': 'There is no mp3-320 version of this song.', + 'url': 'http://y.qq.com/#type=song&mid=004MsGEo3DdNxV', + 'md5': 'fa3926f0c585cda0af8fa4f796482e3e', + 'info_dict': { + 'id': '004MsGEo3DdNxV', + 'ext': 'mp3', + 'title': '如果', + 'upload_date': '20050626', + 'creator': '李季美', + 'description': 'md5:46857d5ed62bc4ba84607a805dccf437', + 'thumbnail': 're:^https?://.*\.jpg$', } }] @@ -68,6 +83,14 @@ if lrc_content: lrc_content = lrc_content.replace('\\n', '\n') + thumbnail_url = None + albummid = self._search_regex( + [r'albummid:\'([0-9a-zA-Z]+)\'', r'"albummid":"([0-9a-zA-Z]+)"'], + detail_info_page, 'album mid', default=None) + if albummid: + thumbnail_url = "http://i.gtimg.cn/music/photo/mid_album_500/%s/%s/%s.jpg" \ + % (albummid[-2:-1], albummid[-1], albummid) + guid = self.m_r_get_ruin() vkey = self._download_json( @@ -85,6 +108,7 @@ 'preference': details['preference'], 'abr': details.get('abr'), }) + self._check_formats(formats, mid) self._sort_formats(formats) return { @@ -94,6 +118,7 @@ 'upload_date': publish_time, 'creator': singer, 'description': lrc_content, + 'thumbnail': thumbnail_url, } @@ -163,31 +188,40 @@ IE_NAME = 'qqmusic:album' _VALID_URL = r'http://y.qq.com/#type=album&mid=(?P[0-9A-Za-z]+)' - _TEST = { - 'url': 'http://y.qq.com/#type=album&mid=000gXCTb2AhRR1&play=0', + _TESTS = [{ + 'url': 'http://y.qq.com/#type=album&mid=000gXCTb2AhRR1', 'info_dict': { 'id': '000gXCTb2AhRR1', 'title': '我们都是这样长大的', - 'description': 'md5:d216c55a2d4b3537fe4415b8767d74d6', + 'description': 'md5:179c5dce203a5931970d306aa9607ea6', }, 'playlist_count': 4, - } + }, { + 'url': 'http://y.qq.com/#type=album&mid=002Y5a3b3AlCu3', + 'info_dict': { + 'id': '002Y5a3b3AlCu3', + 'title': '그리고...', + 'description': 'md5:a48823755615508a95080e81b51ba729', + }, + 'playlist_count': 8, + }] def _real_extract(self, url): mid = self._match_id(url) - album_page = self._download_webpage( - self.qq_static_url('album', mid), mid, 'Download album page') + album = self._download_json( + 'http://i.y.qq.com/v8/fcg-bin/fcg_v8_album_info_cp.fcg?albummid=%s&format=json' % mid, + mid, 'Download album page')['data'] - entries = self.get_entries_from_page(album_page) - - album_name = self._html_search_regex( - r"albumname\s*:\s*'([^']+)',", album_page, 'album name', - default=None) - - album_detail = self._html_search_regex( - r'
\s*

((?:[^<>]+(?:
)?)+)

', - album_page, 'album details', default=None) + entries = [ + self.url_result( + 'http://y.qq.com/#type=song&mid=' + song['songmid'], 'QQMusic', song['songmid'] + ) for song in album['list'] + ] + album_name = album.get('name') + album_detail = album.get('desc') + if album_detail is not None: + album_detail = album_detail.strip() return self.playlist_result(entries, mid, album_name, album_detail) @@ -243,3 +277,36 @@ list_name = topinfo.get('ListName') list_description = topinfo.get('info') return self.playlist_result(entries, list_id, list_name, list_description) + + +class QQMusicPlaylistIE(QQPlaylistBaseIE): + IE_NAME = 'qqmusic:playlist' + _VALID_URL = r'http://y\.qq\.com/#type=taoge&id=(?P[0-9]+)' + + _TEST = { + 'url': 'http://y.qq.com/#type=taoge&id=3462654915', + 'info_dict': { + 'id': '3462654915', + 'title': '韩国5月新歌精选下旬', + 'description': 'md5:d2c9d758a96b9888cf4fe82f603121d4', + }, + 'playlist_count': 40, + } + + def _real_extract(self, url): + list_id = self._match_id(url) + + list_json = self._download_json( + 'http://i.y.qq.com/qzone-music/fcg-bin/fcg_ucc_getcdinfo_byids_cp.fcg?type=1&json=1&utf8=1&onlysong=0&disstid=%s' + % list_id, list_id, 'Download list page', + transform_source=strip_jsonp)['cdlist'][0] + + entries = [ + self.url_result( + 'http://y.qq.com/#type=song&mid=' + song['songmid'], 'QQMusic', song['songmid'] + ) for song in list_json['songlist'] + ] + + list_name = list_json.get('dissname') + list_description = clean_html(unescapeHTML(list_json.get('desc'))) + return self.playlist_result(entries, list_id, list_name, list_description) diff -Nru youtube-dl-2015.07.04/youtube_dl/extractor/rtlnl.py youtube-dl-2015.07.07/youtube_dl/extractor/rtlnl.py --- youtube-dl-2015.07.04/youtube_dl/extractor/rtlnl.py 2015-06-14 23:35:40.000000000 +0000 +++ youtube-dl-2015.07.07/youtube_dl/extractor/rtlnl.py 2015-07-07 08:36:00.000000000 +0000 @@ -44,6 +44,10 @@ 'description': 'Er zijn nieuwe beelden vrijgegeven die vlak na de aanslag in Kopenhagen zijn gemaakt. Op de video is goed te zien hoe omstanders zich bekommeren om één van de slachtoffers, terwijl de eerste agenten ter plaatse komen.', } }, { + # encrypted m3u8 streams, georestricted + 'url': 'http://www.rtlxl.nl/#!/afl-2-257632/52a74543-c504-4cde-8aa8-ec66fe8d68a7', + 'only_matching': True, + }, { 'url': 'http://www.rtl.nl/system/videoplayer/derden/embed.html#!/uuid=bb0353b0-d6a4-1dad-90e9-18fe75b8d1f0', 'only_matching': True, }] @@ -51,7 +55,7 @@ def _real_extract(self, url): uuid = self._match_id(url) info = self._download_json( - 'http://www.rtl.nl/system/s4m/vfd/version=2/uuid=%s/fmt=flash/' % uuid, + 'http://www.rtl.nl/system/s4m/vfd/version=2/uuid=%s/fmt=adaptive/' % uuid, uuid) material = info['material'][0] @@ -59,9 +63,14 @@ subtitle = material['title'] or info['episodes'][0]['name'] description = material.get('synopsis') or info['episodes'][0]['synopsis'] + meta = info.get('meta', {}) + # Use unencrypted m3u8 streams (See https://github.com/rg3/youtube-dl/issues/4118) - videopath = material['videopath'].replace('.f4m', '.m3u8') - m3u8_url = 'http://manifest.us.rtl.nl' + videopath + # NB: nowadays, recent ffmpeg and avconv can handle these encrypted streams, so + # this adaptive -> flash workaround is not required in general, but it also + # allows bypassing georestriction therefore is retained for now. + videopath = material['videopath'].replace('/adaptive/', '/flash/') + m3u8_url = meta.get('videohost', 'http://manifest.us.rtl.nl') + videopath formats = self._extract_m3u8_formats(m3u8_url, uuid, ext='mp4') @@ -82,7 +91,7 @@ self._sort_formats(formats) thumbnails = [] - meta = info.get('meta', {}) + for p in ('poster_base_url', '"thumb_base_url"'): if not meta.get(p): continue diff -Nru youtube-dl-2015.07.04/youtube_dl/extractor/spiegeltv.py youtube-dl-2015.07.07/youtube_dl/extractor/spiegeltv.py --- youtube-dl-2015.07.04/youtube_dl/extractor/spiegeltv.py 2015-06-09 20:24:51.000000000 +0000 +++ youtube-dl-2015.07.07/youtube_dl/extractor/spiegeltv.py 2015-07-07 08:36:00.000000000 +0000 @@ -77,11 +77,13 @@ 'rtmp_live': True, }) elif determine_ext(endpoint) == 'm3u8': - formats.extend(self._extract_m3u8_formats( + m3u8_formats = self._extract_m3u8_formats( endpoint.replace('[video]', play_path), video_id, 'm4v', preference=1, # Prefer hls since it allows to workaround georestriction - m3u8_id='hls')) + m3u8_id='hls', fatal=False) + if m3u8_formats is not False: + formats.extend(m3u8_formats) else: formats.append({ 'url': endpoint, diff -Nru youtube-dl-2015.07.04/youtube_dl/extractor/vk.py youtube-dl-2015.07.07/youtube_dl/extractor/vk.py --- youtube-dl-2015.07.04/youtube_dl/extractor/vk.py 2015-06-30 22:23:28.000000000 +0000 +++ youtube-dl-2015.07.07/youtube_dl/extractor/vk.py 2015-07-07 08:36:00.000000000 +0000 @@ -153,9 +153,14 @@ if not video_id: video_id = '%s_%s' % (mobj.group('oid'), mobj.group('id')) - info_url = 'http://vk.com/al_video.php?act=show&al=1&module=video&video=%s' % video_id + info_url = 'https://vk.com/al_video.php?act=show&al=1&module=video&video=%s' % video_id info_page = self._download_webpage(info_url, video_id) + if re.search(r'/login\.php\?.*\bact=security_check', info_page): + raise ExtractorError( + 'You are trying to log in from an unusual location. You should confirm ownership at vk.com to log in with this IP.', + expected=True) + ERRORS = { r'>Видеозапись .*? была изъята из публичного доступа в связи с обращением правообладателя.<': 'Video %s has been removed from public access due to rightholder complaint.', diff -Nru youtube-dl-2015.07.04/youtube_dl/version.py youtube-dl-2015.07.07/youtube_dl/version.py --- youtube-dl-2015.07.04/youtube_dl/version.py 2015-07-04 07:23:59.000000000 +0000 +++ youtube-dl-2015.07.07/youtube_dl/version.py 2015-07-07 08:36:05.000000000 +0000 @@ -1,3 +1,3 @@ from __future__ import unicode_literals -__version__ = '2015.07.04' +__version__ = '2015.07.07' Binary files /tmp/qSuaomvpAd/youtube-dl-2015.07.04/youtube-dl and /tmp/Pvd9_CCp6x/youtube-dl-2015.07.07/youtube-dl differ