Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
np1 committed Jul 24, 2014
2 parents 1589217 + a024107 commit 9668af4
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 18 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
24 July 2014
Version 0.3.58

[Feature] - Added cancel download function (stav) (#47)
[Bugfix] - Update signature decryption to work with YouTube changes (#48)

-------------------------------------------------------------------------------

16 July 2014
Version 0.3.56

Expand Down
6 changes: 3 additions & 3 deletions docs-sphinx/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
# sys.path.insert(0, os.path.abspath('..'))
sys.path.insert(0, "../pafy")
sys.path.insert(0, os.path.abspath("../pafy"))

# -- General configuration ------------------------------------------------

Expand Down Expand Up @@ -55,9 +55,9 @@
# built documents.
#
# The short X.Y version.
version = '0.3.56'
version = '0.3.58'
# The full version, including alpha/beta/rc tags.
release = '0.3.56'
release = '0.3.58'

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
Expand Down
69 changes: 58 additions & 11 deletions pafy/pafy.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# -*- coding: utf-8 -*-

"""
Python API for YouTube.
pafy.py.
Python library to retrieve YouTube content and metadata
https://github.com/np1/pafy
Expand All @@ -25,7 +27,7 @@

from __future__ import unicode_literals

__version__ = "0.3.56"
__version__ = "0.3.58"
__author__ = "nagev"
__license__ = "GPLv3"

Expand Down Expand Up @@ -307,7 +309,7 @@ def _get_other_funcs(primary_func, js):
call = re.compile(r'(?:[$\w+])=([$\w]+)\(((?:\w+,?)+)\)$')

# dot notation function call; X=O.F(A,B,C..)
dotcall = re.compile(r'(?:[$\w+])=([$\w]+)\.([$\w]+)\(((?:\w+,?)+)\)$')
dotcall = re.compile(r'(?:[$\w+]=)?([$\w]+)\.([$\w]+)\(((?:\w+,?)+)\)$')

functions = {}

Expand Down Expand Up @@ -380,6 +382,7 @@ def _get_func_from_call(caller, name, arguments, js_url):
def _solve(f, js_url):
"""Solve basic javascript function. Return solution value (str). """
# pylint: disable=R0914,R0912
resv = "slice|splice|reverse"
patterns = {
'split_or_join': r'(\w+)=\1\.(?:split|join)\(""\)$',
'func_call': r'(\w+)=([$\w]+)\(((?:\w+,?)+)\)$',
Expand All @@ -388,10 +391,15 @@ def _solve(f, js_url):
'x3': r'(\w+)\[(\w+)\]=(\w+)$',
'return': r'return (\w+)(\.join\(""\))?$',
'reverse': r'(\w+)=(\w+)\.reverse\(\)$',
'return_reverse': r'return (\w+)\.reverse()',
'reverse_noass': r'(\w+)\.reverse\(\)$',
'return_reverse': r'return (\w+)\.reverse()$',
'slice': r'(\w+)=(\w+)\.slice\((\w+)\)$',
'splice_noass': r'([$\w]+)\.splice\(([$\w]+)\,([$\w]+)\)$',
'return_slice': r'return (\w+)\.slice\((\w+)\)$',
'func_call_dict': r'(\w)=([$\w]+)\.(?!slice)([$\w]+)\(((?:\w+,?)+)\)$'
'func_call_dict': r'(\w)=([$\w]+)\.(?!%s)([$\w]+)\(((?:\w+,?)+)\)$'
% resv,
'func_call_dict_noret': r'([$\w]+)\.(?!%s)([$\w]+)\(((?:\w+,?)+)\)$'
% resv
}

parts = f['body'].split(";")
Expand All @@ -418,6 +426,19 @@ def _solve(f, js_url):
newfunc = _get_func_from_call(f, funcname, args.split(","), js_url)
f['args'][lhs] = _solve(newfunc, js_url)

elif name == "func_call_dict_noret":
dic, key, args = m.group(1, 2, 3)
funcname = "%s.%s" % (dic, key)
newfunc = _get_func_from_call(f, funcname, args.split(","), js_url)
_solve.expect_noret = True
changed_args = _solve(newfunc, js_url)
_solve.expect_noret = False

for arg in f['args']:

if arg in changed_args:
f['args'][arg] = changed_args[arg]

elif name == "func_call":
lhs, funcname, args = m.group(1, 2, 3)
newfunc = _get_func_from_call(f, funcname, args.split(","), js_url)
Expand Down Expand Up @@ -445,6 +466,13 @@ def _solve(f, js_url):
elif name == "reverse":
f['args'][m.group(1)] = _getval(m.group(2), f['args'])[::-1]

elif name == "reverse_noass":
f['args'][m.group(1)] = _getval(m.group(1), f['args'])[::-1]

elif name == "splice_noass":
a, b, c = [_getval(x, f['args']) for x in m.group(1, 2, 3)]
f['args'][m.group(1)] = a[:b] + a[b + c:]

elif name == "return_reverse":
return f['args'][m.group(1)][::-1]

Expand All @@ -456,7 +484,12 @@ def _solve(f, js_url):
a, b, c = [_getval(x, f['args']) for x in m.group(1, 2, 3)]
f['args'][m.group(1)] = b[c:]

raise IOError("Processed js funtion parts without finding return")
if _solve.expect_noret:
return f['args']

else:
raise IOError("Processed js funtion parts without finding return")



def _decodesig(sig, js_url):
Expand Down Expand Up @@ -588,6 +621,7 @@ def safeint(x):
self._url = None
self._rawurl = sm['url']
self._sig = sm['s'] if self.encrypted else sm.get("sig")
self._active = False

if self.mediatype == "audio":
self._dimensions = (0, 0)
Expand Down Expand Up @@ -748,15 +782,21 @@ def get_filesize(self):

return self._fsize

def cancel(self):
""" Cancel an active download. """
if self._active:
self._active = False
return True

def download(self, filepath="", quiet=False, callback=lambda *x: None,
meta=False):
""" Download. Use quiet=True to supress output. Return filename.
Use meta=True to append video id and itag to generated filename
"""
# pylint: disable=R0914
# Too many local variables - who cares?
# pylint: disable=R0912,R0914
# Too many branches, too many local vars
savedir = filename = ""

if filepath and os.path.isdir(filepath):
Expand Down Expand Up @@ -800,7 +840,9 @@ def download(self, filepath="", quiet=False, callback=lambda *x: None,
response = resuming_opener.open(self.url)
bytesdone = offset

while True:
self._active = True

while self._active:
chunk = response.read(chunksize)
outfh.write(chunk)
elapsed = time.time() - t0
Expand All @@ -821,8 +863,13 @@ def download(self, filepath="", quiet=False, callback=lambda *x: None,
if callback:
callback(total, *progress_stats)

os.rename(temp_filepath, filepath)
return filepath
if self._active:
os.rename(temp_filepath, filepath)
return filepath

else:
outfh.close()
return temp_filepath


class Pafy(object):
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
name='Pafy',
packages=['pafy'],
scripts=['scripts/ytdl'],
version='0.3.56',
version='0.3.58',
description="Retrieve YouTube content and metadata",
keywords=["Pafy", "API", "YouTube", "youtube", "download", "video"],
author="nagev",
Expand Down
7 changes: 4 additions & 3 deletions tests/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,9 +165,10 @@ def test_video_properties(self):
video['description'])

for prop in self.properties:
paf_prop = getattr(video['pafy'], prop)
exp_prop = video[prop]
self.assertEqual(paf_prop, exp_prop)
if prop != "thumb":
paf_prop = getattr(video['pafy'], prop)
exp_prop = video[prop]
self.assertEqual(paf_prop, exp_prop)

self.assertNotEqual(video.__repr__(), None)

Expand Down

0 comments on commit 9668af4

Please sign in to comment.