Browse Source

use argparse + modularize upload

Arnau Sanchez 8 years ago
parent
commit
6357b5adf5
2 changed files with 180 additions and 185 deletions
  1. 34 185
      youtube_upload/main.py
  2. 146 0
      youtube_upload/upload.py

+ 34 - 185
youtube_upload/main.py

@@ -15,236 +15,85 @@ Upload a video to Youtube from the command-line.
     pxzZ-fYjeYs
 """
 
-import os
 import sys
-import optparse
-import collections
-import webbrowser
+import argparse
 
 import googleapiclient.errors
 import oauth2client
 
-from . import auth
-from . import upload_video
-from . import categories
+from . import upload
 from . import lib
-from . import playlists
 
-# http://code.google.com/p/python-progressbar (>= 2.3)
-try:
-    import progressbar
-except ImportError:
-    progressbar = None
-
-class InvalidCategory(Exception): pass
 class OptionsError(Exception): pass
-class AuthenticationError(Exception): pass
-class RequestError(Exception): pass
 
 EXIT_CODES = {
     OptionsError: 2,
-    InvalidCategory: 3,
-    RequestError: 3,
-    AuthenticationError: 4,
+    upload.InvalidCategory: 3,
+    upload.RequestError: 3,
+    upload.AuthenticationError: 4,
     oauth2client.client.FlowExchangeError: 4,
     NotImplementedError: 5,
 }
 
 WATCH_VIDEO_URL = "https://www.youtube.com/watch?v={id}"
 
-debug = lib.debug
-struct = collections.namedtuple
-
-def open_link(url):
-    """Opens a URL link in the client's browser."""
-    webbrowser.open(url)
-    
-def get_progress_info():
-    """Return a function callback to update the progressbar."""
-    progressinfo = struct("ProgressInfo", ["callback", "finish"])
-
-    if progressbar:
-        bar = progressbar.ProgressBar(widgets=[
-            progressbar.Percentage(), ' ',
-            progressbar.Bar(), ' ',
-            progressbar.FileTransferSpeed(),
-        ])
-        def _callback(total_size, completed):
-            if not hasattr(bar, "next_update"):
-                if hasattr(bar, "maxval"):
-                    bar.maxval = total_size
-                else:
-                    bar.max_value = total_size
-                bar.start()
-            bar.update(completed)
-        def _finish():
-            if hasattr(bar, "next_update"):
-                return bar.finish()
-        return progressinfo(callback=_callback, finish=_finish)
-    else:
-        return progressinfo(callback=None, finish=lambda: True)
-
-def get_category_id(category):
-    """Return category ID from its name."""
-    if category:
-        if category in categories.IDS:
-            ncategory = categories.IDS[category]
-            debug("Using category: {0} (id={1})".format(category, ncategory))
-            return str(categories.IDS[category])
-        else:
-            msg = "{0} is not a valid category".format(category)
-            raise InvalidCategory(msg)
-
-def upload_youtube_video(youtube, options, video_path, total_videos, index):
-    """Upload video with index (for split videos)."""
-    u = lib.to_utf8
-    title = u(options.title)
-    if hasattr(u('string'), 'decode'):   
-        description = u(options.description or "").decode("string-escape")
-    else:
-        description = options.description
-    if options.publish_at:    
-      debug("Your video will remain private until specified date.")
-      
-    tags = [u(s.strip()) for s in (options.tags or "").split(",")]
-    ns = dict(title=title, n=index+1, total=total_videos)
-    title_template = u(options.title_template)
-    complete_title = (title_template.format(**ns) if total_videos > 1 else title)
-    progress = get_progress_info()
-    category_id = get_category_id(options.category)
-    request_body = {
-        "snippet": {
-            "title": complete_title,
-            "description": description,
-            "categoryId": category_id,
-            "tags": tags,
-            "defaultLanguage": options.default_language,
-            "defaultAudioLanguage": options.default_audio_language,
-
-        },
-        "status": {
-            "privacyStatus": ("private" if options.publish_at else options.privacy),
-            "publishAt": options.publish_at,
-
-        },
-        "recordingDetails": {
-            "location": lib.string_to_dict(options.location),
-            "recordingDate": options.recording_date,
-        },
-    }
-
-    debug("Start upload: {0}".format(video_path))
-    try:
-        video_id = upload_video.upload(youtube, video_path, 
-            request_body, progress_callback=progress.callback)
-    finally:
-        progress.finish()
-    return video_id
-
-def get_youtube_handler(options):
-    """Return the API Youtube object."""
-    home = os.path.expanduser("~")
-    default_client_secrets = lib.get_first_existing_filename(
-        [sys.prefix, os.path.join(sys.prefix, "local")],
-        "share/youtube_upload/client_secrets.json")  
-    default_credentials = os.path.join(home, ".youtube-upload-credentials.json")
-    client_secrets = options.client_secrets or default_client_secrets or \
-        os.path.join(home, ".client_secrets.json")
-    credentials = options.credentials_file or default_credentials
-    debug("Using client secrets: {0}".format(client_secrets))
-    debug("Using credentials file: {0}".format(credentials))
-    get_code_callback = (auth.browser.get_code 
-        if options.auth_browser else auth.console.get_code)
-    return auth.get_resource(client_secrets, credentials,
-        get_code_callback=get_code_callback)
-
-def parse_options_error(parser, options):
-    """Check errors in options."""
-    required_options = ["title"]
-    missing = [opt for opt in required_options if not getattr(options, opt)]
-    if missing:
-        parser.print_usage()
-        msg = "Some required option are missing: {0}".format(", ".join(missing))
-        raise OptionsError(msg)
-
-def run_main(parser, options, args, output=sys.stdout):
-    """Run the main scripts from the parsed options/args."""
-    parse_options_error(parser, options)
-    youtube = get_youtube_handler(options)
-
-    if youtube:
-        for index, video_path in enumerate(args):
-            video_id = upload_youtube_video(youtube, options, video_path, len(args), index)
-            video_url = WATCH_VIDEO_URL.format(id=video_id)
-            debug("Video URL: {0}".format(video_url))
-            if options.open_link:
-                open_link(video_url) #Opens the Youtube Video's link in a webbrowser
-                
-            if options.thumb:
-                youtube.thumbnails().set(videoId=video_id, media_body=options.thumb).execute()
-            if options.playlist:
-                playlists.add_video_to_playlist(youtube, video_id, 
-                    title=lib.to_utf8(options.playlist), privacy=options.privacy)
-            output.write(video_id + "\n")
-    else:
-        raise AuthenticationError("Cannot get youtube resource")
-
 def main(arguments):
     """Upload videos to Youtube."""
