Compare commits
28 Commits
2015.01.10
...
2015.01.10
Author | SHA1 | Date | |
---|---|---|---|
|
cd3063f3fa | ||
|
58b1f00d19 | ||
|
149f05c7b6 | ||
|
8a1b9b068e | ||
|
c5a59d9391 | ||
|
500b8b41c1 | ||
|
be4a824d74 | ||
|
ed3958d714 | ||
|
6ce08764a1 | ||
|
c80ede5f13 | ||
|
bc694039e4 | ||
|
3462af03e6 | ||
|
ea1d5bdcdd | ||
|
121c09c7be | ||
|
76bfaf6daf | ||
|
d89c6e336a | ||
|
776dc3992a | ||
|
27ca82ebc6 | ||
|
385f8ae468 | ||
|
b9f030cc26 | ||
|
52afb2ac1b | ||
|
43bc88903d | ||
|
0cd64bd077 | ||
|
0551a02b82 | ||
|
25fadd06d0 | ||
|
7a47d07c6d | ||
|
34e48bed3b | ||
|
c816336cbd |
1
AUTHORS
1
AUTHORS
@@ -101,3 +101,4 @@ Thijs Vermeir
|
||||
Joel Leclerc
|
||||
Christopher Krooss
|
||||
Ondřej Caletka
|
||||
Dinesh S
|
||||
|
18
README.md
18
README.md
@@ -60,10 +60,6 @@ which means you can modify it, redistribute it or use it however you like.
|
||||
they would handle
|
||||
--extractor-descriptions Output descriptions of all supported
|
||||
extractors
|
||||
--proxy URL Use the specified HTTP/HTTPS proxy. Pass in
|
||||
an empty string (--proxy "") for direct
|
||||
connection
|
||||
--socket-timeout None Time to wait before giving up, in seconds
|
||||
--default-search PREFIX Use this prefix for unqualified URLs. For
|
||||
example "gvsearch2:" downloads two videos
|
||||
from google videos for youtube-dl "large
|
||||
@@ -82,6 +78,18 @@ which means you can modify it, redistribute it or use it however you like.
|
||||
--flat-playlist Do not extract the videos of a playlist,
|
||||
only list them.
|
||||
|
||||
## Network Options:
|
||||
--proxy URL Use the specified HTTP/HTTPS proxy. Pass in
|
||||
an empty string (--proxy "") for direct
|
||||
connection
|
||||
--socket-timeout SECONDS Time to wait before giving up, in seconds
|
||||
--source-address IP Client-side IP address to bind to
|
||||
(experimental)
|
||||
-4, --force-ipv4 Make all connections via IPv4
|
||||
(experimental)
|
||||
-6, --force-ipv6 Make all connections via IPv6
|
||||
(experimental)
|
||||
|
||||
## Video Selection:
|
||||
--playlist-start NUMBER playlist video to start at (default is 1)
|
||||
--playlist-end NUMBER playlist video to end at (default is last)
|
||||
@@ -231,6 +239,8 @@ which means you can modify it, redistribute it or use it however you like.
|
||||
files in the current directory to debug
|
||||
problems
|
||||
--print-traffic Display sent and read HTTP traffic
|
||||
-C, --call-home Contact the youtube-dl server for
|
||||
debugging. (Experimental)
|
||||
|
||||
## Workarounds:
|
||||
--encoding ENCODING Force the specified encoding (experimental)
|
||||
|
@@ -58,6 +58,7 @@ from .utils import (
|
||||
takewhile_inclusive,
|
||||
UnavailableVideoError,
|
||||
url_basename,
|
||||
version_tuple,
|
||||
write_json_file,
|
||||
write_string,
|
||||
YoutubeDLHandler,
|
||||
@@ -211,6 +212,9 @@ class YoutubeDL(object):
|
||||
- "warn": only emit a warning
|
||||
- "detect_or_warn": check whether we can do anything
|
||||
about it, warn otherwise
|
||||
source_address: (Experimental) Client-side IP address to bind to.
|
||||
call_home: (Experimental) Boolean, true iff we are allowed to
|
||||
contact the youtube-dl servers for debugging.
|
||||
|
||||
|
||||
The following parameters are not used by YoutubeDL itself, they are used by
|
||||
@@ -1463,6 +1467,17 @@ class YoutubeDL(object):
|
||||
proxy_map.update(handler.proxies)
|
||||
self._write_string('[debug] Proxy map: ' + compat_str(proxy_map) + '\n')
|
||||
|
||||
if self.params.get('call_home', False):
|
||||
ipaddr = self.urlopen('https://yt-dl.org/ip').read().decode('utf-8')
|
||||
self._write_string('[debug] Public IP address: %s\n' % ipaddr)
|
||||
latest_version = self.urlopen(
|
||||
'https://yt-dl.org/latest/version').read().decode('utf-8')
|
||||
if version_tuple(latest_version) > version_tuple(__version__):
|
||||
self.report_warning(
|
||||
'You are using an outdated version (newest version: %s)! '
|
||||
'See https://yt-dl.org/update if you need help updating.' %
|
||||
latest_version)
|
||||
|
||||
def _setup_opener(self):
|
||||
timeout_val = self.params.get('socket_timeout')
|
||||
self._socket_timeout = 600 if timeout_val is None else float(timeout_val)
|
||||
@@ -1493,9 +1508,8 @@ class YoutubeDL(object):
|
||||
proxy_handler = compat_urllib_request.ProxyHandler(proxies)
|
||||
|
||||
debuglevel = 1 if self.params.get('debug_printtraffic') else 0
|
||||
https_handler = make_HTTPS_handler(
|
||||
self.params.get('nocheckcertificate', False), debuglevel=debuglevel)
|
||||
ydlh = YoutubeDLHandler(debuglevel=debuglevel)
|
||||
https_handler = make_HTTPS_handler(self.params, debuglevel=debuglevel)
|
||||
ydlh = YoutubeDLHandler(self.params, debuglevel=debuglevel)
|
||||
opener = compat_urllib_request.build_opener(
|
||||
https_handler, proxy_handler, cookie_processor, ydlh)
|
||||
# Delete the default user-agent header, which would otherwise apply in
|
||||
|
@@ -327,6 +327,8 @@ def _real_main(argv=None):
|
||||
'merge_output_format': opts.merge_output_format,
|
||||
'postprocessors': postprocessors,
|
||||
'fixup': opts.fixup,
|
||||
'source_address': opts.source_address,
|
||||
'call_home': opts.call_home,
|
||||
}
|
||||
|
||||
with YoutubeDL(ydl_opts) as ydl:
|
||||
|
@@ -4,6 +4,7 @@ import getpass
|
||||
import optparse
|
||||
import os
|
||||
import re
|
||||
import socket
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
@@ -307,6 +308,32 @@ else:
|
||||
compat_kwargs = lambda kwargs: kwargs
|
||||
|
||||
|
||||
if sys.version_info < (2, 7):
|
||||
def compat_socket_create_connection(address, timeout, source_address=None):
|
||||
host, port = address
|
||||
err = None
|
||||
for res in socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM):
|
||||
af, socktype, proto, canonname, sa = res
|
||||
sock = None
|
||||
try:
|
||||
sock = socket.socket(af, socktype, proto)
|
||||
sock.settimeout(timeout)
|
||||
if source_address:
|
||||
sock.bind(source_address)
|
||||
sock.connect(sa)
|
||||
return sock
|
||||
except socket.error as _:
|
||||
err = _
|
||||
if sock is not None:
|
||||
sock.close()
|
||||
if err is not None:
|
||||
raise err
|
||||
else:
|
||||
raise error("getaddrinfo returns an empty list")
|
||||
else:
|
||||
compat_socket_create_connection = socket.create_connection
|
||||
|
||||
|
||||
# Fix https://github.com/rg3/youtube-dl/issues/4223
|
||||
# See http://bugs.python.org/issue9161 for what is broken
|
||||
def workaround_optparse_bug9161():
|
||||
@@ -343,6 +370,7 @@ __all__ = [
|
||||
'compat_parse_qs',
|
||||
'compat_print',
|
||||
'compat_str',
|
||||
'compat_socket_create_connection',
|
||||
'compat_subprocess_get_DEVNULL',
|
||||
'compat_urllib_error',
|
||||
'compat_urllib_parse',
|
||||
|
@@ -26,6 +26,7 @@ from .arte import (
|
||||
ArteTVEmbedIE,
|
||||
)
|
||||
from .atresplayer import AtresPlayerIE
|
||||
from .atttechchannel import ATTTechChannelIE
|
||||
from .audiomack import AudiomackIE, AudiomackAlbumIE
|
||||
from .auengine import AUEngineIE
|
||||
from .azubu import AzubuIE
|
||||
@@ -69,6 +70,7 @@ from .cnn import (
|
||||
CNNArticleIE,
|
||||
)
|
||||
from .collegehumor import CollegeHumorIE
|
||||
from .collegerama import CollegeRamaIE
|
||||
from .comedycentral import ComedyCentralIE, ComedyCentralShowsIE
|
||||
from .comcarcoff import ComCarCoffIE
|
||||
from .commonmistakes import CommonMistakesIE
|
||||
@@ -206,6 +208,7 @@ from .jove import JoveIE
|
||||
from .jukebox import JukeboxIE
|
||||
from .jpopsukitv import JpopsukiIE
|
||||
from .kankan import KankanIE
|
||||
from .karaoketv import KaraoketvIE
|
||||
from .keezmovies import KeezMoviesIE
|
||||
from .khanacademy import KhanAcademyIE
|
||||
from .kickstarter import KickStarterIE
|
||||
@@ -339,6 +342,7 @@ from .ro220 import Ro220IE
|
||||
from .rottentomatoes import RottenTomatoesIE
|
||||
from .roxwel import RoxwelIE
|
||||
from .rtbf import RTBFIE
|
||||
from .rte import RteIE
|
||||
from .rtlnl import RtlXlIE
|
||||
from .rtlnow import RTLnowIE
|
||||
from .rtp import RTPIE
|
||||
|
55
youtube_dl/extractor/atttechchannel.py
Normal file
55
youtube_dl/extractor/atttechchannel.py
Normal file
@@ -0,0 +1,55 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from .common import InfoExtractor
|
||||
from ..utils import unified_strdate
|
||||
|
||||
|
||||
class ATTTechChannelIE(InfoExtractor):
|
||||
_VALID_URL = r'https?://techchannel\.att\.com/play-video\.cfm/([^/]+/)*(?P<id>.+)'
|
||||
_TEST = {
|
||||
'url': 'http://techchannel.att.com/play-video.cfm/2014/1/27/ATT-Archives-The-UNIX-System-Making-Computers-Easier-to-Use',
|
||||
'info_dict': {
|
||||
'id': '11316',
|
||||
'display_id': 'ATT-Archives-The-UNIX-System-Making-Computers-Easier-to-Use',
|
||||
'ext': 'flv',
|
||||
'title': 'AT&T Archives : The UNIX System: Making Computers Easier to Use',
|
||||
'description': 'A 1982 film about UNIX is the foundation for software in use around Bell Labs and AT&T.',
|
||||
'thumbnail': 're:^https?://.*\.jpg$',
|
||||
'upload_date': '20140127',
|
||||
},
|
||||
'params': {
|
||||
# rtmp download
|
||||
'skip_download': True,
|
||||
},
|
||||
}
|
||||
|
||||
def _real_extract(self, url):
|
||||
display_id = self._match_id(url)
|
||||
|
||||
webpage = self._download_webpage(url, display_id)
|
||||
|
||||
video_url = self._search_regex(
|
||||
r"url\s*:\s*'(rtmp://[^']+)'",
|
||||
webpage, 'video URL')
|
||||
|
||||
video_id = self._search_regex(
|
||||
r'mediaid\s*=\s*(\d+)',
|
||||
webpage, 'video id', fatal=False)
|
||||
|
||||
title = self._og_search_title(webpage)
|
||||
description = self._og_search_description(webpage)
|
||||
thumbnail = self._og_search_thumbnail(webpage)
|
||||
upload_date = unified_strdate(self._search_regex(
|
||||
r'[Rr]elease\s+date:\s*(\d{1,2}/\d{1,2}/\d{4})',
|
||||
webpage, 'upload date', fatal=False), False)
|
||||
|
||||
return {
|
||||
'id': video_id,
|
||||
'display_id': display_id,
|
||||
'url': video_url,
|
||||
'ext': 'flv',
|
||||
'title': title,
|
||||
'description': description,
|
||||
'thumbnail': thumbnail,
|
||||
'upload_date': upload_date,
|
||||
}
|
92
youtube_dl/extractor/collegerama.py
Normal file
92
youtube_dl/extractor/collegerama.py
Normal file
@@ -0,0 +1,92 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import json
|
||||
|
||||
from .common import InfoExtractor
|
||||
from ..compat import compat_urllib_request
|
||||
from ..utils import (
|
||||
float_or_none,
|
||||
int_or_none,
|
||||
)
|
||||
|
||||
|
||||
class CollegeRamaIE(InfoExtractor):
|
||||
_VALID_URL = r'https?://collegerama\.tudelft\.nl/Mediasite/Play/(?P<id>[\da-f]+)'
|
||||
_TESTS = [
|
||||
{
|
||||
'url': 'https://collegerama.tudelft.nl/Mediasite/Play/585a43626e544bdd97aeb71a0ec907a01d',
|
||||
'md5': '481fda1c11f67588c0d9d8fbdced4e39',
|
||||
'info_dict': {
|
||||
'id': '585a43626e544bdd97aeb71a0ec907a01d',
|
||||
'ext': 'mp4',
|
||||
'title': 'Een nieuwe wereld: waarden, bewustzijn en techniek van de mensheid 2.0.',
|
||||
'description': '',
|
||||
'thumbnail': 're:^https?://.*\.jpg$',
|
||||
'duration': 7713.088,
|
||||
'timestamp': 1413309600,
|
||||
'upload_date': '20141014',
|
||||
},
|
||||
},
|
||||
{
|
||||
'url': 'https://collegerama.tudelft.nl/Mediasite/Play/86a9ea9f53e149079fbdb4202b521ed21d?catalog=fd32fd35-6c99-466c-89d4-cd3c431bc8a4',
|
||||
'md5': 'ef1fdded95bdf19b12c5999949419c92',
|
||||
'info_dict': {
|
||||
'id': '86a9ea9f53e149079fbdb4202b521ed21d',
|
||||
'ext': 'wmv',
|
||||
'title': '64ste Vakantiecursus: Afvalwater',
|
||||
'description': 'md5:7fd774865cc69d972f542b157c328305',
|
||||
'duration': 10853,
|
||||
'timestamp': 1326446400,
|
||||
'upload_date': '20120113',
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
def _real_extract(self, url):
|
||||
video_id = self._match_id(url)
|
||||
|
||||
player_options_request = {
|
||||
"getPlayerOptionsRequest": {
|
||||
"ResourceId": video_id,
|
||||
"QueryString": "",
|
||||
}
|
||||
}
|
||||
|
||||
request = compat_urllib_request.Request(
|
||||
'http://collegerama.tudelft.nl/Mediasite/PlayerService/PlayerService.svc/json/GetPlayerOptions',
|
||||
json.dumps(player_options_request))
|
||||
request.add_header('Content-Type', 'application/json')
|
||||
|
||||
player_options = self._download_json(request, video_id)
|
||||
|
||||
presentation = player_options['d']['Presentation']
|
||||
title = presentation['Title']
|
||||
description = presentation.get('Description')
|
||||
thumbnail = None
|
||||
duration = float_or_none(presentation.get('Duration'), 1000)
|
||||
timestamp = int_or_none(presentation.get('UnixTime'), 1000)
|
||||
|
||||
formats = []
|
||||
for stream in presentation['Streams']:
|
||||
for video in stream['VideoUrls']:
|
||||
thumbnail_url = stream.get('ThumbnailUrl')
|
||||
if thumbnail_url:
|
||||
thumbnail = 'http://collegerama.tudelft.nl' + thumbnail_url
|
||||
format_id = video['MediaType']
|
||||
if format_id == 'SS':
|
||||
continue
|
||||
formats.append({
|
||||
'url': video['Location'],
|
||||
'format_id': format_id,
|
||||
})
|
||||
self._sort_formats(formats)
|
||||
|
||||
return {
|
||||
'id': video_id,
|
||||
'title': title,
|
||||
'description': description,
|
||||
'thumbnail': thumbnail,
|
||||
'duration': duration,
|
||||
'timestamp': timestamp,
|
||||
'formats': formats,
|
||||
}
|
@@ -742,8 +742,14 @@ class InfoExtractor(object):
|
||||
'Unable to download f4m manifest')
|
||||
|
||||
formats = []
|
||||
manifest_version = '1.0'
|
||||
media_nodes = manifest.findall('{http://ns.adobe.com/f4m/1.0}media')
|
||||
if not media_nodes:
|
||||
manifest_version = '2.0'
|
||||
media_nodes = manifest.findall('{http://ns.adobe.com/f4m/2.0}media')
|
||||
for i, media_el in enumerate(media_nodes):
|
||||
if manifest_version == '2.0':
|
||||
manifest_url = '/'.join(manifest_url.split('/')[:-1]) + '/' + media_el.attrib.get('href')
|
||||
tbr = int_or_none(media_el.attrib.get('bitrate'))
|
||||
format_id = 'f4m-%d' % (i if tbr is None else tbr)
|
||||
formats.append({
|
||||
|
@@ -122,7 +122,6 @@ class EightTracksIE(InfoExtractor):
|
||||
entries = []
|
||||
|
||||
for i in range(track_count):
|
||||
|
||||
api_json = None
|
||||
download_tries = 0
|
||||
|
||||
|
41
youtube_dl/extractor/karaoketv.py
Normal file
41
youtube_dl/extractor/karaoketv.py
Normal file
@@ -0,0 +1,41 @@
|
||||
# coding: utf-8
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from .common import InfoExtractor
|
||||
from ..compat import compat_urllib_parse
|
||||
from ..utils import (
|
||||
ExtractorError,
|
||||
js_to_json,
|
||||
)
|
||||
|
||||
|
||||
class KaraoketvIE(InfoExtractor):
|
||||
_VALID_URL = r'http://karaoketv\.co\.il/\?container=songs&id=(?P<id>[0-9]+)'
|
||||
_TEST = {
|
||||
'url': 'http://karaoketv.co.il/?container=songs&id=171568',
|
||||
'info_dict': {
|
||||
'id': '171568',
|
||||
'ext': 'mp4',
|
||||
'title': 'אל העולם שלך - רותם כהן - שרים קריוקי',
|
||||
}
|
||||
}
|
||||
|
||||
def _real_extract(self, url):
|
||||
video_id = self._match_id(url)
|
||||
webpage = self._download_webpage(url, video_id)
|
||||
|
||||
page_video_url = self._og_search_video_url(webpage, video_id)
|
||||
config_json = compat_urllib_parse.unquote_plus(self._search_regex(
|
||||
r'config=(.*)', page_video_url, 'configuration'))
|
||||
|
||||
urls_info_json = self._download_json(
|
||||
config_json, video_id, 'Downloading configuration',
|
||||
transform_source=js_to_json)
|
||||
|
||||
url = urls_info_json['playlist'][0]['url']
|
||||
|
||||
return {
|
||||
'id': video_id,
|
||||
'title': self._og_search_title(webpage),
|
||||
'url': url,
|
||||
}
|
@@ -7,8 +7,10 @@ from .common import InfoExtractor
|
||||
from ..utils import (
|
||||
ExtractorError,
|
||||
float_or_none,
|
||||
parse_duration,
|
||||
unified_strdate,
|
||||
)
|
||||
from .subtitles import SubtitlesInfoExtractor
|
||||
|
||||
|
||||
class NRKIE(InfoExtractor):
|
||||
@@ -71,8 +73,8 @@ class NRKIE(InfoExtractor):
|
||||
}
|
||||
|
||||
|
||||
class NRKTVIE(InfoExtractor):
|
||||
_VALID_URL = r'http://tv\.nrk(?:super)?\.no/(?:serie/[^/]+|program)/(?P<id>[a-zA-Z]{4}\d{8})(?:/\d{2}-\d{2}-\d{4})?(?:#del=(?P<part_id>\d+))?'
|
||||
class NRKTVIE(SubtitlesInfoExtractor):
|
||||
_VALID_URL = r'(?P<baseurl>http://tv\.nrk(?:super)?\.no/)(?:serie/[^/]+|program)/(?P<id>[a-zA-Z]{4}\d{8})(?:/\d{2}-\d{2}-\d{4})?(?:#del=(?P<part_id>\d+))?'
|
||||
|
||||
_TESTS = [
|
||||
{
|
||||
@@ -147,6 +149,29 @@ class NRKTVIE(InfoExtractor):
|
||||
}
|
||||
]
|
||||
|
||||
def _seconds2str(self, s):
|
||||
return '%02d:%02d:%02d.%03d' % (s / 3600, (s % 3600) / 60, s % 60, (s % 1) * 1000)
|
||||
|
||||
def _debug_print(self, txt):
|
||||
if self._downloader.params.get('verbose', False):
|
||||
self.to_screen('[debug] %s' % txt)
|
||||
|
||||
def _extract_captions(self, subtitlesurl, video_id, baseurl):
|
||||
url = "%s%s" % (baseurl, subtitlesurl)
|
||||
self._debug_print('%s: Subtitle url: %s' % (video_id, url))
|
||||
captions = self._download_xml(url, video_id, 'Downloading subtitles')
|
||||
lang = captions.get('lang', 'no')
|
||||
ps = captions.findall('./{0}body/{0}div/{0}p'.format('{http://www.w3.org/ns/ttml}'))
|
||||
srt = ''
|
||||
for pos, p in enumerate(ps):
|
||||
begin = parse_duration(p.get('begin'))
|
||||
duration = parse_duration(p.get('dur'))
|
||||
starttime = self._seconds2str(begin)
|
||||
endtime = self._seconds2str(begin + duration)
|
||||
text = '\n'.join(p.itertext())
|
||||
srt += '%s\r\n%s --> %s\r\n%s\r\n\r\n' % (str(pos), starttime, endtime, text)
|
||||
return {lang: srt}
|
||||
|
||||
def _extract_f4m(self, manifest_url, video_id):
|
||||
return self._extract_f4m_formats(manifest_url + '?hdcore=3.1.1&plugin=aasp-3.1.1.69.124', video_id)
|
||||
|
||||
@@ -154,6 +179,7 @@ class NRKTVIE(InfoExtractor):
|
||||
mobj = re.match(self._VALID_URL, url)
|
||||
video_id = mobj.group('id')
|
||||
part_id = mobj.group('part_id')
|
||||
baseurl = mobj.group('baseurl')
|
||||
|
||||
webpage = self._download_webpage(url, video_id)
|
||||
|
||||
@@ -210,9 +236,18 @@ class NRKTVIE(InfoExtractor):
|
||||
m3u8_url = re.search(r'data-hls-media="([^"]+)"', webpage)
|
||||
if m3u8_url:
|
||||
formats.extend(self._extract_m3u8_formats(m3u8_url.group(1), video_id, 'mp4'))
|
||||
|
||||
self._sort_formats(formats)
|
||||
|
||||
subtitles_url = self._html_search_regex(
|
||||
r'data-subtitlesurl[ ]*=[ ]*"([^"]+)"',
|
||||
webpage, 'subtitle URL', default=None)
|
||||
subtitles = None
|
||||
if subtitles_url:
|
||||
subtitles = self._extract_captions(subtitles_url, video_id, baseurl)
|
||||
if self._downloader.params.get('listsubtitles', False):
|
||||
self._list_available_subtitles(video_id, subtitles)
|
||||
return
|
||||
|
||||
return {
|
||||
'id': video_id,
|
||||
'title': title,
|
||||
@@ -221,4 +256,5 @@ class NRKTVIE(InfoExtractor):
|
||||
'upload_date': upload_date,
|
||||
'duration': duration,
|
||||
'formats': formats,
|
||||
'subtitles': subtitles,
|
||||
}
|
||||
|
@@ -128,13 +128,16 @@ class ORFTVthekIE(InfoExtractor):
|
||||
}
|
||||
|
||||
|
||||
# Audios on ORF radio are only available for 7 days, so we can't add tests.
|
||||
|
||||
|
||||
class ORFOE1IE(InfoExtractor):
|
||||
IE_NAME = 'orf:oe1'
|
||||
IE_DESC = 'Radio Österreich 1'
|
||||
_VALID_URL = r'http://oe1\.orf\.at/programm/(?P<id>[0-9]+)'
|
||||
_VALID_URL = r'http://oe1\.orf\.at/(?:programm/|konsole.*?#\?track_id=)(?P<id>[0-9]+)'
|
||||
|
||||
# Audios on ORF radio are only available for 7 days, so we can't add tests.
|
||||
_TEST = {
|
||||
'url': 'http://oe1.orf.at/konsole?show=on_demand#?track_id=394211',
|
||||
'only_matching': True,
|
||||
}
|
||||
|
||||
def _real_extract(self, url):
|
||||
show_id = self._match_id(url)
|
||||
@@ -160,7 +163,7 @@ class ORFOE1IE(InfoExtractor):
|
||||
|
||||
|
||||
class ORFFM4IE(InfoExtractor):
|
||||
IE_DESC = 'orf:fm4'
|
||||
IE_NAME = 'orf:fm4'
|
||||
IE_DESC = 'radio FM4'
|
||||
_VALID_URL = r'http://fm4\.orf\.at/7tage/?#(?P<date>[0-9]+)/(?P<show>\w+)'
|
||||
|
||||
|
62
youtube_dl/extractor/rte.py
Normal file
62
youtube_dl/extractor/rte.py
Normal file
@@ -0,0 +1,62 @@
|
||||
# coding: utf-8
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from .common import InfoExtractor
|
||||
|
||||
from ..utils import (
|
||||
float_or_none,
|
||||
)
|
||||
|
||||
|
||||
class RteIE(InfoExtractor):
|
||||
_VALID_URL = r'http?://(?:www\.)?rte\.ie/player/[^/]{2,3}/show/(?P<id>[0-9]+)/'
|
||||
_TEST = {
|
||||
'url': 'http://www.rte.ie/player/de/show/10363114/',
|
||||
'info_dict': {
|
||||
'id': '10363114',
|
||||
'ext': 'mp4',
|
||||
'title': 'One News',
|
||||
'thumbnail': 're:^https?://.*\.jpg$',
|
||||
'description': 'The One O\'Clock News followed by Weather.',
|
||||
'duration': 436.844,
|
||||
},
|
||||
'params': {
|
||||
'skip_download': 'f4m fails with --test atm'
|
||||
}
|
||||
}
|
||||
|
||||
def _real_extract(self, url):
|
||||
video_id = self._match_id(url)
|
||||
webpage = self._download_webpage(url, video_id)
|
||||
|
||||
title = self._og_search_title(webpage)
|
||||
description = self._html_search_meta('description', webpage, 'description')
|
||||
duration = float_or_none(self._html_search_meta(
|
||||
'duration', webpage, 'duration', fatal=False), 1000)
|
||||
|
||||
thumbnail_id = self._search_regex(
|
||||
r'<meta name="thumbnail" content="uri:irus:(.*?)" />', webpage, 'thumbnail')
|
||||
thumbnail = 'http://img.rasset.ie/' + thumbnail_id + '.jpg'
|
||||
|
||||
feeds_url = self._html_search_meta("feeds-prefix", webpage, 'feeds url') + video_id
|
||||
json_string = self._download_json(feeds_url, video_id)
|
||||
|
||||
# f4m_url = server + relative_url
|
||||
f4m_url = json_string['shows'][0]['media:group'][0]['rte:server'] + json_string['shows'][0]['media:group'][0]['url']
|
||||
f4m_formats = self._extract_f4m_formats(f4m_url, video_id)
|
||||
f4m_formats = [{
|
||||
'format_id': f['format_id'],
|
||||
'url': f['url'],
|
||||
'ext': 'mp4',
|
||||
'width': f['width'],
|
||||
'height': f['height'],
|
||||
} for f in f4m_formats]
|
||||
|
||||
return {
|
||||
'id': video_id,
|
||||
'title': title,
|
||||
'formats': f4m_formats,
|
||||
'description': description,
|
||||
'thumbnail': thumbnail,
|
||||
'duration': duration,
|
||||
}
|
@@ -148,14 +148,6 @@ def parseOpts(overrideArguments=None):
|
||||
'--extractor-descriptions',
|
||||
action='store_true', dest='list_extractor_descriptions', default=False,
|
||||
help='Output descriptions of all supported extractors')
|
||||
general.add_option(
|
||||
'--proxy', dest='proxy',
|
||||
default=None, metavar='URL',
|
||||
help='Use the specified HTTP/HTTPS proxy. Pass in an empty string (--proxy "") for direct connection')
|
||||
general.add_option(
|
||||
'--socket-timeout',
|
||||
dest='socket_timeout', type=float, default=None,
|
||||
help='Time to wait before giving up, in seconds')
|
||||
general.add_option(
|
||||
'--default-search',
|
||||
dest='default_search', metavar='PREFIX',
|
||||
@@ -173,6 +165,31 @@ def parseOpts(overrideArguments=None):
|
||||
default=False,
|
||||
help='Do not extract the videos of a playlist, only list them.')
|
||||
|
||||
network = optparse.OptionGroup(parser, 'Network Options')
|
||||
network.add_option(
|
||||
'--proxy', dest='proxy',
|
||||
default=None, metavar='URL',
|
||||
help='Use the specified HTTP/HTTPS proxy. Pass in an empty string (--proxy "") for direct connection')
|
||||
network.add_option(
|
||||
'--socket-timeout',
|
||||
dest='socket_timeout', type=float, default=None, metavar='SECONDS',
|
||||
help='Time to wait before giving up, in seconds')
|
||||
network.add_option(
|
||||
'--source-address',
|
||||
metavar='IP', dest='source_address', default=None,
|
||||
help='Client-side IP address to bind to (experimental)',
|
||||
)
|
||||
network.add_option(
|
||||
'-4', '--force-ipv4',
|
||||
action='store_const', const='0.0.0.0', dest='source_address',
|
||||
help='Make all connections via IPv4 (experimental)',
|
||||
)
|
||||
network.add_option(
|
||||
'-6', '--force-ipv6',
|
||||
action='store_const', const='::', dest='source_address',
|
||||
help='Make all connections via IPv6 (experimental)',
|
||||
)
|
||||
|
||||
selection = optparse.OptionGroup(parser, 'Video Selection')
|
||||
selection.add_option(
|
||||
'--playlist-start',
|
||||
@@ -488,6 +505,10 @@ def parseOpts(overrideArguments=None):
|
||||
'--print-traffic',
|
||||
dest='debug_printtraffic', action='store_true', default=False,
|
||||
help='Display sent and read HTTP traffic')
|
||||
verbosity.add_option(
|
||||
'-C', '--call-home',
|
||||
dest='call_home', action='store_true', default=False,
|
||||
help='Contact the youtube-dl server for debugging. (Experimental)')
|
||||
|
||||
filesystem = optparse.OptionGroup(parser, 'Filesystem Options')
|
||||
filesystem.add_option(
|
||||
@@ -652,6 +673,7 @@ def parseOpts(overrideArguments=None):
|
||||
help='Execute a command on the file after downloading, similar to find\'s -exec syntax. Example: --exec \'adb push {} /sdcard/Music/ && rm {}\'')
|
||||
|
||||
parser.add_option_group(general)
|
||||
parser.add_option_group(network)
|
||||
parser.add_option_group(selection)
|
||||
parser.add_option_group(downloader)
|
||||
parser.add_option_group(filesystem)
|
||||
|
@@ -82,12 +82,15 @@ class FFmpegPostProcessor(PostProcessor):
|
||||
def run_ffmpeg_multiple_files(self, input_paths, out_path, opts):
|
||||
self.check_version()
|
||||
|
||||
oldest_mtime = min(
|
||||
os.stat(encodeFilename(path)).st_mtime for path in input_paths)
|
||||
|
||||
files_cmd = []
|
||||
for path in input_paths:
|
||||
files_cmd.extend([encodeArgument('-i'), encodeFilename(path, True)])
|
||||
cmd = ([encodeFilename(self._executable, True), encodeArgument('-y')] +
|
||||
files_cmd
|
||||
+ [encodeArgument(o) for o in opts] +
|
||||
files_cmd +
|
||||
[encodeArgument(o) for o in opts] +
|
||||
[encodeFilename(self._ffmpeg_filename_argument(out_path), True)])
|
||||
|
||||
if self._downloader.params.get('verbose', False):
|
||||
@@ -98,6 +101,7 @@ class FFmpegPostProcessor(PostProcessor):
|
||||
stderr = stderr.decode('utf-8', 'replace')
|
||||
msg = stderr.strip().split('\n')[-1]
|
||||
raise FFmpegPostProcessorError(msg)
|
||||
os.utime(encodeFilename(out_path), (oldest_mtime, oldest_mtime))
|
||||
if self._deletetempfiles:
|
||||
for ipath in input_paths:
|
||||
os.remove(ipath)
|
||||
|
@@ -59,7 +59,7 @@ def update_self(to_screen, verbose):
|
||||
to_screen('It looks like you installed youtube-dl with a package manager, pip, setup.py or a tarball. Please use that to update.')
|
||||
return
|
||||
|
||||
https_handler = make_HTTPS_handler(False)
|
||||
https_handler = make_HTTPS_handler({})
|
||||
opener = compat_urllib_request.build_opener(https_handler)
|
||||
|
||||
# Check if there is a new version
|
||||
|
@@ -10,6 +10,7 @@ import ctypes
|
||||
import datetime
|
||||
import email.utils
|
||||
import errno
|
||||
import functools
|
||||
import gzip
|
||||
import itertools
|
||||
import io
|
||||
@@ -34,7 +35,9 @@ from .compat import (
|
||||
compat_chr,
|
||||
compat_getenv,
|
||||
compat_html_entities,
|
||||
compat_http_client,
|
||||
compat_parse_qs,
|
||||
compat_socket_create_connection,
|
||||
compat_str,
|
||||
compat_urllib_error,
|
||||
compat_urllib_parse,
|
||||
@@ -391,13 +394,14 @@ def formatSeconds(secs):
|
||||
return '%d' % secs
|
||||
|
||||
|
||||
def make_HTTPS_handler(opts_no_check_certificate, **kwargs):
|
||||
def make_HTTPS_handler(params, **kwargs):
|
||||
opts_no_check_certificate = params.get('nocheckcertificate', False)
|
||||
if hasattr(ssl, 'create_default_context'): # Python >= 3.4 or 2.7.9
|
||||
context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
|
||||
if opts_no_check_certificate:
|
||||
context.verify_mode = ssl.CERT_NONE
|
||||
try:
|
||||
return compat_urllib_request.HTTPSHandler(context=context, **kwargs)
|
||||
return YoutubeDLHTTPSHandler(params, context=context, **kwargs)
|
||||
except TypeError:
|
||||
# Python 2.7.8
|
||||
# (create_default_context present but HTTPSHandler has no context=)
|
||||
@@ -420,17 +424,14 @@ def make_HTTPS_handler(opts_no_check_certificate, **kwargs):
|
||||
except ssl.SSLError:
|
||||
self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file, ssl_version=ssl.PROTOCOL_SSLv23)
|
||||
|
||||
class HTTPSHandlerV3(compat_urllib_request.HTTPSHandler):
|
||||
def https_open(self, req):
|
||||
return self.do_open(HTTPSConnectionV3, req)
|
||||
return HTTPSHandlerV3(**kwargs)
|
||||
return YoutubeDLHTTPSHandler(params, https_conn_class=HTTPSConnectionV3, **kwargs)
|
||||
else: # Python < 3.4
|
||||
context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
|
||||
context.verify_mode = (ssl.CERT_NONE
|
||||
if opts_no_check_certificate
|
||||
else ssl.CERT_REQUIRED)
|
||||
context.set_default_verify_paths()
|
||||
return compat_urllib_request.HTTPSHandler(context=context, **kwargs)
|
||||
return YoutubeDLHTTPSHandler(params, context=context, **kwargs)
|
||||
|
||||
|
||||
class ExtractorError(Exception):
|
||||
@@ -544,6 +545,26 @@ class ContentTooShortError(Exception):
|
||||
self.expected = expected
|
||||
|
||||
|
||||
def _create_http_connection(ydl_handler, http_class, is_https, *args, **kwargs):
|
||||
hc = http_class(*args, **kwargs)
|
||||
source_address = ydl_handler._params.get('source_address')
|
||||
if source_address is not None:
|
||||
sa = (source_address, 0)
|
||||
if hasattr(hc, 'source_address'): # Python 2.7+
|
||||
hc.source_address = sa
|
||||
else: # Python 2.6
|
||||
def _hc_connect(self, *args, **kwargs):
|
||||
sock = compat_socket_create_connection(
|
||||
(self.host, self.port), self.timeout, sa)
|
||||
if is_https:
|
||||
self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file)
|
||||
else:
|
||||
self.sock = sock
|
||||
hc.connect = functools.partial(_hc_connect, hc)
|
||||
|
||||
return hc
|
||||
|
||||
|
||||
class YoutubeDLHandler(compat_urllib_request.HTTPHandler):
|
||||
"""Handler for HTTP requests and responses.
|
||||
|
||||
@@ -562,6 +583,15 @@ class YoutubeDLHandler(compat_urllib_request.HTTPHandler):
|
||||
public domain.
|
||||
"""
|
||||
|
||||
def __init__(self, params, *args, **kwargs):
|
||||
compat_urllib_request.HTTPHandler.__init__(self, *args, **kwargs)
|
||||
self._params = params
|
||||
|
||||
def http_open(self, req):
|
||||
return self.do_open(functools.partial(
|
||||
_create_http_connection, self, compat_http_client.HTTPConnection, False),
|
||||
req)
|
||||
|
||||
@staticmethod
|
||||
def deflate(data):
|
||||
try:
|
||||
@@ -631,6 +661,18 @@ class YoutubeDLHandler(compat_urllib_request.HTTPHandler):
|
||||
https_response = http_response
|
||||
|
||||
|
||||
class YoutubeDLHTTPSHandler(compat_urllib_request.HTTPSHandler):
|
||||
def __init__(self, params, https_conn_class=None, *args, **kwargs):
|
||||
compat_urllib_request.HTTPSHandler.__init__(self, *args, **kwargs)
|
||||
self._https_conn_class = https_conn_class or compat_http_client.HTTPSConnection
|
||||
self._params = params
|
||||
|
||||
def https_open(self, req):
|
||||
return self.do_open(functools.partial(
|
||||
_create_http_connection, self, self._https_conn_class, True),
|
||||
req)
|
||||
|
||||
|
||||
def parse_iso8601(date_str, delimiter='T'):
|
||||
""" Return a UNIX timestamp from the given date """
|
||||
|
||||
@@ -679,9 +721,6 @@ def unified_strdate(date_str, day_first=True):
|
||||
'%b %dth %Y %I:%M%p',
|
||||
'%Y-%m-%d',
|
||||
'%Y/%m/%d',
|
||||
'%d.%m.%Y',
|
||||
'%d/%m/%Y',
|
||||
'%d/%m/%y',
|
||||
'%Y/%m/%d %H:%M:%S',
|
||||
'%Y-%m-%d %H:%M:%S',
|
||||
'%Y-%m-%d %H:%M:%S.%f',
|
||||
@@ -696,10 +735,16 @@ def unified_strdate(date_str, day_first=True):
|
||||
]
|
||||
if day_first:
|
||||
format_expressions.extend([
|
||||
'%d.%m.%Y',
|
||||
'%d/%m/%Y',
|
||||
'%d/%m/%y',
|
||||
'%d/%m/%Y %H:%M:%S',
|
||||
])
|
||||
else:
|
||||
format_expressions.extend([
|
||||
'%m.%d.%Y',
|
||||
'%m/%d/%Y',
|
||||
'%m/%d/%y',
|
||||
'%m/%d/%Y %H:%M:%S',
|
||||
])
|
||||
for expression in format_expressions:
|
||||
|
@@ -1,3 +1,3 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
__version__ = '2015.01.10'
|
||||
__version__ = '2015.01.10.2'
|
||||
|
Reference in New Issue
Block a user