--- /usr/bin/youtube-dl 2006-09-27 17:58:06.000000000 -0400
+++ youtube-dl 2006-10-25 19:29:26.000000000 -0400
@@ -30,6 +30,9 @@
import httplib
import urllib2
import re
+import os
+import os.path
+import gzip
# First off, check Python and refuse to run
if sys.hexversion < 0x020400f0:
@@ -46,11 +49,15 @@
const_login_post_str = 'current_form=loginForm&next=%%2F&username=%s&password=%s&action_login=Log+In'
const_age_url_str = 'http://www.youtube.com/verify_age?next_url=/watch%%3Fv%%3D%s'
const_age_post_str = 'next_url=%%2Fwatch%%3Fv%%3D%s&action_confirm=Confirm'
-const_video_url_params_re = re.compile(r'player2\.swf\?([^"]+)"', re.M)
const_video_url_real_str = 'http://www.youtube.com/get_video?%s'
const_1k = 1024
const_block_size = 10 * const_1k
+# Global regular expressions for searching through the HTML file.
+const_video_url_params_re = re.compile(r'\s+// <!\[CDATA\[\n^\s+var fo = new SWFObject\("/player2\.swf\?([^"]+)"', re.M)
+const_video_title_re = re.compile(r'^\s*<h1\s+id="video_title">\s*([^<]+)\s*</h1>\s*$', re.M)
+const_video_removed_re = re.compile(r'^\s+<div class="errorBox">\n\s+This video has been removed by the user.\s*$', re.M)
+
# Print error message, followed by standard advice information, and then exit
def error_advice_exit(error_text):
global exit_failure
@@ -98,10 +105,30 @@
sys.stdout.write(str)
sys.stdout.flush()
+# Safe string for saving file names.
+def safe_html_string(str):
+ const_html_space_re = re.compile(r'\s+')
+ const_html_invalid_re = re.compile(r'[^0-9A-Za-z _-]+')
+ const_html_entity_re = re.compile(r'&[^ ;]+')
+
+ # This is messy. Remove all HTML entities and then remove anything that
+ # is not a valid filename character. The "valid" characters are a small
+ # subset of what is allowed for a filename.
+ str = const_html_entity_re.sub(r'', str)
+ str = const_html_invalid_re.sub(r'', str)
+ str = const_html_space_re.sub(r'_', str)
+
+ # XXX FIX ME. The length of the the filename should be checked.
+ # There must be room for the video URL id so it is an unique name.
+
+ return str
+
# Create the command line options parser and parse command line
cmdl_usage = 'usage: %prog [options] video_url'
-cmdl_version = '2006.09.25'
+cmdl_version = '2006.10.25'
cmdl_parser = optparse.OptionParser(usage=cmdl_usage, version=cmdl_version, conflict_handler='resolve')
+cmdl_parser.add_option('-d', '--dir', action='store_true', dest='directory', help='Save gzipped HTML to "html/" dir')
+cmdl_parser.add_option('-a', '--auto-name', action='store_true', dest='auto_name', help='Automatically name file based on title and video ID')
cmdl_parser.add_option('-h', '--help', action='help', help='print this help text and exit')
cmdl_parser.add_option('-v', '--version', action='version', help='print program version and exit')
cmdl_parser.add_option('-u', '--username', dest='username', metavar='USERNAME', help='account username')
@@ -128,7 +155,16 @@
if not cmdl_opts.outfile is None and cmdl_opts.simulate:
sys.stderr.write('Warning: video file name given but will not be used.\n')
-# Get output file name
+# Cannot create a name based on the video title and specify an output.
+if cmdl_opts.auto_name is True and not cmdl_opts.outfile is None:
+ sys.exit('Error: Cannot automatically name the file and specify one.\n')
+
+# Verify both or none present
+if ((cmdl_opts.username is None and not cmdl_opts.password is None) or
+ (not cmdl_opts.username is None and cmdl_opts.password is None)):
+ sys.exit('Error: both username and password must be given, or none.')
+
+# Get output file name unless they want to automatically name based on the HTML.
if cmdl_opts.outfile is None:
video_filename = '%s.flv' % video_url_id
else:
@@ -138,13 +174,8 @@
if not video_filename.lower().endswith('.flv'):
sys.stderr.write('Warning: video file name does not end in .flv\n')
-# Verify both or none present
-if ((cmdl_opts.username is None and not cmdl_opts.password is None) or
- (not cmdl_opts.username is None and cmdl_opts.password is None)):
- sys.exit('Error: both username and password must be given, or none.')
-
-# Test writable file
-if not cmdl_opts.simulate:
+# Test writable file unless we are going to auto name it.
+if not cmdl_opts.simulate and cmdl_opts.auto_name is False:
try:
disk_test = open(video_filename, 'wb')
disk_test.close()
@@ -197,6 +228,42 @@
except KeyboardInterrupt:
sys.exit('\n')
+# Save a copy for future reference
+try:
+ if cmdl_opts.directory is True:
+ cond_print("Saving HTML file 'html/%s.html.gz'\n" % video_url_id)
+ if os.path.exists("html") is False:
+ os.mkdir("html")
+
+ myf = gzip.GzipFile("html/%s.html.gz" % video_url_id, "wb")
+ myf.write(video_webpage)
+ myf.flush()
+ myf.close()
+
+except (OSError, IOError):
+ sys.exit('Error: unable to open html/%s.html.gz for writing.' % video_url_id)
+
+except KeyboardInterrupt:
+ sys.exit('\n')
+
+# If requested, name the file based on the video title
+try:
+ if cmdl_opts.auto_name is True:
+ video_title_match = const_video_title_re.search(video_webpage)
+
+ if video_title_match is None:
+ cond_print('failed to get the video title.\n')
+ error_advice_exit('unable to get video title')
+
+ # Replace any characters we don't want to show up in a file name.
+ video_title_safe = safe_html_string(video_title_match.group(1))
+
+ # Append the video URL id so it's unique in case of video title collisions.
+ video_filename = '%s_-_%s.flv' % (video_title_safe, video_url_id)
+
+except KeyboardInterrupt:
+ sys.exit('\n')
+
# Extract needed video URL parameters
try:
cond_print('Extracting video URL parameters... ')
@@ -204,7 +271,11 @@
if video_url_params_mo is None:
cond_print('failed.\n')
- error_advice_exit('unable to extract URL parameters')
+
+ if not const_video_removed_re.search(video_webpage) is None:
+ sys.exit('Error: Video removed by the user')
+ else:
+ error_advice_exit('unable to extract URL parameters')
video_url_params = video_url_params_mo.group(1)
video_url_real = const_video_url_real_str % video_url_params
@@ -246,5 +317,8 @@
cond_print('failed.\n')
error_advice_exit('unable to download video data')
+except (OSError, IOError):
+ sys.exit('Error: unable to open %s for writing.' % video_filename)
+
except KeyboardInterrupt:
sys.exit('\n')