Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
Conflicts:
	CHANGELOG
	pafy/pafy.py
	setup.py
  • Loading branch information
np1 committed Jun 19, 2014
2 parents c8aa287 + 914aee6 commit 68cbddb
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 13 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
19 June 2014
Version 0.3.50

[Feature] - Resume partially downloaded files (issue #32)
[Bugfix] - Fix javascript function extraction

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

12 June 2014
Version 0.3.48

Expand Down
52 changes: 45 additions & 7 deletions pafy/pafy.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

from __future__ import unicode_literals

__version__ = "0.3.48"
__version__ = "0.3.50"
__author__ = "nagev"
__license__ = "GPLv3"

Expand Down Expand Up @@ -275,7 +275,7 @@ def _extract_function_from_js(name, js):
def _get_mainfunc_from_js(js):
""" Return main signature decryption function from javascript as dict. """
dbg("Scanning js for main function.")
m = re.search(r'\w\.sig\|\|(\w+)\(\w+\.\w+\)', js)
m = re.search(r'\w\.sig\|\|([$\w]+)\(\w+\.\w+\)', js)
funcname = m.group(1)
dbg("Found main function: %s", funcname)
function = _extract_function_from_js(funcname, js)
Expand Down Expand Up @@ -685,6 +685,15 @@ def download(self, filepath="", quiet=False, callback=None):
""" Download. Use quiet=True to supress output. Return filename. """
# pylint: disable=R0914
# Too many local variables - who cares?

def safe_filename(ff):
""" Remove troublesome characters in basename. """
d, b = os.path.split(ff)
ok = re.compile(r'[^\\/?*$\'"%&:<>|]')
b = "".join(x if ok.match(x) else "_" for x in b)
ff = os.path.join(d, b)
return ff.encode("utf8", "ignore")

status_string = (' {:,} Bytes [{:.2%}] received. Rate: [{:4.0f} '
'KB/s]. ETA: [{:.0f} secs]')

Expand All @@ -696,21 +705,49 @@ def download(self, filepath="", quiet=False, callback=None):
total = int(response.info()['Content-Length'].strip())
chunksize, bytesdone, t0 = 16384, 0, time.time()
fname = filepath or self.filename
dirname = os.path.dirname(fname)

if dirname and not os.path.isdir(dirname):
raise IOError("Invalid directory: %s" % dirname)

elif os.path.isdir(fname):
fname = os.path.join(fname, self.filename)

temp = os.path.join(os.path.dirname(fname), self._parent.title)
tempfname = "%s_%s_%s.part" % (temp, self._parent.videoid, self.itag)
safename, fmode, offset = safe_filename(tempfname), "wb", 0

if os.path.exists(tempfname) and os.stat(tempfname).st_size < total:
offset = os.stat(tempfname).st_size
fmode = "ab"

elif os.path.exists(safename) and os.stat(safename).st_size < total:
offset = os.stat(safename).st_size
fmode = "ab"

try:
outfh = open(fname, 'wb')
outfh = open(tempfname, fmode)

except IOError:
ok = re.compile(r'[^\\/?*$\'"%&:<>|]')
fname = "".join(x if ok.match(x) else "_" for x in self.filename)
outfh = open(fname.encode("utf8", "ignore"), 'wb')
# remove special chars from filename
fname = safe_filename(fname)
tempfname = safename
outfh = open(tempfname, fmode)

if offset:
# partial file exists, resume download
resuming_opener = build_opener()
resuming_opener.addheaders = [('User-Agent', g.ua),
("Range", "bytes=%s-" % offset)]
response = resuming_opener.open(self.url)
bytesdone = offset

while True:
chunk = response.read(chunksize)
outfh.write(chunk)
elapsed = time.time() - t0
bytesdone += len(chunk)
rate = (bytesdone / 1024) / elapsed
rate = ((bytesdone - offset) / 1024) / elapsed
eta = (total - bytesdone) / (rate * 1024)
progress_stats = (bytesdone, bytesdone * 1.0 / total, rate, eta)

Expand All @@ -726,6 +763,7 @@ def download(self, filepath="", quiet=False, callback=None):
if callback:
callback(total, *progress_stats)

os.rename(tempfname, fname)
return fname


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.48',
version='0.3.50',
description="Python API for YouTube, query and download YouTube content",
keywords=["Pafy", "API", "YouTube", "youtube", "download", "video"],
author="nagev",
Expand Down
32 changes: 27 additions & 5 deletions tests/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,35 @@ def get_all_funcs(self):
@stdout_to_null
def test_pafy_download(self):
""" Test downloading. """

callback = lambda a, b, c, d, e: 0
vid = pafy.new("DsAn_n6O5Ns", gdata=True)
vstream = vid.audiostreams[-1]
name = vstream.download(filepath="file/", callback=callback)
name = vstream.download(callback=callback)
self.assertEqual(name[0:5], "WASTE")

@stdout_to_null
def test_pafy_download_resume(self):
""" Test resuming a partial download. """
tempname = "WASTE 2 SECONDS OF YOUR LIFE_DsAn_n6O5Ns_171.part"
open(tempname, "w").write("abc")
vid = pafy.new("DsAn_n6O5Ns", gdata=True)
vstream = vid.audiostreams[-1].download()
name = "WASTE 2 SECONDS OF YOUR LIFE.ogg"
self.assertEqual(22675, os.stat(name).st_size)

def test_pafy_download_invalid_dirname(self):
""" Test user specified invalid path. """
vid = pafy.new("DsAn_n6O5Ns", gdata=True)
self.assertRaises(IOError, vid.audiostreams[-1].download, "/bad/h/")

@stdout_to_null
def test_pafy_download_to_dir(self):
""" Test user specified path. """
vid = pafy.new("DsAn_n6O5Ns", gdata=True)
vstream = vid.audiostreams[-1].download("/tmp")
name = "/tmp/WASTE 2 SECONDS OF YOUR LIFE.ogg"
self.assertEqual(22675, os.stat(name).st_size)

def test_lazy_pafy(self):
""" Test create pafy object without fetching data. """

Expand Down Expand Up @@ -276,15 +298,15 @@ def test_misc_tests(self):
'identifier': 'https://youtu.be/watch?v=07FYdnEawAQ',
'videoid': '07FYdnEawAQ',
'title': 'Justin Timberlake - Tunnel Vision (Explicit)',
'length': 420,
'duration': '00:07:00',
'length': 419,
'duration': '00:06:59',
'author': 'justintimberlakeVEVO',
'username': 'justintimberlakeVEVO',
'published': '2013-07-03 22:00:16',
'thumb': 'http://i1.ytimg.com/vi/07FYdnEawAQ/default.jpg',
'category': 'Music',
'description': '55e8e6e2b219712bf94d67c2434530474a503265',
'bestsize': 80664244,
'bestsize': 79885533,
'all streams': 21,
'normal streams': 6,
'video streams': 13,
Expand Down

0 comments on commit 68cbddb

Please sign in to comment.