-    usage = """Usage: %prog [OPTIONS] VIDEO [VIDEO2 ...]
+    usage = """Usage: %prog [OPTIONS] VIDEO
 
     Upload videos to Youtube."""
-    parser = optparse.OptionParser(usage)
+    parser = argparse.ArgumentParser(usage)
 
     # Video metadata
-    parser.add_option('-t', '--title', dest='title', type="string",
+    parser.add_argument('-t', '--title', dest='title', type=str,
         help='Video title')
-    parser.add_option('-c', '--category', dest='category', type="string",
+    parser.add_argument('-c', '--category', dest='category', type=str,
         help='Video category')
-    parser.add_option('-d', '--description', dest='description', type="string",
+    parser.add_argument('-d', '--description', dest='description', type=str,
         help='Video description')
-    parser.add_option('', '--tags', dest='tags', type="string",
+    parser.add_argument('--tags', dest='tags', type=str,
         help='Video tags (separated by commas: "tag1, tag2,...")')
-    parser.add_option('', '--privacy', dest='privacy', metavar="STRING",
+    parser.add_argument('--privacy', dest='privacy', metavar="STRING",
         default="public", help='Privacy status (public | unlisted | private)')
-    parser.add_option('', '--publish-at', dest='publish_at', metavar="datetime",
+    parser.add_argument('--publish-at', dest='publish_at', metavar="datetime",
        default=None, help='Publish date (ISO 8601): YYYY-MM-DDThh:mm:ss.sZ')
-    parser.add_option('', '--location', dest='location', type="string",
+    parser.add_argument('--location', dest='location', type=str,
         default=None, metavar="latitude=VAL,longitude=VAL[,altitude=VAL]",
         help='Video location"')
-    parser.add_option('', '--recording-date', dest='recording_date', metavar="datetime",
+    parser.add_argument('--recording-date', dest='recording_date', metavar="datetime",
         default=None, help="Recording date (ISO 8601): YYYY-MM-DDThh:mm:ss.sZ")
-    parser.add_option('', '--default-language', dest='default_language', type="string",
+    parser.add_argument('--default-language', dest='default_language', type=str,
         default=None, metavar="string", 
         help="Default language (ISO 639-1: en | fr | de | ...)")
-    parser.add_option('', '--default-audio-language', dest='default_audio_language', type="string",
+    parser.add_argument('--default-audio-language', dest='default_audio_language', type=str,
         default=None, metavar="string", 
         help="Default audio language (ISO 639-1: en | fr | de | ...)")
-    parser.add_option('', '--thumbnail', dest='thumb', type="string", metavar="FILE", 
+    parser.add_argument('--thumbnail', dest='thumb', type=str, metavar="FILE", 
         help='Image file to use as video thumbnail (JPEG or PNG)')
-    parser.add_option('', '--playlist', dest='playlist', type="string",
+    parser.add_argument('--playlist', dest='playlist', type=str,
         help='Playlist title (if it does not exist, it will be created)')
-    parser.add_option('', '--title-template', dest='title_template',
-        type="string", default="{title} [{n}/{total}]", metavar="string",
-        help='Template for multiple videos (default: {title} [{n}/{total}])')
 
     # Authentication
-    parser.add_option('', '--client-secrets', dest='client_secrets',
-        type="string", help='Client secrets JSON file')
-    parser.add_option('', '--credentials-file', dest='credentials_file',
-        type="string", help='Credentials JSON file')
-    parser.add_option('', '--auth-browser', dest='auth_browser', action='store_true',
+    parser.add_argument('--client-secrets', dest='client_secrets',
+        type=str, help='Client secrets JSON file')
+    parser.add_argument('--credentials-file', dest='credentials_file',
+        type=str, help='Credentials JSON file')
+    parser.add_argument('--auth-browser', dest='auth_browser', action='store_true',
         help='Open a GUI browser to authenticate if required')
 
     #Additional options
-    parser.add_option('', '--open-link', dest='open_link', action='store_true',
+    parser.add_argument('--open-link', dest='open_link', action='store_true',
         help='Opens a url in a web browser to display the uploaded video')
 
-    options, args = parser.parse_args(arguments)
-    try:
-        run_main(parser, options, args)
-    except googleapiclient.errors.HttpError as error:
-        raise RequestError("Server response: {0}".format(bytes.decode(error.content).strip()))
+    # Positional arguments
+    parser.add_argument('video_path', metavar="VIDEO PATH", type=str,
+        help="Video to upload (local path)")
+
+    options = parser.parse_args(arguments)
+    video_id = upload.upload(options.video_path, options)
+    video_url = WATCH_VIDEO_URL.format(id=video_id)
+    lib.debug("Video URL: {0}".format(video_url))
+    sys.stdout.write(video_id + "\n")
 
 def run():
     sys.exit(lib.catch_exceptions(EXIT_CODES, main, sys.argv[1:]))

+ 146 - 0
youtube_upload/upload.py

@@ -0,0 +1,146 @@
+import os
+import sys
+import collections
+import webbrowser
+
+import googleapiclient.errors
+import oauth2client
+
+from . import lib
+from . import playlists
+from . import auth
+from . import upload_video
+from . import categories
+
+# http://code.google.com/p/python-progressbar (>= 2.3)
+try:
+    import progressbar
+except ImportError:
+    progressbar = None
+
+debug = lib.debug
+struct = collections.namedtuple
+
+class InvalidCategory(Exception): pass
+class AuthenticationError(Exception): pass
+class RequestError(Exception): pass
+
+def open_link(url):
+    """Opens a URL link in the client's browser."""
+    webbrowser.open(url)
+
+def get_progress_info():
+    """Return a function callback to update the progressbar."""
+    progressinfo = struct("ProgressInfo", ["callback", "finish"])
+
+    if progressbar:
+        bar = progressbar.ProgressBar(widgets=[
+            progressbar.Percentage(), ' ',
+            progressbar.Bar(), ' ',
+            progressbar.FileTransferSpeed(),
+        ])
+        def _callback(total_size, completed):
+            if not hasattr(bar, "next_update"):
+                if hasattr(bar, "maxval"):
+                    bar.maxval = total_size
+                else:
+                    bar.max_value = total_size
+                bar.start()
+            bar.update(completed)
+        def _finish():
+            if hasattr(bar, "next_update"):
+                return bar.finish()
+        return progressinfo(callback=_callback, finish=_finish)
+    else:
+        return progressinfo(callback=None, finish=lambda: True)
+
+def get_category_id(category):
+    """Return category ID from its name."""
+    if category:
+        if category in categories.IDS:
+            ncategory = categories.IDS[category]
+            debug("Using category: {0} (id={1})".format(category, ncategory))
+            return str(categories.IDS[category])
+        else:
+            msg = "{0} is not a valid category".format(category)
+            raise InvalidCategory(msg)
+
+def upload_youtube_video(youtube, options, video_path):
+    """Upload video."""
+    u = lib.to_utf8
+    title = u(options.title)
+    if hasattr(u('string'), 'decode'):   
+        description = u(options.description or "").decode("string-escape")
+    else:
+        description = options.description
+    if options.publish_at:    
+      debug("Your video will remain private until specified date.")
+      
+    tags = [u(s.strip()) for s in (options.tags or "").split(",")]
+    progress = get_progress_info()
+    category_id = get_category_id(options.category)
+    request_body = {
+        "snippet": {
+            "title": title,
+            "description": description,
+            "categoryId": category_id,
+            "tags": tags,
+            "defaultLanguage": options.default_language,
+            "defaultAudioLanguage": options.default_audio_language,
+
+        },
+        "status": {
+            "privacyStatus": ("private" if options.publish_at else options.privacy),
+            "publishAt": options.publish_at,
+
+        },
+        "recordingDetails": {
+            "location": lib.string_to_dict(options.location),
+            "recordingDate": options.recording_date,
+        },
+    }
+
+    debug("Start upload: {0}".format(video_path))
+    try:
+        video_id = upload_video.upload(youtube, video_path, 
+            request_body, progress_callback=progress.callback)
+    finally:
+        progress.finish()
+    return video_id
+
+def get_youtube_handler(options):
+    """Return the API Youtube object."""
+    home = os.path.expanduser("~")
+    default_client_secrets = lib.get_first_existing_filename(
+        [sys.prefix, os.path.join(sys.prefix, "local")],
+        "share/youtube_upload/client_secrets.json")  
+    default_credentials = os.path.join(home, ".youtube-upload-credentials.json")
+    client_secrets = options.client_secrets or default_client_secrets or \
+        os.path.join(home, ".client_secrets.json")
+    credentials = options.credentials_file or default_credentials
+    debug("Using client secrets: {0}".format(client_secrets))
+    debug("Using credentials file: {0}".format(credentials))
+    get_code_callback = (auth.browser.get_code 
+        if options.auth_browser else auth.console.get_code)
+    return auth.get_resource(client_secrets, credentials,
+        get_code_callback=get_code_callback)
+
+def upload(video_path, options):
+    """Run the main scripts from the parsed options/args."""
+    youtube = get_youtube_handler(options)
+
+    if youtube:
+        try:            
+            video_id = upload_youtube_video(youtube, options, video_path)
+            if options.open_link:
+                open_link(video_url)
+            if options.thumb:
+                youtube.thumbnails().set(videoId=video_id, media_body=options.thumb).execute()
+            if options.playlist:
+                playlists.add_video_to_playlist(youtube, video_id, 
+                    title=lib.to_utf8(options.playlist), privacy=options.privacy)
+        except googleapiclient.errors.HttpError as error:
+            raise RequestError("Server response: {0}".format(bytes.decode(error.content).strip()))
+        return video_id
+    else:
+        raise AuthenticationError("Cannot get youtube resource")