All pastes #222676 Raw Edit

youtube-dl patch

public unlisted text v1 · immutable
#222676 ·published 2006-10-26 14:03 UTC
rendered paste body
--- /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')