|
@@ -1,7 +1,7 @@
|
|
|
#!/usr/bin/python2
|
|
|
#
|
|
|
# Author: Arnau Sanchez <pyarnau@gmail.com>
|
|
|
-# Site: https://github.com/tokland/youtube-upload
|
|
|
+# Project site: https://github.com/tokland/youtube-upload
|
|
|
"""
|
|
|
Upload a video to Youtube from the command-line.
|
|
|
|
|
@@ -15,13 +15,13 @@ Upload a video to Youtube from the command-line.
|
|
|
|
|
|
import os
|
|
|
import sys
|
|
|
-import locale
|
|
|
import optparse
|
|
|
import collections
|
|
|
|
|
|
import youtube_upload.auth
|
|
|
import youtube_upload.upload_video
|
|
|
import youtube_upload.categories
|
|
|
+import youtube_upload.lib as lib
|
|
|
|
|
|
# http://code.google.com/p/python-progressbar (>= 2.3)
|
|
|
try:
|
|
@@ -32,8 +32,6 @@ except ImportError:
|
|
|
class InvalidCategory(Exception): pass
|
|
|
class OptionsMissing(Exception): pass
|
|
|
|
|
|
-VERSION = "0.8.0"
|
|
|
-
|
|
|
EXIT_CODES = {
|
|
|
OptionsMissing: 2,
|
|
|
InvalidCategory: 3,
|
|
@@ -41,44 +39,7 @@ EXIT_CODES = {
|
|
|
|
|
|
WATCH_VIDEO_URL = "https://www.youtube.com/watch?v={id}"
|
|
|
|
|
|
-def to_utf8(s):
|
|
|
- """Re-encode string from the default system encoding to UTF-8."""
|
|
|
- current = locale.getpreferredencoding()
|
|
|
- return s.decode(current).encode("UTF-8") if s and current != "UTF-8" else s
|
|
|
-
|
|
|
-def debug(obj, fd=sys.stderr):
|
|
|
- """Write obj to standard error."""
|
|
|
- string = str(obj.encode(get_encoding(fd), "backslashreplace")
|
|
|
- if isinstance(obj, unicode) else obj)
|
|
|
- fd.write(string + "\n")
|
|
|
-
|
|
|
-def catch_exceptions(exit_codes, fun, *args, **kwargs):
|
|
|
- """
|
|
|
- Catch exceptions on fun(*args, **kwargs) and return the exit code specified
|
|
|
- in the exit_codes dictionary. Return 0 if no exception is raised.
|
|
|
- """
|
|
|
- try:
|
|
|
- fun(*args, **kwargs)
|
|
|
- return 0
|
|
|
- except tuple(exit_codes.keys()) as exc:
|
|
|
- debug("[%s] %s" % (exc.__class__.__name__, exc))
|
|
|
- return exit_codes[exc.__class__]
|
|
|
-
|
|
|
-def get_encoding(fd):
|
|
|
- """Guess terminal encoding."""
|
|
|
- return fd.encoding or locale.getpreferredencoding()
|
|
|
-
|
|
|
-def compact(it):
|
|
|
- """Filter false (in the truth sense) elements in iterator."""
|
|
|
- return filter(bool, it)
|
|
|
-
|
|
|
-def tosize(seq, size):
|
|
|
- """Return list of fixed length from sequence."""
|
|
|
- return seq[:size] if len(seq) >= size else (seq + [None] * (size-len(seq)))
|
|
|
-
|
|
|
-def first(it):
|
|
|
- """Return first element in iterable."""
|
|
|
- return it.next()
|
|
|
+debug = lib.debug
|
|
|
|
|
|
def get_progress_info():
|
|
|
"""Return a function callback to update the progressbar."""
|
|
@@ -99,12 +60,7 @@ def get_progress_info():
|
|
|
bar = progressbar.ProgressBar(widgets=widgets)
|
|
|
return build(callback=_callback, finish=bar.finish)
|
|
|
else:
|
|
|
- return build(callback=lambda *args: True, finish=lambda: True)
|
|
|
-
|
|
|
-def string_to_dict(string):
|
|
|
- """Return dictionary from string "key1=value1, key2=value2"."""
|
|
|
- pairs = [s.strip() for s in (string or "").split(",")]
|
|
|
- return dict(pair.split("=") for pair in pairs)
|
|
|
+ return build(callback=None, finish=lambda: True)
|
|
|
|
|
|
def get_category_id(category):
|
|
|
"""Return category ID from its name."""
|
|
@@ -117,14 +73,13 @@ def get_category_id(category):
|
|
|
|
|
|
def upload_video(youtube, options, video_path, total_videos, index):
|
|
|
"""Upload video with index (for split videos)."""
|
|
|
- title = to_utf8(options.title)
|
|
|
- description = to_utf8(options.description or "").decode("string-escape")
|
|
|
+ title = lib.to_utf8(options.title)
|
|
|
+ description = lib.to_utf8(options.description or "").decode("string-escape")
|
|
|
ns = dict(title=title, n=index+1, total=total_videos)
|
|
|
complete_title = \
|
|
|
(options.title_template.format(**ns) if total_videos > 1 else title)
|
|
|
progress = get_progress_info()
|
|
|
category_id = get_category_id(options.category)
|
|
|
-
|
|
|
body = {
|
|
|
"snippet": {
|
|
|
"title": complete_title,
|
|
@@ -136,7 +91,7 @@ def upload_video(youtube, options, video_path, total_videos, index):
|
|
|
"privacyStatus": options.privacy
|
|
|
},
|
|
|
"recordingDetails": {
|
|
|
- "location": string_to_dict(options.location),
|
|
|
+ "location": lib.string_to_dict(options.location),
|
|
|
},
|
|
|
}
|
|
|
|
|
@@ -176,27 +131,25 @@ def main(arguments):
|
|
|
usage = """Usage: %prog [OPTIONS] VIDEO_PATH [VIDEO_PATH2 ...]
|
|
|
|
|
|
Upload videos to youtube."""
|
|
|
- parser = optparse.OptionParser(usage, version=VERSION)
|
|
|
+ parser = optparse.OptionParser(usage)
|
|
|
|
|
|
- # Required options
|
|
|
+ # Video metadata
|
|
|
parser.add_option('-t', '--title', dest='title', type="string",
|
|
|
help='Video(s) title')
|
|
|
parser.add_option('-c', '--category', dest='category', type="string",
|
|
|
help='Video(s) category')
|
|
|
-
|
|
|
- # Optional options
|
|
|
parser.add_option('-d', '--description', dest='description', type="string",
|
|
|
help='Video(s) description')
|
|
|
parser.add_option('', '--tags', dest='tags', type="string",
|
|
|
help='Video(s) tags (separated by commas: tag1,tag2,...)')
|
|
|
- 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}])')
|
|
|
parser.add_option('', '--privacy', dest='privacy', metavar="STRING",
|
|
|
default="public", help='Privacy status (public | unlisted | private)')
|
|
|
parser.add_option('', '--location', dest='location', type="string",
|
|
|
default=None, metavar="latitude=VAL, longitude=VAL, altitude=VAL",
|
|
|
help='Video(s) location"')
|
|
|
+ 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',
|
|
@@ -208,4 +161,4 @@ def main(arguments):
|
|
|
run_main(parser, options, args)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
- sys.exit(catch_exceptions(EXIT_CODES, main, sys.argv[1:]))
|
|
|
+ sys.exit(lib.catch_exceptions(EXIT_CODES, main, sys.argv[1:]))
|