nficano commited on
Commit
e2a1b83
·
2 Parent(s): f2e4f40 3b3f92b

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

Files changed (4) hide show
  1. README.rst +15 -0
  2. pytube/__init__.py +1 -1
  3. pytube/exceptions.py +5 -0
  4. 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.3'
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 Exception('Recursion limit reached')
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 Exception('Premature end of parens in %r' % expr)
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>{0})\.(?P<member>[^(]+)(?:\(+(?P<args>[^()]*)\))?$'
121
- .format(_NAME_RE), expr)
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 Exception(
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 Exception(
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 Exception('Unsupported JS expression %r' % expr)
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'function\(.*?\)\s*\{.*?\})*)\}\s*;',
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 Exception('Could not find JS function %r' % funcname)
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'))