Merge branch 'master' of github.com:NFicano/pytube
Browse files* 'master' of github.com:NFicano/pytube:
whitespace
rearranged
notification of bug fix.
Release version 6.1.4
fixes Cipher Algorithm issue #79
Update README.rst
- README.rst +15 -0
- pytube/__init__.py +1 -1
- pytube/exceptions.py +5 -0
- pytube/jsinterp.py +17 -15
README.rst
CHANGED
@@ -37,6 +37,15 @@ YouTube is the most popular video-sharing platform in the world and as a hacker
|
|
37 |
|
38 |
Finally *pytube* also includes a command-line utility, allowing you to quickly download videos right from terminal.
|
39 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
40 |
Installation
|
41 |
============
|
42 |
|
@@ -77,6 +86,12 @@ and/or optionally choose the filename (``-f`` or ``--filename=``):
|
|
77 |
|
78 |
$ pytube -e mp4 -f Dancing Scene from Pulp Fiction http://www.youtube.com/watch?v=Ik-RsDGPI5Y
|
79 |
|
|
|
|
|
|
|
|
|
|
|
|
|
80 |
|
81 |
|
82 |
Library usage
|
|
|
37 |
|
38 |
Finally *pytube* also includes a command-line utility, allowing you to quickly download videos right from terminal.
|
39 |
|
40 |
+
Fix for CipherError
|
41 |
+
==========================================
|
42 |
+
|
43 |
+
*Update December 2015* If you're using version <=6.1.3, you'll need to run the following to fix *pytube* for some videos.
|
44 |
+
|
45 |
+
.. code:: bash
|
46 |
+
|
47 |
+
pip install -U pytube
|
48 |
+
|
49 |
Installation
|
50 |
============
|
51 |
|
|
|
86 |
|
87 |
$ pytube -e mp4 -f Dancing Scene from Pulp Fiction http://www.youtube.com/watch?v=Ik-RsDGPI5Y
|
88 |
|
89 |
+
You can also specify a resolution or desired filetype:
|
90 |
+
|
91 |
+
.. code:: bash
|
92 |
+
|
93 |
+
$ pytube -e mp4 -r 720p http://www.youtube.com/watch?v=Ik-RsDGPI5Y
|
94 |
+
|
95 |
|
96 |
|
97 |
Library usage
|
pytube/__init__.py
CHANGED
@@ -2,7 +2,7 @@
|
|
2 |
# -*- coding: utf-8 -*-
|
3 |
# flake8: noqa
|
4 |
__title__ = 'pytube'
|
5 |
-
__version__ = '6.1.
|
6 |
__author__ = 'Nick Ficano'
|
7 |
__license__ = 'MIT License'
|
8 |
__copyright__ = 'Copyright 2015 Nick Ficano'
|
|
|
2 |
# -*- coding: utf-8 -*-
|
3 |
# flake8: noqa
|
4 |
__title__ = 'pytube'
|
5 |
+
__version__ = '6.1.4'
|
6 |
__author__ = 'Nick Ficano'
|
7 |
__license__ = 'MIT License'
|
8 |
__copyright__ = 'Copyright 2015 Nick Ficano'
|
pytube/exceptions.py
CHANGED
@@ -4,6 +4,11 @@ class MultipleObjectsReturned(Exception):
|
|
4 |
pass
|
5 |
|
6 |
|
|
|
|
|
|
|
|
|
|
|
7 |
class PytubeError(Exception):
|
8 |
"""Something specific to the wrapper failed.
|
9 |
"""
|
|
|
4 |
pass
|
5 |
|
6 |
|
7 |
+
class ExtractorError(Exception):
|
8 |
+
"""Something specific to the js parser failed.
|
9 |
+
"""
|
10 |
+
|
11 |
+
|
12 |
class PytubeError(Exception):
|
13 |
"""Something specific to the wrapper failed.
|
14 |
"""
|
pytube/jsinterp.py
CHANGED
@@ -1,9 +1,12 @@
|
|
|
|
|
|
1 |
from __future__ import unicode_literals
|
2 |
-
|
3 |
import json
|
4 |
import operator
|
5 |
import re
|
6 |
|
|
|
|
|
7 |
_OPERATORS = [
|
8 |
('|', operator.or_),
|
9 |
('^', operator.xor),
|
@@ -32,7 +35,7 @@ class JSInterpreter(object):
|
|
32 |
|
33 |
def interpret_statement(self, stmt, local_vars, allow_recursion=100):
|
34 |
if allow_recursion < 0:
|
35 |
-
raise
|
36 |
|
37 |
should_abort = False
|
38 |
stmt = stmt.lstrip()
|
@@ -75,7 +78,7 @@ class JSInterpreter(object):
|
|
75 |
expr = json.dumps(sub_result) + remaining_expr
|
76 |
break
|
77 |
else:
|
78 |
-
raise
|
79 |
|
80 |
for op, opfunc in _ASSIGN_OPERATORS:
|
81 |
m = re.match(r'''(?x)
|
@@ -117,8 +120,8 @@ class JSInterpreter(object):
|
|
117 |
pass
|
118 |
|
119 |
m = re.match(
|
120 |
-
r'(?P<var
|
121 |
-
|
122 |
if m:
|
123 |
variable = m.group('var')
|
124 |
member = m.group('member')
|
@@ -184,12 +187,12 @@ class JSInterpreter(object):
|
|
184 |
x, abort = self.interpret_statement(
|
185 |
m.group('x'), local_vars, allow_recursion - 1)
|
186 |
if abort:
|
187 |
-
raise
|
188 |
'Premature left-side return of %s in %r' % (op, expr))
|
189 |
y, abort = self.interpret_statement(
|
190 |
m.group('y'), local_vars, allow_recursion - 1)
|
191 |
if abort:
|
192 |
-
raise
|
193 |
'Premature right-side return of %s in %r' % (op, expr))
|
194 |
return opfunc(x, y)
|
195 |
|
@@ -204,14 +207,14 @@ class JSInterpreter(object):
|
|
204 |
self._functions[fname] = self.extract_function(fname)
|
205 |
return self._functions[fname](argvals)
|
206 |
|
207 |
-
raise
|
208 |
|
209 |
def extract_object(self, objname):
|
210 |
obj = {}
|
211 |
obj_m = re.search(
|
212 |
(r'(?:var\s+)?%s\s*=\s*\{' % re.escape(objname)) +
|
213 |
-
r'\s*(?P<fields>([a-zA-Z$0-9]+\s*:\s*' +
|
214 |
-
r'
|
215 |
self.code)
|
216 |
fields = obj_m.group('fields')
|
217 |
# Currently, it only supports function definitions
|
@@ -221,21 +224,20 @@ class JSInterpreter(object):
|
|
221 |
fields)
|
222 |
for f in fields_m:
|
223 |
argnames = f.group('args').split(',')
|
224 |
-
obj[f.group('key')] = self.build_function(
|
225 |
-
argnames, f.group('code'))
|
226 |
|
227 |
return obj
|
228 |
|
229 |
def extract_function(self, funcname):
|
230 |
func_m = re.search(
|
231 |
r'''(?x)
|
232 |
-
(?:function\s+%s|[{;]%s\s*=\s*function)\s*
|
233 |
\((?P<args>[^)]*)\)\s*
|
234 |
\{(?P<code>[^}]+)\}''' % (
|
235 |
-
re.escape(funcname), re.escape(funcname)),
|
236 |
self.code)
|
237 |
if func_m is None:
|
238 |
-
raise
|
239 |
argnames = func_m.group('args').split(',')
|
240 |
|
241 |
return self.build_function(argnames, func_m.group('code'))
|
|
|
1 |
+
#!/usr/bin/env python
|
2 |
+
# -*- coding: utf-8 -*-
|
3 |
from __future__ import unicode_literals
|
|
|
4 |
import json
|
5 |
import operator
|
6 |
import re
|
7 |
|
8 |
+
from .exceptions import ExtractorError
|
9 |
+
|
10 |
_OPERATORS = [
|
11 |
('|', operator.or_),
|
12 |
('^', operator.xor),
|
|
|
35 |
|
36 |
def interpret_statement(self, stmt, local_vars, allow_recursion=100):
|
37 |
if allow_recursion < 0:
|
38 |
+
raise ExtractorError('Recursion limit reached')
|
39 |
|
40 |
should_abort = False
|
41 |
stmt = stmt.lstrip()
|
|
|
78 |
expr = json.dumps(sub_result) + remaining_expr
|
79 |
break
|
80 |
else:
|
81 |
+
raise ExtractorError('Premature end of parens in %r' % expr)
|
82 |
|
83 |
for op, opfunc in _ASSIGN_OPERATORS:
|
84 |
m = re.match(r'''(?x)
|
|
|
120 |
pass
|
121 |
|
122 |
m = re.match(
|
123 |
+
r'(?P<var>%s)\.(?P<member>[^(]+)(?:\(+(?P<args>[^()]*)\))?$' % _NAME_RE,
|
124 |
+
expr)
|
125 |
if m:
|
126 |
variable = m.group('var')
|
127 |
member = m.group('member')
|
|
|
187 |
x, abort = self.interpret_statement(
|
188 |
m.group('x'), local_vars, allow_recursion - 1)
|
189 |
if abort:
|
190 |
+
raise ExtractorError(
|
191 |
'Premature left-side return of %s in %r' % (op, expr))
|
192 |
y, abort = self.interpret_statement(
|
193 |
m.group('y'), local_vars, allow_recursion - 1)
|
194 |
if abort:
|
195 |
+
raise ExtractorError(
|
196 |
'Premature right-side return of %s in %r' % (op, expr))
|
197 |
return opfunc(x, y)
|
198 |
|
|
|
207 |
self._functions[fname] = self.extract_function(fname)
|
208 |
return self._functions[fname](argvals)
|
209 |
|
210 |
+
raise ExtractorError('Unsupported JS expression %r' % expr)
|
211 |
|
212 |
def extract_object(self, objname):
|
213 |
obj = {}
|
214 |
obj_m = re.search(
|
215 |
(r'(?:var\s+)?%s\s*=\s*\{' % re.escape(objname)) +
|
216 |
+
r'\s*(?P<fields>([a-zA-Z$0-9]+\s*:\s*function\(.*?\)\s*\{.*?\}(?:,\s*)?)*)' +
|
217 |
+
r'\}\s*;',
|
218 |
self.code)
|
219 |
fields = obj_m.group('fields')
|
220 |
# Currently, it only supports function definitions
|
|
|
224 |
fields)
|
225 |
for f in fields_m:
|
226 |
argnames = f.group('args').split(',')
|
227 |
+
obj[f.group('key')] = self.build_function(argnames, f.group('code'))
|
|
|
228 |
|
229 |
return obj
|
230 |
|
231 |
def extract_function(self, funcname):
|
232 |
func_m = re.search(
|
233 |
r'''(?x)
|
234 |
+
(?:function\s+%s|[{;]%s\s*=\s*function|var\s+%s\s*=\s*function)\s*
|
235 |
\((?P<args>[^)]*)\)\s*
|
236 |
\{(?P<code>[^}]+)\}''' % (
|
237 |
+
re.escape(funcname), re.escape(funcname), re.escape(funcname)),
|
238 |
self.code)
|
239 |
if func_m is None:
|
240 |
+
raise ExtractorError('Could not find JS function %r' % funcname)
|
241 |
argnames = func_m.group('args').split(',')
|
242 |
|
243 |
return self.build_function(argnames, func_m.group('code'))
|