From 8e30e9ed176eb726c31caafae61b05df11457b8e Mon Sep 17 00:00:00 2001 From: Marco 'don' Kaulea Date: Fri, 24 Jul 2015 21:45:00 +0200 Subject: [PATCH] Add --socks-proxy parameter to use SOCKS proxy This allows the user to specify a socks proxy to tunnel the connection through. This feature requires that PySocks is available. If the user tries to use a SOCKS proxy without PySocks available an error is printed and the process is aborted. --- youtube_dl/YoutubeDL.py | 33 +++++++++++++++++++++++++++++++-- youtube_dl/__init__.py | 4 ++++ youtube_dl/options.py | 5 +++++ youtube_dl/utils.py | 17 +++++++++++++++++ 4 files changed, 57 insertions(+), 2 deletions(-) diff --git a/youtube_dl/YoutubeDL.py b/youtube_dl/YoutubeDL.py index 982e658ce..a4b1b1c3a 100755 --- a/youtube_dl/YoutubeDL.py +++ b/youtube_dl/YoutubeDL.py @@ -52,6 +52,7 @@ from .utils import ( locked_file, make_HTTPS_handler, MaxDownloadsReached, + OptionalDependencyNotFound, PagedList, parse_filesize, PerRequestProxyHandler, @@ -1953,11 +1954,39 @@ class YoutubeDL(object): proxies['https'] = proxies['http'] proxy_handler = PerRequestProxyHandler(proxies) + socks_handler = None + opts_socks = self.params.get('socksproxy') + if opts_socks is not None and opts_socks: + # Try to import the dependencies for this feature + try: + import socks + except ImportError: + raise OptionalDependencyNotFound(module_name='socks', + feature_name='--socks-proxy') + try: + from sockshandler import SocksiPyHandler + except ImportError: + raise OptionalDependencyNotFound(module_name='sockshandler', + feature_name='--socks-proxy') + + pair = opts_socks.split(':') + if len(pair) == 2: + socks_handler = SocksiPyHandler(socks.PROXY_TYPE_SOCKS5, + pair[0], + int(pair[1])) + else: + socks_handler = SocksiPyHandler(socks.PROXY_TYPE_SOCKS5, + 'localhost', + int(pair[0])) + debuglevel = 1 if self.params.get('debug_printtraffic') else 0 https_handler = make_HTTPS_handler(self.params, debuglevel=debuglevel) ydlh = YoutubeDLHandler(self.params, debuglevel=debuglevel) - opener = compat_urllib_request.build_opener( - proxy_handler, https_handler, cookie_processor, ydlh) + proxy_list = [] + if socks_handler: + proxy_list.append(socks_handler) + proxy_list += [proxy_handler, https_handler, cookie_processor, ydlh] + opener = compat_urllib_request.build_opener(*proxy_list) # Delete the default user-agent header, which would otherwise apply in # cases where our custom HTTP handler doesn't come into play diff --git a/youtube_dl/__init__.py b/youtube_dl/__init__.py index 55b22c889..a9ebe00ae 100644 --- a/youtube_dl/__init__.py +++ b/youtube_dl/__init__.py @@ -29,6 +29,7 @@ from .utils import ( DownloadError, match_filter_func, MaxDownloadsReached, + OptionalDependencyNotFound, preferredencoding, read_batch_urls, SameFileError, @@ -346,6 +347,7 @@ def _real_main(argv=None): 'nocheckcertificate': opts.no_check_certificate, 'prefer_insecure': opts.prefer_insecure, 'proxy': opts.proxy, + 'socksproxy': opts.socksproxy, 'socket_timeout': opts.socket_timeout, 'bidi_workaround': opts.bidi_workaround, 'debug_printtraffic': opts.debug_printtraffic, @@ -414,5 +416,7 @@ def main(argv=None): sys.exit('ERROR: fixed output name but more than one file to download') except KeyboardInterrupt: sys.exit('\nERROR: Interrupted by user') + except OptionalDependencyNotFound as e: + sys.exit(str(e)) __all__ = ['main', 'YoutubeDL', 'gen_extractors', 'list_extractors'] diff --git a/youtube_dl/options.py b/youtube_dl/options.py index 8c4ff12bd..dc6d37285 100644 --- a/youtube_dl/options.py +++ b/youtube_dl/options.py @@ -181,6 +181,11 @@ def parseOpts(overrideArguments=None): '--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( + '--socks-proxy', dest='socksproxy', default=None, metavar='URL', + help=('Use the specified socks proxy. Pass in an empty string ' + '(--socks-proxy "") for direct connection. This feature requires' + 'the pysocks library.')) network.add_option( '--socket-timeout', dest='socket_timeout', type=float, default=None, metavar='SECONDS', diff --git a/youtube_dl/utils.py b/youtube_dl/utils.py index b7a423166..9492386d8 100644 --- a/youtube_dl/utils.py +++ b/youtube_dl/utils.py @@ -586,6 +586,23 @@ class ContentTooShortError(Exception): self.expected = expected +class OptionalDependencyNotFound(Exception): + """Optional dependency not found + + This exception may be raised by YoutubeDL, when the user tries to use a + feature that requires an optional dependency which could not be found. + """ + + def __init__(self, module_name, feature_name): + self.module_name = str(module_name) + self.feature_name = str(feature_name) + + def __str__(self): + return ("Unable to use '{feature}', because it depends on '{module}' " + "which was not found.").format( + feature=self.feature_name, module=self.module_name) + + def _create_http_connection(ydl_handler, http_class, is_https, *args, **kwargs): # Working around python 2 bug (see http://bugs.python.org/issue17849) by limiting # expected HTTP responses to meet HTTP/1.0 or later (see also