From c822a10d0e2705d2ec75af707c13464a6b6820c6 Mon Sep 17 00:00:00 2001 From: LecygneNoir Date: Sat, 28 Jul 2018 12:50:53 +0200 Subject: [PATCH 1/6] Add utilities to manage thumbnail --- lib/utils.py | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/lib/utils.py b/lib/utils.py index bd7b9d8..f1958e1 100644 --- a/lib/utils.py +++ b/lib/utils.py @@ -98,6 +98,32 @@ def getLanguage(language, platform): return PEERTUBE_LANGUAGE[language.lower()] +def remove_empty_kwargs(**kwargs): + good_kwargs = {} + if kwargs is not None: + for key, value in kwargs.iteritems(): + if value: + good_kwargs[key] = value + return good_kwargs + +def searchThumbnail(options): + video_directory = dirname(options.get('--file')) + "/" + # First, check for thumbnail based on videoname + if options.get('--name'): + if isfile(video_directory + options.get('--name') + ".jpg"): + options['--thumbnail'] = video_directory + options.get('--name') + ".jpg" + elif isfile(video_directory + options.get('--name') + ".jpeg"): + options['--thumbnail'] = video_directory + options.get('--name') + ".jpeg" + # Then, if we still not have thumbnail, check for thumbnail based on videofile name + if not options.get('--thumbnail'): + video_file = splitext(basename(options.get('--file')))[0] + if isfile(video_directory + video_file + ".jpg"): + options['--thumbnail'] = video_directory + video_file + ".jpg" + elif isfile(video_directory + video_file + ".jpeg"): + options['--thumbnail'] = video_directory + video_file + ".jpeg" + return options + + # return the nfo as a RawConfigParser object def loadNFO(options): video_directory = dirname(options.get('--file')) + "/" @@ -117,7 +143,6 @@ def loadNFO(options): else: if options.get('--name'): nfo_file = video_directory + options.get('--name') + ".txt" - print nfo_file if isfile(nfo_file): try: logging.info("Using " + nfo_file + " as NFO, loading...") From b442f15b1785c5ed144ba355491724566aeef077 Mon Sep 17 00:00:00 2001 From: LecygneNoir Date: Sat, 28 Jul 2018 12:51:08 +0200 Subject: [PATCH 2/6] Add thumbnail support for peertube --- lib/pt_upload.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/lib/pt_upload.py b/lib/pt_upload.py index e30d1f4..2a782b1 100644 --- a/lib/pt_upload.py +++ b/lib/pt_upload.py @@ -47,12 +47,11 @@ def upload_video(oauth, secret, options): user_info = json.loads(oauth.get(url + "/api/v1/users/me").content) return str(user_info["id"]) - def get_videofile(path): + def get_file(path): mimetypes.init() return (basename(path), open(abspath(path), 'rb'), mimetypes.types_map[splitext(path)[1]]) - path = options.get('--file') url = secret.get('peertube', 'peertube_url') # We need to transform fields into tuple to deal with tags as @@ -65,7 +64,7 @@ def upload_video(oauth, secret, options): ("description", options.get('--description') or "default description"), ("nsfw", str(int(options.get('--nsfw')) or "0")), ("channelId", get_userinfo()), - ("videofile", get_videofile(path)) + ("videofile", get_file(options.get('--file'))) ] if options.get('--tags'): @@ -76,7 +75,7 @@ def upload_video(oauth, secret, options): continue # Tag more than 30 chars crashes Peertube, so exit and check tags if len(strtag) >= 30: - logging.warning("Sorry, Peertube does not support tag with more than 30 characters, please reduce your tag size") + logging.warning("Peertube: Sorry, Peertube does not support tag with more than 30 characters, please reduce your tag size") exit(1) # If Mastodon compatibility is enabled, clean tags from special characters if options.get('--mt'): @@ -105,6 +104,9 @@ def upload_video(oauth, secret, options): else: fields.append(("commentsEnabled", "1")) + if options.get('--thumbnail'): + fields.append(("thumbnailfile", get_file(options.get('--thumbnail')))) + multipart_data = MultipartEncoder(fields) headers = { @@ -120,13 +122,13 @@ def upload_video(oauth, secret, options): jresponse = jresponse['video'] uuid = jresponse['uuid'] idvideo = str(jresponse['id']) - template = ('Peertube : Video was successfully uploaded.\n' - 'Watch it at %s/videos/watch/%s.') + logging.info('Peertube : Video was successfully uploaded.') + template = 'Peertube: Watch it at %s/videos/watch/%s.' logging.info(template % (url, uuid)) if options.get('--publishAt'): utils.publishAt(str(options.get('--publishAt')), oauth, url, idvideo, secret) else: - logging.error(('Peertube : The upload failed with an unexpected response: ' + logging.error(('Peertube: The upload failed with an unexpected response: ' '%s') % response) exit(1) @@ -136,16 +138,16 @@ def run(options): try: secret.read(PEERTUBE_SECRETS_FILE) except Exception as e: - logging.error("Error loading " + str(PEERTUBE_SECRETS_FILE) + ": " + str(e)) + logging.error("Peertube: Error loading " + str(PEERTUBE_SECRETS_FILE) + ": " + str(e)) exit(1) insecure_transport = secret.get('peertube', 'OAUTHLIB_INSECURE_TRANSPORT') os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = insecure_transport oauth = get_authenticated_service(secret) try: - logging.info('Peertube : Uploading file...') + logging.info('Peertube: Uploading video...') upload_video(oauth, secret, options) except Exception as e: if hasattr(e, 'message'): - logging.error("Error: " + str(e.message)) + logging.error("Peertube: Error: " + str(e.message)) else: - logging.error("Error: " + str(e)) + logging.error("Peertube: Error: " + str(e)) From 48030691783458aaafa65f28291ca60f54ccc74e Mon Sep 17 00:00:00 2001 From: LecygneNoir Date: Sat, 28 Jul 2018 12:51:21 +0200 Subject: [PATCH 3/6] Add thumbnail support for youtube --- lib/yt_upload.py | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/lib/yt_upload.py b/lib/yt_upload.py index 28e9c6f..df58e04 100644 --- a/lib/yt_upload.py +++ b/lib/yt_upload.py @@ -20,6 +20,7 @@ from googleapiclient.errors import HttpError from googleapiclient.http import MediaFileUpload from google_auth_oauthlib.flow import InstalledAppFlow + import utils logging.basicConfig(format='%(asctime)s %(message)s', level=logging.INFO) @@ -126,25 +127,44 @@ def initialize_upload(youtube, options): body=body, media_body=MediaFileUpload(path, chunksize=-1, resumable=True) ) - resumable_upload(insert_request) + video_id = resumable_upload(insert_request, 'video', 'insert') + + # If we get a video_id, upload is successful and we are able to set thumbnail + if video_id and options.get('--thumbnail'): + set_thumbnail(youtube, options.get('--thumbnail'), videoId=video_id) + + +def set_thumbnail(youtube, media_file, **kwargs): + kwargs = utils.remove_empty_kwargs(**kwargs) # See full sample for function + request = youtube.thumbnails().set( + media_body=MediaFileUpload(media_file, chunksize=-1, + resumable=True), + **kwargs + ) + + # See full sample for function + return resumable_upload(request, 'thumbnail', 'set') # This method implements an exponential backoff strategy to resume a # failed upload. -def resumable_upload(request): +def resumable_upload(request, resource, method): response = None error = None retry = 0 while response is None: try: - logging.info('Youtube : Uploading file...') + template = 'Youtube: Uploading %s...' + logging.info(template % resource) status, response = request.next_chunk() if response is not None: - if 'id' in response: - template = ('Youtube : Video was successfully ' - 'uploaded.\n' - 'Watch it at https://youtu.be/%s (post-encoding could take some time)') + if method == 'insert' and 'id' in response: + logging.info('Youtube : Video was successfully uploaded.') + template = 'Youtube: Watch it at https://youtu.be/%s (post-encoding could take some time)' logging.info(template % response['id']) + return response['id'] + elif method != 'insert' or "id" not in response: + logging.info('Youtube: Thumbnail was successfully set.') else: template = ('Youtube : The upload failed with an ' 'unexpected response: %s') From 5896522df5d56964edae0b4e4f57f292fed9f264 Mon Sep 17 00:00:00 2001 From: LecygneNoir Date: Sat, 28 Jul 2018 12:51:44 +0200 Subject: [PATCH 4/6] Update help and base script to manage thumbnail --- nfo_example.txt | 1 + prismedia_upload.py | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/nfo_example.txt b/nfo_example.txt index 065601d..dc538df 100644 --- a/nfo_example.txt +++ b/nfo_example.txt @@ -14,6 +14,7 @@ category = Films cca = True privacy = private disable-comments = True +thumbnail = /path/to/your/thumbnail.jpg # Set the absolute path to your thumbnail nsfw = True platform = youtube, peertube language = French diff --git a/prismedia_upload.py b/prismedia_upload.py index 7261cbf..508e5ef 100755 --- a/prismedia_upload.py +++ b/prismedia_upload.py @@ -34,6 +34,9 @@ Options: DATE should be on the form YYYY-MM-DDThh:mm:ss eg: 2018-03-12T19:00:00 DATE should be in the future For Peertube, requires the "atd" and "curl utilities installed on the system + --thumbnail=STRING Path to a file to use as a thumbnail for the video. + Supported types are jpg and jpeg. + By default, prismedia search for an image based on video name followed by .jpg or .jpeg -h --help Show this help. --version Show version. @@ -151,6 +154,12 @@ def validatePublish(publish): return False return True +def validateThumbnail(thumbnail): + supported_types = ['image/jpg', 'image/jpeg'] + if magic.from_file(thumbnail, mime=True) in supported_types: + return thumbnail + else: + return False if __name__ == '__main__': @@ -199,12 +208,18 @@ if __name__ == '__main__': Optional('--cca'): bool, Optional('--disable-comments'): bool, Optional('--nsfw'): bool, + Optional('--thumbnail'): Or(None, And( + str, validateThumbnail, error='thumbnail is not supported, please use jpg/jpeg'), + ), '--help': bool, '--version': bool }) options = utils.parseNFO(options) + if not options.get('--thumbnail'): + options = utils.searchThumbnail(options) + try: options = schema.validate(options) except SchemaError as e: From b784a5b5155f2587d5590af8eae1de9f7f6449d3 Mon Sep 17 00:00:00 2001 From: LecygneNoir Date: Sat, 28 Jul 2018 12:52:01 +0200 Subject: [PATCH 5/6] Update README with new features and help for thumbnail --- README.md | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index fd7ec79..2197a95 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,12 @@ Specify description and tags: ./prismedia_upload.py --file="yourvideo.mp4" -d "My supa description" -t "tag1,tag2,foo" ``` +Provide a thumbnail: + +``` +./prismedia_upload.py --file="yourvideo.mp4" -d "Video with thumbnail" --thumbnail="/path/to/your/thumbnail.jpg" +``` + Use a NFO file to specify your video options: @@ -111,7 +117,10 @@ Options: --publishAt=DATE Publish the video at the given DATE using local server timezone. DATE should be on the form YYYY-MM-DDThh:mm:ss eg: 2018-03-12T19:00:00 DATE should be in the future - For Peertube, requires the "atd", "curl" and "jq" utilities installed on the system + For Peertube, requires the "atd" and "curl utilities installed on the system + --thumbnail=STRING Path to a file to use as a thumbnail for the video. + Supported types are jpg and jpeg. + By default, prismedia search for an image based on video name followed by .jpg or .jpeg -h --help Show this help. --version Show version. @@ -145,8 +154,8 @@ Languages: - [x] enabling/disabling comment (Peertube only as Youtube API does not support it) - [x] nsfw (Peertube only as Youtube API does not support it) - [x] set default language - - [ ] thumbnail/preview (YT workflow: upload video, upload thumbnail, add thumbnail to video) - - [ ] multiple lines description (see [issue 4](https://git.lecygnenoir.info/LecygneNoir/prismedia/issues/4)) + - [x] thumbnail/preview + - [x] multiple lines description (see [issue 4](https://git.lecygnenoir.info/LecygneNoir/prismedia/issues/4)) - [ ] add videos to playlist (YT & PT workflow: upload video, find playlist id, add video to playlist) - [x] Use a config file (NFO) file to retrieve videos arguments - [x] Allow to choose peertube or youtube upload (to resume failed upload for example) From 6dd929a7c846d64d3490c933002855b891e85188 Mon Sep 17 00:00:00 2001 From: LecygneNoir Date: Wed, 1 Aug 2018 21:25:48 +0200 Subject: [PATCH 6/6] Fix old variable path inside peertube upload --- lib/pt_upload.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pt_upload.py b/lib/pt_upload.py index 2a782b1..650aa78 100644 --- a/lib/pt_upload.py +++ b/lib/pt_upload.py @@ -59,7 +59,7 @@ def upload_video(oauth, secret, options): # https://github.com/requests/toolbelt/issues/190 and # https://github.com/requests/toolbelt/issues/205 fields = [ - ("name", options.get('--name') or splitext(basename(path))[0]), + ("name", options.get('--name') or splitext(basename(options.get('--file')))[0]), ("licence", "1"), ("description", options.get('--description') or "default description"), ("nsfw", str(int(options.get('--nsfw')) or "0")),