From 5c991581e83697360f096e99f5fdfde4ff83e150 Mon Sep 17 00:00:00 2001 From: LecygneNoir Date: Wed, 11 Nov 2020 10:47:29 +0100 Subject: [PATCH 01/15] bump to v0.10.2 for poetry --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index ab8c307..aa015ba 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "prismedia" -version = "0.10.1" +version = "0.10.2" description = "scripting your way to upload videos on peertube and youtube" authors = [ "LecygneNoir ", From 447310a17ef1f90b52677a889d330c8358e2bcb1 Mon Sep 17 00:00:00 2001 From: LecygneNoir Date: Mon, 30 Nov 2020 12:26:44 +0100 Subject: [PATCH 02/15] Add options and bunch of functions to maange the originalDate fields in prismedia, cf #50 --- prismedia/upload.py | 44 ++++++++++++++++++++++++++++++++++++-------- prismedia/utils.py | 10 +++++++--- 2 files changed, 43 insertions(+), 11 deletions(-) diff --git a/prismedia/upload.py b/prismedia/upload.py index 14b4133..77ad873 100755 --- a/prismedia/upload.py +++ b/prismedia/upload.py @@ -34,6 +34,11 @@ Options: DATE should be in the future --peertubeAt=DATE --youtubeAt=DATE Override publishAt for the corresponding platform. Allow to create preview on specific platform + --originalDate=DATE Configure the video as initially recorded at DATE + DATE should be on the form YYYY-MM-DDThh:mm:ss eg: 2018-03-12T19:00:00 + DATE should be in the past + Default use the last modification date of the file. + --no-originalDate Do not set the initial record field when uploading --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 @@ -190,11 +195,11 @@ def validateLanguage(language): return False -def validatePublish(publish): +def validatePublishDate(publishDate): # Check date format and if date is future try: now = datetime.datetime.now() - publishAt = datetime.datetime.strptime(publish, '%Y-%m-%dT%H:%M:%S') + publishAt = datetime.datetime.strptime(publishDate, '%Y-%m-%dT%H:%M:%S') if now >= publishAt: return False except ValueError: @@ -202,6 +207,18 @@ def validatePublish(publish): return True +def validateOriginalDate(originalDate): + # Check date format and if date is future + try: + now = datetime.datetime.now() + originalDate = datetime.datetime.strptime(originalDate, '%Y-%m-%dT%H:%M:%S') + if now <= originalDate: + return False + except ValueError: + return False + return True + + def validateThumbnail(thumbnail): supported_types = ['image/jpg', 'image/jpeg'] if os.path.exists(thumbnail) and \ @@ -336,19 +353,25 @@ def main(): Optional('--platform'): Or(None, And(str, validatePlatform, error="Sorry, upload platform not supported")), Optional('--publishAt'): Or(None, And( str, - validatePublish, - error="DATE should be the form YYYY-MM-DDThh:mm:ss and has to be in the future") + validatePublishDate, + error="Publish Date should be the form YYYY-MM-DDThh:mm:ss and has to be in the future") ), Optional('--peertubeAt'): Or(None, And( str, - validatePublish, - error="DATE should be the form YYYY-MM-DDThh:mm:ss and has to be in the future") + validatePublishDate, + error="Publish Date should be the form YYYY-MM-DDThh:mm:ss and has to be in the future") ), Optional('--youtubeAt'): Or(None, And( str, - validatePublish, - error="DATE should be the form YYYY-MM-DDThh:mm:ss and has to be in the future") + validatePublishDate, + error="Publish Date should be the form YYYY-MM-DDThh:mm:ss and has to be in the future") ), + Optional('--originalDate'): Or(None, And( + str, + validateOriginalDate, + error="Original date should be the form YYYY-MM-DDThh:mm:ss and has to be in the past") + ), + Optional('--no-originalDate'): bool, Optional('--cca'): bool, Optional('--disable-comments'): bool, Optional('--nsfw'): bool, @@ -387,6 +410,11 @@ def main(): if not options.get('--thumbnail'): options = utils.searchThumbnail(options) + # If after loading NFO we still has no original date and --no-originalDate is not enabled, + # then we need to search from the file + if not options.get('--originalDate') and not options.get('--no-originalDate'): + options['--originalDate'] = utils.searchOriginalDate(options) + try: options = schema.validate(options) except SchemaError as e: diff --git a/prismedia/utils.py b/prismedia/utils.py index d7f85ad..c16a885 100644 --- a/prismedia/utils.py +++ b/prismedia/utils.py @@ -2,12 +2,11 @@ # coding: utf-8 from configparser import RawConfigParser, NoOptionError, NoSectionError -from os.path import dirname, splitext, basename, isfile +from os.path import dirname, splitext, basename, isfile, getmtime import re -from os import devnull -from subprocess import check_call, CalledProcessError, STDOUT import unidecode import logging +import datetime logger = logging.getLogger('Prismedia') @@ -135,6 +134,11 @@ def searchThumbnail(options): return options +def searchOriginalDate(options): + fileModificationDate = getmtime(options.get('--file')) + return datetime.datetime.fromtimestamp(fileModificationDate).isoformat() + + # return the nfo as a RawConfigParser object def loadNFO(filename): try: From 60bf26418d3565cce711530cbb09bd29d2a1f55a Mon Sep 17 00:00:00 2001 From: LecygneNoir Date: Mon, 30 Nov 2020 12:27:09 +0100 Subject: [PATCH 03/15] Add the originalDate options to Youtube videos --- prismedia/yt_upload.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/prismedia/yt_upload.py b/prismedia/yt_upload.py index 3cfd900..c08619a 100644 --- a/prismedia/yt_upload.py +++ b/prismedia/yt_upload.py @@ -119,6 +119,9 @@ def initialize_upload(youtube, options): "status": { "privacyStatus": str(options.get('--privacy') or "private"), "license": str(license or "youtube"), + }, + "recordingDetails": { + } } @@ -128,6 +131,7 @@ def initialize_upload(youtube, options): elif options.get('--publishAt'): publishAt = options.get('--publishAt') + # Check if publishAt variable exists in local variables if 'publishAt' in locals(): # Youtube needs microsecond and the local timezone from ISO 8601 publishAt = publishAt + ".000001" @@ -137,6 +141,16 @@ def initialize_upload(youtube, options): publishAt = tz.localize(publishAt).isoformat() body['status']['publishAt'] = str(publishAt) + # Set originalDate except if the user force no originalDate + if not options.get('--no-originalDate'): + # Youtube needs microsecond and the local timezone from ISO 8601 + originalDate = options.get('--originalDate') + ".000001" + originalDate = datetime.datetime.strptime(originalDate, '%Y-%m-%dT%H:%M:%S.%f') + tz = get_localzone() + tz = pytz.timezone(str(tz)) + originalDate = tz.localize(originalDate).isoformat() + body['recordingDetails']['recordingDate'] = str(originalDate) + if options.get('--playlist'): playlist_id = get_playlist_by_name(youtube, options.get('--playlist')) if not playlist_id and options.get('--playlistCreate'): From dc98f2e155404ace0fa083e6098f8142f8b8ddd7 Mon Sep 17 00:00:00 2001 From: LecygneNoir Date: Mon, 30 Nov 2020 13:22:24 +0100 Subject: [PATCH 04/15] Add functions to manage Original Date of record for peertube --- prismedia/pt_upload.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/prismedia/pt_upload.py b/prismedia/pt_upload.py index 1f35e62..8267ecd 100644 --- a/prismedia/pt_upload.py +++ b/prismedia/pt_upload.py @@ -265,6 +265,14 @@ def upload_video(oauth, secret, options): else: fields.append(("privacy", str(PEERTUBE_PRIVACY[privacy or "private"]))) + # Set originalDate except if the user force no originalDate + if not options.get('--no-originalDate'): + originalDate = datetime.datetime.strptime(options.get('--originalDate'), '%Y-%m-%dT%H:%M:%S') + tz = get_localzone() + tz = pytz.timezone(str(tz)) + originalDate = tz.localize(originalDate).isoformat() + fields.append(("originallyPublishedAt", originalDate)) + if options.get('--thumbnail'): fields.append(("thumbnailfile", get_file(options.get('--thumbnail')))) fields.append(("previewfile", get_file(options.get('--thumbnail')))) From 4b7c01a7077a25a6d076e2dbb5cca3f9d2bb7216 Mon Sep 17 00:00:00 2001 From: LecygneNoir Date: Mon, 30 Nov 2020 13:23:31 +0100 Subject: [PATCH 05/15] Add help for option originalDate and changelog about the feature --- CHANGELOG.md | 5 +++++ README.md | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 81f0d6d..eb4d58f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## v0.11.0 + +## Features + - Add the configuration of Original date of Record for Youtube and Peertube (see #50) + ## v0.10.3 ### Fix diff --git a/README.md b/README.md index 02269d0..41e7585 100644 --- a/README.md +++ b/README.md @@ -145,6 +145,11 @@ Options: DATE should be in the future --peertubeAt=DATE --youtubeAt=DATE Override publishAt for the corresponding platform. Allow to create preview on specific platform + --originalDate=DATE Configure the video as initially recorded at DATE + DATE should be on the form YYYY-MM-DDThh:mm:ss eg: 2018-03-12T19:00:00 + DATE should be in the past + Default use the last modification date of the file. + --no-originalDate Do not set the initial record field when uploading --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 From 8dc3a86aabd62f164be9bc24650b594f25dbe44e Mon Sep 17 00:00:00 2001 From: LecygneNoir Date: Tue, 1 Dec 2020 09:24:36 +0100 Subject: [PATCH 06/15] Stripe the README from the full --help output to focus on some main features --- README.md | 99 ++++-------------------------------------- prismedia/yt_upload.py | 22 +++++----- 2 files changed, 18 insertions(+), 103 deletions(-) diff --git a/README.md b/README.md index 41e7585..4139c9f 100644 --- a/README.md +++ b/README.md @@ -92,24 +92,27 @@ If you plan a larger usage, please consider creating your own youtube_secret fil Support only mp4 for cross compatibility between Youtube and Peertube. **Note that all options may be specified in a NFO file!** (see [Enhanced NFO](#enhanced-use-of-nfo)) -Upload a video: +Here are some demonstration of main usage you would like! +Upload a video: ``` prismedia --file="yourvideo.mp4" ``` Specify description and tags: - ``` prismedia --file="yourvideo.mp4" -d "My supa description" -t "tag1,tag2,foo" ``` Provide a thumbnail: - ``` prismedia --file="yourvideo.mp4" -d "Video with thumbnail" --thumbnail="/path/to/your/thumbnail.jpg" ``` +Publish on Peertube only, while using a channel and a playlist, creating them if they does not exist.: +``` +prismedia --file="yourvideo.mp4" --platform=peertube --channel="Cooking recipes" --playlist="Cake recipes" --channelCreate --playlistCreate +``` Use a NFO file to specify your video options: (See [Enhanced NFO](#enhanced-use-of-nfo) for more precise example) @@ -118,95 +121,9 @@ prismedia --file="yourvideo.mp4" --nfo /path/to/your/nfo.txt ``` -Use --help to get all available options: - +Take a look at all available options with `--help`! ``` -Options: - -f, --file=STRING Path to the video file to upload in mp4. This is the only mandatory option. - --name=NAME Name of the video to upload. (default to video filename) - -d, --description=STRING Description of the video. (default: default description) - -t, --tags=STRING Tags for the video. comma separated. - WARN: tags with punctuation (!, ', ", ?, ...) - are not supported by Mastodon to be published from Peertube - -c, --category=STRING Category for the videos, see below. (default: Films) - --cca License should be CreativeCommon Attribution (affects Youtube upload only) - -p, --privacy=STRING Choose between public, unlisted or private. (default: private) - --disable-comments Disable comments (Peertube only as YT API does not support) (default: comments are enabled) - --nsfw Set the video as No Safe For Work (Peertube only as YT API does not support) (default: video is safe) - --nfo=STRING Configure a specific nfo file to set options for the video. - By default Prismedia search a .txt based on the video name and will - decode the file as UTF-8 (so make sure your nfo file is UTF-8 encoded) - See nfo_example.txt for more details - --platform=STRING List of platform(s) to upload to, comma separated. - Supported platforms are youtube and peertube (default is both) - --language=STRING Specify the default language for video. See below for supported language. (default is English) - --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 - --peertubeAt=DATE - --youtubeAt=DATE Override publishAt for the corresponding platform. Allow to create preview on specific platform - --originalDate=DATE Configure the video as initially recorded at DATE - DATE should be on the form YYYY-MM-DDThh:mm:ss eg: 2018-03-12T19:00:00 - DATE should be in the past - Default use the last modification date of the file. - --no-originalDate Do not set the initial record field when uploading - --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 - --channel=STRING Set the channel to use for the video (Peertube only) - If the channel is not found, spawn an error except if --channelCreate is set. - --channelCreate Create the channel if not exists. (Peertube only, default do not create) - Only relevant if --channel is set. - --playlist=STRING Set the playlist to use for the video. - If the playlist is not found, spawn an error except if --playlistCreate is set. - --playlistCreate Create the playlist if not exists. (default do not create) - Only relevant if --playlist is set. - -h --help Show this help. - --version Show version. - -Logging options - -q --quiet Suppress any log except Critical (alias for --log=critical). - --log=STRING Log level, between debug, info, warning, error, critical. Ignored if --quiet is set (default to info) - -u --url-only Display generated URL after upload directly on stdout, implies --quiet - --batch Display generated URL after upload with platform information for easier parsing. Implies --quiet - Be careful --batch and --url-only are mutually exclusives. - --debug (Deprecated) Alias for --log=debug. Ignored if --log is set - -Strict options: - Strict options allow you to force some option to be present when uploading a video. It's useful to be sure you do not - forget something when uploading a video, for example if you use multiples NFO. You may force the presence of description, - tags, thumbnail, ... - All strict option are optionals and are provided only to avoid errors when uploading :-) - All strict options can be specified in NFO directly, the only strict option mandatory on cli is --withNFO - All strict options are off by default - - --withNFO Prevent the upload without a NFO, either specified via cli or found in the directory - --withThumbnail Prevent the upload without a thumbnail - --withName Prevent the upload if no name are found - --withDescription Prevent the upload without description - --withTags Prevent the upload without tags - --withPlaylist Prevent the upload if no playlist - --withPublishAt Prevent the upload if no schedule - --withPlatform Prevent the upload if at least one platform is not specified - --withCategory Prevent the upload if no category - --withLanguage Prevent upload if no language - --withChannel Prevent upload if no channel - -Categories: - Category is the type of video you upload. Default is films. - Here are available categories from Peertube and Youtube: - music, films, vehicles, - sports, travels, gaming, people, - comedy, entertainment, news, - how to, education, activism, science & technology, - science, technology, animals - -Languages: - Language of the video (audio track), choose one. Default is English - Here are available languages from Peertube and Youtube: - Arabic, English, French, German, Hindi, Italian, - Japanese, Korean, Mandarin, Portuguese, Punjabi, Russian, Spanish - +prismedia --help ``` ## Enhanced use of NFO diff --git a/prismedia/yt_upload.py b/prismedia/yt_upload.py index c08619a..afccf1a 100644 --- a/prismedia/yt_upload.py +++ b/prismedia/yt_upload.py @@ -89,6 +89,14 @@ def check_authenticated_scopes(): os.remove(CREDENTIALS_PATH) +def convert_youtube_date(date): + # Youtube needs microsecond and the local timezone from ISO 8601 + date = date + ".000001" + date = datetime.datetime.strptime(date, '%Y-%m-%dT%H:%M:%S.%f') + tz = get_localzone() + tz = pytz.timezone(str(tz)) + return tz.localize(date).isoformat() + def initialize_upload(youtube, options): path = options.get('--file') tags = None @@ -133,22 +141,12 @@ def initialize_upload(youtube, options): # Check if publishAt variable exists in local variables if 'publishAt' in locals(): - # Youtube needs microsecond and the local timezone from ISO 8601 - publishAt = publishAt + ".000001" - publishAt = datetime.datetime.strptime(publishAt, '%Y-%m-%dT%H:%M:%S.%f') - tz = get_localzone() - tz = pytz.timezone(str(tz)) - publishAt = tz.localize(publishAt).isoformat() + publishAt = convert_youtube_date(publishAt) body['status']['publishAt'] = str(publishAt) # Set originalDate except if the user force no originalDate if not options.get('--no-originalDate'): - # Youtube needs microsecond and the local timezone from ISO 8601 - originalDate = options.get('--originalDate') + ".000001" - originalDate = datetime.datetime.strptime(originalDate, '%Y-%m-%dT%H:%M:%S.%f') - tz = get_localzone() - tz = pytz.timezone(str(tz)) - originalDate = tz.localize(originalDate).isoformat() + originalDate = convert_youtube_date(options.get('--originalDate')) body['recordingDetails']['recordingDate'] = str(originalDate) if options.get('--playlist'): From 4a9fda5e771f3ec96fc49887de5a31d24deb11d1 Mon Sep 17 00:00:00 2001 From: LecygneNoir Date: Wed, 2 Dec 2020 11:28:07 +0100 Subject: [PATCH 07/15] Add one function to deal with date to avoid duplicate code --- prismedia/pt_upload.py | 17 +++++++++-------- prismedia/yt_upload.py | 1 + 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/prismedia/pt_upload.py b/prismedia/pt_upload.py index 8267ecd..23c1453 100644 --- a/prismedia/pt_upload.py +++ b/prismedia/pt_upload.py @@ -63,6 +63,13 @@ def get_channel_by_name(user_info, options): return channel['id'] +def convert_peertube_date(date): + date = datetime.datetime.strptime(date, '%Y-%m-%dT%H:%M:%S') + tz = get_localzone() + tz = pytz.timezone(str(tz)) + return tz.localize(date).isoformat() + + def create_channel(oauth, url, options): template = ('Peertube: Channel %s does not exist, creating it.') logger.info(template % (str(options.get('--channel')))) @@ -255,10 +262,7 @@ def upload_video(oauth, secret, options): publishAt = options.get('--publishAt') if 'publishAt' in locals(): - publishAt = datetime.datetime.strptime(publishAt, '%Y-%m-%dT%H:%M:%S') - tz = get_localzone() - tz = pytz.timezone(str(tz)) - publishAt = tz.localize(publishAt).isoformat() + publishAt = convert_peertube_date(publishAt) fields.append(("scheduleUpdate[updateAt]", publishAt)) fields.append(("scheduleUpdate[privacy]", str(PEERTUBE_PRIVACY["public"]))) fields.append(("privacy", str(PEERTUBE_PRIVACY["private"]))) @@ -267,10 +271,7 @@ def upload_video(oauth, secret, options): # Set originalDate except if the user force no originalDate if not options.get('--no-originalDate'): - originalDate = datetime.datetime.strptime(options.get('--originalDate'), '%Y-%m-%dT%H:%M:%S') - tz = get_localzone() - tz = pytz.timezone(str(tz)) - originalDate = tz.localize(originalDate).isoformat() + originalDate = convert_peertube_date(options.get('--originalDate')) fields.append(("originallyPublishedAt", originalDate)) if options.get('--thumbnail'): diff --git a/prismedia/yt_upload.py b/prismedia/yt_upload.py index afccf1a..1ffc2a5 100644 --- a/prismedia/yt_upload.py +++ b/prismedia/yt_upload.py @@ -97,6 +97,7 @@ def convert_youtube_date(date): tz = pytz.timezone(str(tz)) return tz.localize(date).isoformat() + def initialize_upload(youtube, options): path = options.get('--file') tags = None From 736582b495db2ec90ee4b9966fa43dd41eef88af Mon Sep 17 00:00:00 2001 From: LecygneNoir Date: Fri, 4 Dec 2020 09:52:46 +0100 Subject: [PATCH 08/15] Change the originalDate behaviour to not default, and add option to auto manage the original date if needed --- prismedia/pt_upload.py | 2 +- prismedia/upload.py | 22 +++++++++++++--------- prismedia/yt_upload.py | 4 +++- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/prismedia/pt_upload.py b/prismedia/pt_upload.py index 23c1453..7c779ad 100644 --- a/prismedia/pt_upload.py +++ b/prismedia/pt_upload.py @@ -270,7 +270,7 @@ def upload_video(oauth, secret, options): fields.append(("privacy", str(PEERTUBE_PRIVACY[privacy or "private"]))) # Set originalDate except if the user force no originalDate - if not options.get('--no-originalDate'): + if options.get('--originalDate'): originalDate = convert_peertube_date(options.get('--originalDate')) fields.append(("originallyPublishedAt", originalDate)) diff --git a/prismedia/upload.py b/prismedia/upload.py index 77ad873..ea907ee 100755 --- a/prismedia/upload.py +++ b/prismedia/upload.py @@ -37,8 +37,7 @@ Options: --originalDate=DATE Configure the video as initially recorded at DATE DATE should be on the form YYYY-MM-DDThh:mm:ss eg: 2018-03-12T19:00:00 DATE should be in the past - Default use the last modification date of the file. - --no-originalDate Do not set the initial record field when uploading + --auto-originalDate Automatically use the file modification time as original date --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 @@ -76,6 +75,7 @@ Strict options: --withTags Prevent the upload without tags --withPlaylist Prevent the upload if no playlist --withPublishAt Prevent the upload if no schedule + --withOriginalDate Prevent the upload if no original date configured --withPlatform Prevent the upload if at least one platform is not specified --withCategory Prevent the upload if no category --withLanguage Prevent upload if no language @@ -208,7 +208,7 @@ def validatePublishDate(publishDate): def validateOriginalDate(originalDate): - # Check date format and if date is future + # Check date format and if date is past try: now = datetime.datetime.now() originalDate = datetime.datetime.strptime(originalDate, '%Y-%m-%dT%H:%M:%S') @@ -234,6 +234,7 @@ def validateLogLevel(loglevel): return False return True + def _optionnalOrStrict(key, scope, error): option = key.replace('-', '') option = option[0].upper() + option[1:] @@ -297,6 +298,7 @@ def main(): Optional('--withTags', default=False): bool, Optional('--withPlaylist', default=False): bool, Optional('--withPublishAt', default=False): bool, + Optional('--withOriginalDate', default=False): bool, Optional('--withPlatform', default=False): bool, Optional('--withCategory', default=False): bool, Optional('--withLanguage', default=False): bool, @@ -315,6 +317,7 @@ def main(): Hook('--language', handler=_optionnalOrStrict): object, Hook('--platform', handler=_optionnalOrStrict): object, Hook('--publishAt', handler=_optionnalOrStrict): object, + Hook('--originalDate', handler=_optionnalOrStrict): object, Hook('--thumbnail', handler=_optionnalOrStrict): object, Hook('--channel', handler=_optionnalOrStrict): object, Hook('--playlist', handler=_optionnalOrStrict): object, @@ -371,7 +374,7 @@ def main(): validateOriginalDate, error="Original date should be the form YYYY-MM-DDThh:mm:ss and has to be in the past") ), - Optional('--no-originalDate'): bool, + Optional('--auto-originalDate'): bool, Optional('--cca'): bool, Optional('--disable-comments'): bool, Optional('--nsfw'): bool, @@ -400,6 +403,12 @@ def main(): options = utils.parseNFO(options) + # If after loading NFO we still has no original date and --auto-originalDate is enabled, + # then we need to search from the file + # We need to do that before the strict validation in case --withOriginalDate is enabled + if not options.get('--originalDate') and options.get('--auto-originalDate'): + options['--originalDate'] = utils.searchOriginalDate(options) + # Once NFO are loaded, we need to revalidate strict options in case some were in NFO try: options = earlyoptionSchema.validate(options) @@ -410,11 +419,6 @@ def main(): if not options.get('--thumbnail'): options = utils.searchThumbnail(options) - # If after loading NFO we still has no original date and --no-originalDate is not enabled, - # then we need to search from the file - if not options.get('--originalDate') and not options.get('--no-originalDate'): - options['--originalDate'] = utils.searchOriginalDate(options) - try: options = schema.validate(options) except SchemaError as e: diff --git a/prismedia/yt_upload.py b/prismedia/yt_upload.py index 1ffc2a5..22db640 100644 --- a/prismedia/yt_upload.py +++ b/prismedia/yt_upload.py @@ -116,6 +116,8 @@ def initialize_upload(youtube, options): if options.get('--cca'): license = "creativeCommon" + # We set recordingDetails empty because it's easier to add options if it already exists + # and if empty, it does not cause problem during upload body = { "snippet": { "title": options.get('--name') or splitext(basename(path))[0], @@ -146,7 +148,7 @@ def initialize_upload(youtube, options): body['status']['publishAt'] = str(publishAt) # Set originalDate except if the user force no originalDate - if not options.get('--no-originalDate'): + if options.get('--originalDate'): originalDate = convert_youtube_date(options.get('--originalDate')) body['recordingDetails']['recordingDate'] = str(originalDate) From 42ee7d761b41ae7bc617f5c52e6dc83c79300749 Mon Sep 17 00:00:00 2001 From: LecygneNoir Date: Tue, 15 Dec 2020 11:25:32 +0100 Subject: [PATCH 09/15] Need to strip the getmtime timestamp as some OSes return timestamp with microsecond (XXXX.YYYY) --- prismedia/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/prismedia/utils.py b/prismedia/utils.py index c16a885..a971ac4 100644 --- a/prismedia/utils.py +++ b/prismedia/utils.py @@ -135,8 +135,8 @@ def searchThumbnail(options): def searchOriginalDate(options): - fileModificationDate = getmtime(options.get('--file')) - return datetime.datetime.fromtimestamp(fileModificationDate).isoformat() + fileModificationDate = str(getmtime(options.get('--file'))).split('.') + return datetime.datetime.fromtimestamp(int(fileModificationDate[0])).isoformat() # return the nfo as a RawConfigParser object From 230ac545c436bc77b7ee9734e1981155b75afb94 Mon Sep 17 00:00:00 2001 From: LecygneNoir Date: Fri, 25 Dec 2020 09:59:23 +0100 Subject: [PATCH 10/15] Patch incorrect loading of NFO keys, breaking the match when the key contains -, and add example for auto-originalDate in NFO --- prismedia/samples/nfo.txt | 5 +++-- prismedia/utils.py | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/prismedia/samples/nfo.txt b/prismedia/samples/nfo.txt index 7b18585..af75d07 100644 --- a/prismedia/samples/nfo.txt +++ b/prismedia/samples/nfo.txt @@ -4,6 +4,7 @@ # Some generic options for your videos cca = True privacy = private -disable-comments = True +disable-comments = False channel = DefaultChannel -channelCreate = True \ No newline at end of file +channelCreate = True +auto-originalDate = True \ No newline at end of file diff --git a/prismedia/utils.py b/prismedia/utils.py index a971ac4..fcea677 100644 --- a/prismedia/utils.py +++ b/prismedia/utils.py @@ -200,7 +200,7 @@ def parseNFO(options): if nfo: # We need to check all options and replace it with the nfo value if not defined (None or False) for key, value in options.items(): - key = key.replace("-", "") + key = key.replace("--", "") try: # get string options if value is None and nfo.get('video', key): From 09c2d843576339004b23afb7fea7f81069dbcdff Mon Sep 17 00:00:00 2001 From: Zykino Date: Mon, 14 Dec 2020 21:59:12 +0100 Subject: [PATCH 11/15] Add a progression bar to Peertube upload --- prismedia/pt_upload.py | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/prismedia/pt_upload.py b/prismedia/pt_upload.py index 7c779ad..512c24b 100644 --- a/prismedia/pt_upload.py +++ b/prismedia/pt_upload.py @@ -14,7 +14,8 @@ from tzlocal import get_localzone from configparser import RawConfigParser from requests_oauthlib import OAuth2Session from oauthlib.oauth2 import LegacyApplicationClient -from requests_toolbelt.multipart.encoder import MultipartEncoder +from requests_toolbelt import MultipartEncoder, MultipartEncoderMonitor +from clint.textui.progress import Bar as ProgressBar from . import utils logger = logging.getLogger('Prismedia') @@ -303,7 +304,9 @@ def upload_video(oauth, secret, options): if options.get('--url-only') or options.get('--batch'): logger_stdout = logging.getLogger('stdoutlogs') - multipart_data = MultipartEncoder(fields) + encoder = MultipartEncoder(fields) + progress_callback = create_callback(encoder) + multipart_data = MultipartEncoderMonitor(encoder, progress_callback) headers = { 'Content-Type': multipart_data.content_type @@ -311,12 +314,14 @@ def upload_video(oauth, secret, options): response = oauth.post(url + "/api/v1/videos/upload", data=multipart_data, headers=headers) + if response is not None: if response.status_code == 200: jresponse = response.json() jresponse = jresponse['video'] uuid = jresponse['uuid'] video_id = str(jresponse['id']) + logger.info('Peertube: Video was successfully uploaded.') template = 'Peertube: Watch it at %s/videos/watch/%s.' logger.info(template % (url, uuid)) @@ -333,6 +338,30 @@ def upload_video(oauth, secret, options): '%s') % response) exit(1) +upload_finished = False +def create_callback(encoder): + encoder_len = encoder.len + upload_size_MB = encoder_len * (1 / (1024 * 1024)) + + bar = ProgressBar(expected_size=100, label=f"Peertube upload progress ({upload_size_MB:.2f}MB) ", filled_char='=') + + def callback(monitor): + # We want the condition to capture the varible from the parent scope, not a local variable that is created after + global upload_finished + percentage = int((monitor.bytes_read / encoder_len) * 100) + bar.show(percentage) + + if monitor.bytes_read == encoder_len: + if not upload_finished: + # We get two time in the callback with both bytes equals, skip the first + upload_finished = True + else: + # Print a blank line to not (partly) override the progress bar + print() + logger.info("PeertubeĀ : Upload finish, Processingā€¦") + + return callback + def run(options): secret = RawConfigParser() From 93f1205ab89766e436f4466a5e8cdadfc327ba95 Mon Sep 17 00:00:00 2001 From: Zykino Date: Sat, 9 Jan 2021 13:40:21 +0100 Subject: [PATCH 12/15] Make it possible to choose between multiples tipes of progress bar --- prismedia/pt_upload.py | 28 ++++++++++++++++++++-------- prismedia/upload.py | 11 +++++++++++ 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/prismedia/pt_upload.py b/prismedia/pt_upload.py index 512c24b..1c57fd9 100644 --- a/prismedia/pt_upload.py +++ b/prismedia/pt_upload.py @@ -305,7 +305,7 @@ def upload_video(oauth, secret, options): logger_stdout = logging.getLogger('stdoutlogs') encoder = MultipartEncoder(fields) - progress_callback = create_callback(encoder) + progress_callback = create_callback(encoder, options.get('--progress')) multipart_data = MultipartEncoderMonitor(encoder, progress_callback) headers = { @@ -338,20 +338,32 @@ def upload_video(oauth, secret, options): '%s') % response) exit(1) + upload_finished = False -def create_callback(encoder): - encoder_len = encoder.len - upload_size_MB = encoder_len * (1 / (1024 * 1024)) +def create_callback(encoder, progress_type): + upload_size_MB = encoder.len * (1 / (1024 * 1024)) + + if progress_type is None or "percentage" in progress_type.lower(): + progress_lambda = lambda x: int((x / encoder.len) * 100) # Default to percentage + elif "bigfile" in progress_type.lower(): + progress_lambda = lambda x: x * (1 / (1024 * 1024)) # MB + elif "accurate" in progress_type.lower(): + progress_lambda = lambda x: x * (1 / (1024)) # kB + else: + # Should not happen outside of development when adding partly a progress type + logger.critical("Peertube: Unknown progress type `" + progress_type + "`") + exit(1) - bar = ProgressBar(expected_size=100, label=f"Peertube upload progress ({upload_size_MB:.2f}MB) ", filled_char='=') + bar = ProgressBar(expected_size=progress_lambda(encoder.len), label=f"Peertube upload progress ({upload_size_MB:.2f}MB) ", filled_char='=') def callback(monitor): # We want the condition to capture the varible from the parent scope, not a local variable that is created after global upload_finished - percentage = int((monitor.bytes_read / encoder_len) * 100) - bar.show(percentage) + progress = progress_lambda(monitor.bytes_read) + + bar.show(progress) - if monitor.bytes_read == encoder_len: + if monitor.bytes_read == encoder.len: if not upload_finished: # We get two time in the callback with both bytes equals, skip the first upload_finished = True diff --git a/prismedia/upload.py b/prismedia/upload.py index ea907ee..94ca61d 100755 --- a/prismedia/upload.py +++ b/prismedia/upload.py @@ -49,6 +49,7 @@ Options: If the playlist is not found, spawn an error except if --playlistCreate is set. --playlistCreate Create the playlist if not exists. (default do not create) Only relevant if --playlist is set. + --progress=STRING Set the progress bar view, one of percentage, bigFile, accurate. -h --help Show this help. --version Show version. @@ -150,6 +151,7 @@ VALID_LANGUAGES = ('arabic', 'english', 'french', 'german', 'hindi', 'italian', 'japanese', 'korean', 'mandarin', 'portuguese', 'punjabi', 'russian', 'spanish') +VALID_PROGRESS = ('percentage', 'bigfile', 'accurate') def validateVideo(path): @@ -235,6 +237,14 @@ def validateLogLevel(loglevel): return True +def validateProgress(progress): + for prgs in progress.split(','): + if prgs.lower().replace(" ", "") not in VALID_PROGRESS: + return False + + return True + + def _optionnalOrStrict(key, scope, error): option = key.replace('-', '') option = option[0].upper() + option[1:] @@ -385,6 +395,7 @@ def main(): Optional('--channelCreate'): bool, Optional('--playlist'): Or(None, str), Optional('--playlistCreate'): bool, + Optional('--progress'): Or(None, And(str, validateProgress, error="Sorry, progress visualisation not supported")), '--help': bool, '--version': bool, # This allow to return all other options for further use: https://github.com/keleshev/schema#extra-keys From c4e3243131ca5a9f0a341823df892ce6cdeed775 Mon Sep 17 00:00:00 2001 From: Zykino Date: Sat, 9 Jan 2021 14:23:20 +0100 Subject: [PATCH 13/15] Add clint as requirement --- prismedia/utils.py | 4 ++-- pyproject.toml | 2 +- requirements.txt | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/prismedia/utils.py b/prismedia/utils.py index fcea677..86d4a33 100644 --- a/prismedia/utils.py +++ b/prismedia/utils.py @@ -166,8 +166,8 @@ def parseNFO(options): elif isfile(video_directory + "/" + "NFO.txt"): nfo_txt = loadNFO(video_directory + "/" + "NFO.txt") - if isfile(video_directory + "/" + directory_name+ ".txt"): - nfo_directory = loadNFO(video_directory + "/" + directory_name+ ".txt") + if isfile(video_directory + "/" + directory_name + ".txt"): + nfo_directory = loadNFO(video_directory + "/" + directory_name + ".txt") if options.get('--name'): if isfile(video_directory + "/" + options.get('--name')): diff --git a/pyproject.toml b/pyproject.toml index 75186f0..bfd8876 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,6 +18,7 @@ keywords = ['peertube', 'youtube', 'prismedia'] [tool.poetry.dependencies] python = ">=3.5" +clint = "^0.5.1" configparser = "^3.7.1" docopt = "^0.6.2" future = "^0.17.1" @@ -46,4 +47,3 @@ prismedia = 'prismedia.upload:main' [build-system] requires = ["poetry>=0.12"] build-backend = "poetry.masonry.api" - diff --git a/requirements.txt b/requirements.txt index 59546a1..be9ff52 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,6 +7,7 @@ certifi==2020.4.5.1 \ chardet==3.0.4 \ --hash=sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691 \ --hash=sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae +clint==0.5.1 configparser==3.8.1 \ --hash=sha256:45d1272aad6cfd7a8a06cf5c73f2ceb6a190f6acc1fa707e7f82a4c053b28b18 \ --hash=sha256:bc37850f0cc42a1725a796ef7d92690651bf1af37d744cc63161dac62cabee17 From 6add1407323356ab103a27050b19cc178e988f42 Mon Sep 17 00:00:00 2001 From: Zykino Date: Sat, 23 Jan 2021 12:24:10 +0100 Subject: [PATCH 14/15] Disable the progressbar when the user want a quiet output --- prismedia/pt_upload.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/prismedia/pt_upload.py b/prismedia/pt_upload.py index 1c57fd9..6b43151 100644 --- a/prismedia/pt_upload.py +++ b/prismedia/pt_upload.py @@ -305,8 +305,11 @@ def upload_video(oauth, secret, options): logger_stdout = logging.getLogger('stdoutlogs') encoder = MultipartEncoder(fields) - progress_callback = create_callback(encoder, options.get('--progress')) - multipart_data = MultipartEncoderMonitor(encoder, progress_callback) + if options.get('--quiet'): + multipart_data = encoder + else: + progress_callback = create_callback(encoder, options.get('--progress')) + multipart_data = MultipartEncoderMonitor(encoder, progress_callback) headers = { 'Content-Type': multipart_data.content_type From 339caeb7f75c7283543308252b91682e5b3cfe43 Mon Sep 17 00:00:00 2001 From: LecygneNoir Date: Sat, 23 Jan 2021 12:59:12 +0100 Subject: [PATCH 15/15] Bump files to v0.11.0, fix #50 --- CHANGELOG.md | 1 + poetry.lock | 234 ++++++++++++++++++++++++++++++++++---------- prismedia/upload.py | 4 +- pyproject.toml | 2 +- 4 files changed, 186 insertions(+), 55 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eb4d58f..b507c12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ## Features - Add the configuration of Original date of Record for Youtube and Peertube (see #50) + - Add a progress bar when uploading on Peertube (Thanks @Zykino, see #52) ## v0.10.3 diff --git a/poetry.lock b/poetry.lock index ebaf6e1..39dd1d9 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,3 +1,11 @@ +[[package]] +category = "main" +description = "Command Arguments for Humans." +name = "args" +optional = false +python-versions = "*" +version = "0.1.0" + [[package]] category = "main" description = "Extensible memoizing collections and decorators" @@ -6,21 +14,40 @@ optional = false python-versions = "*" version = "3.1.1" +[[package]] +category = "main" +description = "Extensible memoizing collections and decorators" +name = "cachetools" +optional = false +python-versions = "~=3.5" +version = "4.2.0" + [[package]] category = "main" description = "Python package for providing Mozilla's CA Bundle." name = "certifi" optional = false python-versions = "*" -version = "2020.6.20" +version = "2020.12.5" [[package]] category = "main" description = "Universal encoding detector for Python 2 and 3" name = "chardet" optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "4.0.0" + +[[package]] +category = "main" +description = "Python Command Line Interface Tools" +name = "clint" +optional = false python-versions = "*" -version = "3.0.4" +version = "0.5.1" + +[package.dependencies] +args = "*" [[package]] category = "main" @@ -64,7 +91,7 @@ description = "Google API client core library" name = "google-api-core" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" -version = "1.22.2" +version = "1.23.0" [package.dependencies] google-auth = ">=1.21.1,<2.0dev" @@ -73,7 +100,29 @@ protobuf = ">=3.12.0" pytz = "*" requests = ">=2.18.0,<3.0.0dev" setuptools = ">=34.0.0" -six = ">=1.10.0" +six = ">=1.13.0" + +[package.extras] +grpc = ["grpcio (>=1.29.0,<2.0dev)"] +grpcgcp = ["grpcio-gcp (>=0.2.2)"] +grpcio-gcp = ["grpcio-gcp (>=0.2.2)"] + +[[package]] +category = "main" +description = "Google API client core library" +name = "google-api-core" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*" +version = "1.25.0" + +[package.dependencies] +google-auth = ">=1.21.1,<2.0dev" +googleapis-common-protos = ">=1.6.0,<2.0dev" +protobuf = ">=3.12.0" +pytz = "*" +requests = ">=2.18.0,<3.0.0dev" +setuptools = ">=40.3.0" +six = ">=1.13.0" [package.extras] grpc = ["grpcio (>=1.29.0,<2.0dev)"] @@ -86,7 +135,7 @@ description = "Google API Client Library for Python" name = "google-api-python-client" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" -version = "1.12.1" +version = "1.12.2" [package.dependencies] google-api-core = ">=1.21.0,<2dev" @@ -102,7 +151,7 @@ description = "Google Authentication Library" name = "google-auth" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" -version = "1.21.1" +version = "1.23.0" [package.dependencies] cachetools = ">=2.0.0,<5.0" @@ -114,6 +163,30 @@ six = ">=1.9.0" python = ">=3.5" version = ">=3.1.4,<5" +[package.extras] +aiohttp = ["aiohttp (>=3.6.2,<4.0.0dev)"] + +[[package]] +category = "main" +description = "Google Authentication Library" +name = "google-auth" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*" +version = "1.24.0" + +[package.dependencies] +cachetools = ">=2.0.0,<5.0" +pyasn1-modules = ">=0.2.1" +setuptools = ">=40.3.0" +six = ">=1.9.0" + +[package.dependencies.rsa] +python = ">=3.6" +version = ">=3.1.4,<5" + +[package.extras] +aiohttp = ["aiohttp (>=3.6.2,<4.0.0dev)"] + [[package]] category = "main" description = "Google Authentication Library: httplib2 transport" @@ -142,6 +215,21 @@ requests-oauthlib = ">=0.7.0" [package.extras] tool = ["click"] +[[package]] +category = "main" +description = "Google Authentication Library" +name = "google-auth-oauthlib" +optional = false +python-versions = ">=3.6" +version = "0.4.2" + +[package.dependencies] +google-auth = "*" +requests-oauthlib = ">=0.7.0" + +[package.extras] +tool = ["click"] + [[package]] category = "main" description = "Common protobufs used in Google APIs" @@ -192,10 +280,9 @@ description = "Protocol Buffers" name = "protobuf" optional = false python-versions = "*" -version = "3.13.0" +version = "3.14.0" [package.dependencies] -setuptools = "*" six = ">=1.9" [[package]] @@ -223,7 +310,7 @@ description = "File type identification using libmagic" name = "python-magic" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "0.4.18" +version = "0.4.20" [[package]] category = "main" @@ -240,7 +327,7 @@ description = "World timezone definitions, modern and historical" name = "pytz" optional = false python-versions = "*" -version = "2020.1" +version = "2020.5" [[package]] category = "main" @@ -248,13 +335,13 @@ description = "Python HTTP for Humans." name = "requests" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "2.24.0" +version = "2.25.1" [package.dependencies] certifi = ">=2017.4.17" -chardet = ">=3.0.2,<4" +chardet = ">=3.0.2,<5" idna = ">=2.5,<3" -urllib3 = ">=1.21.1,<1.25.0 || >1.25.0,<1.25.1 || >1.25.1,<1.26" +urllib3 = ">=1.21.1,<1.27" [package.extras] security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"] @@ -289,7 +376,7 @@ requests = ">=2.0.1,<3.0.0" [[package]] category = "main" description = "Pure-Python RSA implementation" -marker = "python_version >= \"3.5\"" +marker = "python_version >= \"3.6\"" name = "rsa" optional = false python-versions = "*" @@ -298,6 +385,18 @@ version = "4.4" [package.dependencies] pyasn1 = ">=0.1.3" +[[package]] +category = "main" +description = "Pure-Python RSA implementation" +marker = "python_version >= \"3.5\"" +name = "rsa" +optional = false +python-versions = ">=3.5, <4" +version = "4.7" + +[package.dependencies] +pyasn1 = ">=0.1.3" + [[package]] category = "main" description = "Simple data validation library" @@ -334,7 +433,7 @@ description = "ASCII transliterations of Unicode text" name = "unidecode" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "1.1.1" +version = "1.1.2" [[package]] category = "main" @@ -356,22 +455,43 @@ version = "1.22" secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7,<2.0)"] +[[package]] +category = "main" +description = "HTTP library with thread-safe connection pooling, file post, and more." +name = "urllib3" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" +version = "1.26.2" + +[package.extras] +brotli = ["brotlipy (>=0.6.0)"] +secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] +socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7,<2.0)"] + [metadata] -content-hash = "5f912013e1ff1f79bdecd9626157960bd0ccc41f441370419888238adc32b385" +content-hash = "41a9471d93da0f5e3d684cdf9e8f981659030d67f29ef6bf55c07a0d49a3ee93" python-versions = ">=3.5" [metadata.files] +args = [ + {file = "args-0.1.0.tar.gz", hash = "sha256:a785b8d837625e9b61c39108532d95b85274acd679693b71ebb5156848fcf814"}, +] cachetools = [ {file = "cachetools-3.1.1-py2.py3-none-any.whl", hash = "sha256:428266a1c0d36dc5aca63a2d7c5942e88c2c898d72139fca0e97fdd2380517ae"}, {file = "cachetools-3.1.1.tar.gz", hash = "sha256:8ea2d3ce97850f31e4a08b0e2b5e6c34997d7216a9d2c98e0f3978630d4da69a"}, + {file = "cachetools-4.2.0-py3-none-any.whl", hash = "sha256:c6b07a6ded8c78bf36730b3dc452dfff7d95f2a12a2fed856b1a0cb13ca78c61"}, + {file = "cachetools-4.2.0.tar.gz", hash = "sha256:3796e1de094f0eaca982441c92ce96c68c89cced4cd97721ab297ea4b16db90e"}, ] certifi = [ - {file = "certifi-2020.6.20-py2.py3-none-any.whl", hash = "sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41"}, - {file = "certifi-2020.6.20.tar.gz", hash = "sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3"}, + {file = "certifi-2020.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"}, + {file = "certifi-2020.12.5.tar.gz", hash = "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c"}, ] chardet = [ - {file = "chardet-3.0.4-py2.py3-none-any.whl", hash = "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"}, - {file = "chardet-3.0.4.tar.gz", hash = "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae"}, + {file = "chardet-4.0.0-py2.py3-none-any.whl", hash = "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"}, + {file = "chardet-4.0.0.tar.gz", hash = "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"}, +] +clint = [ + {file = "clint-0.5.1.tar.gz", hash = "sha256:05224c32b1075563d0b16d0015faaf9da43aa214e4a2140e51f08789e7a4c5aa"}, ] configparser = [ {file = "configparser-3.8.1-py2.py3-none-any.whl", hash = "sha256:45d1272aad6cfd7a8a06cf5c73f2ceb6a190f6acc1fa707e7f82a4c053b28b18"}, @@ -388,16 +508,20 @@ future = [ {file = "future-0.17.1.tar.gz", hash = "sha256:67045236dcfd6816dc439556d009594abf643e5eb48992e36beac09c2ca659b8"}, ] google-api-core = [ - {file = "google-api-core-1.22.2.tar.gz", hash = "sha256:779107f17e0fef8169c5239d56a8fbff03f9f72a3893c0c9e5842ec29dfedd54"}, - {file = "google_api_core-1.22.2-py2.py3-none-any.whl", hash = "sha256:67e33a852dcca7cb7eff49abc35c8cc2c0bb8ab11397dc8306d911505cae2990"}, + {file = "google-api-core-1.23.0.tar.gz", hash = "sha256:1bb3c485c38eacded8d685b1759968f6cf47dd9432922d34edb90359eaa391e2"}, + {file = "google_api_core-1.23.0-py2.py3-none-any.whl", hash = "sha256:94d8c707d358d8d9e8b0045c42be20efb58433d308bd92cf748511c7825569c8"}, + {file = "google-api-core-1.25.0.tar.gz", hash = "sha256:d967beae8d8acdb88fb2f6f769e2ee0ee813042576a08891bded3b8e234150ae"}, + {file = "google_api_core-1.25.0-py2.py3-none-any.whl", hash = "sha256:4656345cba9627ab1290eab51300a6397cc50370d99366133df1ae64b744e1eb"}, ] google-api-python-client = [ - {file = "google-api-python-client-1.12.1.tar.gz", hash = "sha256:ddadc243ce627512c2a27e11d369f5ddf658ef80dbffb247787499486ef1ea98"}, - {file = "google_api_python_client-1.12.1-py2.py3-none-any.whl", hash = "sha256:750316d670119bf680c24ff73825a05b1b4f48b9157bd48c6e3f2bea15ceb586"}, + {file = "google-api-python-client-1.12.2.tar.gz", hash = "sha256:54a7d330833a2e7b0587446d7e4ae6d0244925a9a8e1dfe878f3f7e06cdedb62"}, + {file = "google_api_python_client-1.12.2-py2.py3-none-any.whl", hash = "sha256:05cb331ed1aa15746f606c7e36ea51dbe7c29b1a5df9bbf58140901fe23d7142"}, ] google-auth = [ - {file = "google-auth-1.21.1.tar.gz", hash = "sha256:bcbd9f970e7144fe933908aa286d7a12c44b7deb6d78a76871f0377a29d09789"}, - {file = "google_auth-1.21.1-py2.py3-none-any.whl", hash = "sha256:f4d5093f13b1b1c0a434ab1dc851cd26a983f86a4d75c95239974e33ed406a87"}, + {file = "google-auth-1.23.0.tar.gz", hash = "sha256:5176db85f1e7e837a646cd9cede72c3c404ccf2e3373d9ee14b2db88febad440"}, + {file = "google_auth-1.23.0-py2.py3-none-any.whl", hash = "sha256:b728625ff5dfce8f9e56a499c8a4eb51443a67f20f6d28b67d5774c310ec4b6b"}, + {file = "google-auth-1.24.0.tar.gz", hash = "sha256:0b0e026b412a0ad096e753907559e4bdb180d9ba9f68dd9036164db4fdc4ad2e"}, + {file = "google_auth-1.24.0-py2.py3-none-any.whl", hash = "sha256:ce752cc51c31f479dbf9928435ef4b07514b20261b021c7383bee4bda646acb8"}, ] google-auth-httplib2 = [ {file = "google-auth-httplib2-0.0.4.tar.gz", hash = "sha256:8d092cc60fb16517b12057ec0bba9185a96e3b7169d86ae12eae98e645b7bc39"}, @@ -406,6 +530,8 @@ google-auth-httplib2 = [ google-auth-oauthlib = [ {file = "google-auth-oauthlib-0.4.1.tar.gz", hash = "sha256:88d2cd115e3391eb85e1243ac6902e76e77c5fe438b7276b297fbe68015458dd"}, {file = "google_auth_oauthlib-0.4.1-py2.py3-none-any.whl", hash = "sha256:a92a0f6f41a0fb6138454fbc02674e64f89d82a244ea32f98471733c8ef0e0e1"}, + {file = "google-auth-oauthlib-0.4.2.tar.gz", hash = "sha256:65b65bc39ad8cab15039b35e5898455d3d66296d0584d96fe0e79d67d04c51d9"}, + {file = "google_auth_oauthlib-0.4.2-py2.py3-none-any.whl", hash = "sha256:d4d98c831ea21d574699978827490a41b94f05d565c617fe1b420e88f1fc8d8d"}, ] googleapis-common-protos = [ {file = "googleapis-common-protos-1.52.0.tar.gz", hash = "sha256:560716c807117394da12cecb0a54da5a451b5cf9866f1d37e9a5e2329a665351"}, @@ -424,24 +550,24 @@ oauthlib = [ {file = "oauthlib-2.1.0.tar.gz", hash = "sha256:ac35665a61c1685c56336bda97d5eefa246f1202618a1d6f34fccb1bdd404162"}, ] protobuf = [ - {file = "protobuf-3.13.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:9c2e63c1743cba12737169c447374fab3dfeb18111a460a8c1a000e35836b18c"}, - {file = "protobuf-3.13.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:1e834076dfef9e585815757a2c7e4560c7ccc5962b9d09f831214c693a91b463"}, - {file = "protobuf-3.13.0-cp35-cp35m-macosx_10_9_intel.whl", hash = "sha256:df3932e1834a64b46ebc262e951cd82c3cf0fa936a154f0a42231140d8237060"}, - {file = "protobuf-3.13.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:8c35bcbed1c0d29b127c886790e9d37e845ffc2725cc1db4bd06d70f4e8359f4"}, - {file = "protobuf-3.13.0-cp35-cp35m-win32.whl", hash = "sha256:339c3a003e3c797bc84499fa32e0aac83c768e67b3de4a5d7a5a9aa3b0da634c"}, - {file = "protobuf-3.13.0-cp35-cp35m-win_amd64.whl", hash = "sha256:361acd76f0ad38c6e38f14d08775514fbd241316cce08deb2ce914c7dfa1184a"}, - {file = "protobuf-3.13.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:9edfdc679a3669988ec55a989ff62449f670dfa7018df6ad7f04e8dbacb10630"}, - {file = "protobuf-3.13.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:5db9d3e12b6ede5e601b8d8684a7f9d90581882925c96acf8495957b4f1b204b"}, - {file = "protobuf-3.13.0-cp36-cp36m-win32.whl", hash = "sha256:c8abd7605185836f6f11f97b21200f8a864f9cb078a193fe3c9e235711d3ff1e"}, - {file = "protobuf-3.13.0-cp36-cp36m-win_amd64.whl", hash = "sha256:4d1174c9ed303070ad59553f435846a2f877598f59f9afc1b89757bdf846f2a7"}, - {file = "protobuf-3.13.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0bba42f439bf45c0f600c3c5993666fcb88e8441d011fad80a11df6f324eef33"}, - {file = "protobuf-3.13.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:c0c5ab9c4b1eac0a9b838f1e46038c3175a95b0f2d944385884af72876bd6bc7"}, - {file = "protobuf-3.13.0-cp37-cp37m-win32.whl", hash = "sha256:f68eb9d03c7d84bd01c790948320b768de8559761897763731294e3bc316decb"}, - {file = "protobuf-3.13.0-cp37-cp37m-win_amd64.whl", hash = "sha256:91c2d897da84c62816e2f473ece60ebfeab024a16c1751aaf31100127ccd93ec"}, - {file = "protobuf-3.13.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3dee442884a18c16d023e52e32dd34a8930a889e511af493f6dc7d4d9bf12e4f"}, - {file = "protobuf-3.13.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:e7662437ca1e0c51b93cadb988f9b353fa6b8013c0385d63a70c8a77d84da5f9"}, - {file = "protobuf-3.13.0-py2.py3-none-any.whl", hash = "sha256:d69697acac76d9f250ab745b46c725edf3e98ac24763990b24d58c16c642947a"}, - {file = "protobuf-3.13.0.tar.gz", hash = "sha256:6a82e0c8bb2bf58f606040cc5814e07715b2094caeba281e2e7d0b0e2e397db5"}, + {file = "protobuf-3.14.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:629b03fd3caae7f815b0c66b41273f6b1900a579e2ccb41ef4493a4f5fb84f3a"}, + {file = "protobuf-3.14.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:5b7a637212cc9b2bcf85dd828b1178d19efdf74dbfe1ddf8cd1b8e01fdaaa7f5"}, + {file = "protobuf-3.14.0-cp35-cp35m-macosx_10_9_intel.whl", hash = "sha256:43b554b9e73a07ba84ed6cf25db0ff88b1e06be610b37656e292e3cbb5437472"}, + {file = "protobuf-3.14.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:5e9806a43232a1fa0c9cf5da8dc06f6910d53e4390be1fa06f06454d888a9142"}, + {file = "protobuf-3.14.0-cp35-cp35m-win32.whl", hash = "sha256:1c51fda1bbc9634246e7be6016d860be01747354ed7015ebe38acf4452f470d2"}, + {file = "protobuf-3.14.0-cp35-cp35m-win_amd64.whl", hash = "sha256:4b74301b30513b1a7494d3055d95c714b560fbb630d8fb9956b6f27992c9f980"}, + {file = "protobuf-3.14.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:86a75477addde4918e9a1904e5c6af8d7b691f2a3f65587d73b16100fbe4c3b2"}, + {file = "protobuf-3.14.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:ecc33531a213eee22ad60e0e2aaea6c8ba0021f0cce35dbf0ab03dee6e2a23a1"}, + {file = "protobuf-3.14.0-cp36-cp36m-win32.whl", hash = "sha256:72230ed56f026dd664c21d73c5db73ebba50d924d7ba6b7c0d81a121e390406e"}, + {file = "protobuf-3.14.0-cp36-cp36m-win_amd64.whl", hash = "sha256:0fc96785262042e4863b3f3b5c429d4636f10d90061e1840fce1baaf59b1a836"}, + {file = "protobuf-3.14.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4e75105c9dfe13719b7293f75bd53033108f4ba03d44e71db0ec2a0e8401eafd"}, + {file = "protobuf-3.14.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:2a7e2fe101a7ace75e9327b9c946d247749e564a267b0515cf41dfe450b69bac"}, + {file = "protobuf-3.14.0-cp37-cp37m-win32.whl", hash = "sha256:b0d5d35faeb07e22a1ddf8dce620860c8fe145426c02d1a0ae2688c6e8ede36d"}, + {file = "protobuf-3.14.0-cp37-cp37m-win_amd64.whl", hash = "sha256:8971c421dbd7aad930c9bd2694122f332350b6ccb5202a8b7b06f3f1a5c41ed5"}, + {file = "protobuf-3.14.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9616f0b65a30851e62f1713336c931fcd32c057202b7ff2cfbfca0fc7d5e3043"}, + {file = "protobuf-3.14.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:22bcd2e284b3b1d969c12e84dc9b9a71701ec82d8ce975fdda19712e1cfd4e00"}, + {file = "protobuf-3.14.0-py2.py3-none-any.whl", hash = "sha256:0e247612fadda953047f53301a7b0407cb0c3cb4ae25a6fde661597a04039b3c"}, + {file = "protobuf-3.14.0.tar.gz", hash = "sha256:1d63eb389347293d8915fb47bee0951c7b5dab522a4a60118b9a18f33e21f8ce"}, ] pyasn1 = [ {file = "pyasn1-0.4.8-py2.4.egg", hash = "sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3"}, @@ -474,8 +600,8 @@ pyasn1-modules = [ {file = "pyasn1_modules-0.2.8-py3.7.egg", hash = "sha256:c29a5e5cc7a3f05926aff34e097e84f8589cd790ce0ed41b67aed6857b26aafd"}, ] python-magic = [ - {file = "python-magic-0.4.18.tar.gz", hash = "sha256:b757db2a5289ea3f1ced9e60f072965243ea43a2221430048fd8cacab17be0ce"}, - {file = "python_magic-0.4.18-py2.py3-none-any.whl", hash = "sha256:356efa93c8899047d1eb7d3eb91e871ba2f5b1376edbaf4cc305e3c872207355"}, + {file = "python-magic-0.4.20.tar.gz", hash = "sha256:0cc52ccad086c377b9194014e3dbf98d94b194344630172510a6a3e716b47801"}, + {file = "python_magic-0.4.20-py2.py3-none-any.whl", hash = "sha256:33ce94d9395aa269a9c5fac10ae124a5fb328ebe248f36efc5a43922edee662e"}, ] python-magic-bin = [ {file = "python_magic_bin-0.4.14-py2.py3-none-macosx_10_6_intel.whl", hash = "sha256:7b1743b3dbf16601d6eedf4e7c2c9a637901b0faaf24ad4df4d4527e7d8f66a4"}, @@ -483,12 +609,12 @@ python-magic-bin = [ {file = "python_magic_bin-0.4.14-py2.py3-none-win_amd64.whl", hash = "sha256:90be6206ad31071a36065a2fc169c5afb5e0355cbe6030e87641c6c62edc2b69"}, ] pytz = [ - {file = "pytz-2020.1-py2.py3-none-any.whl", hash = "sha256:a494d53b6d39c3c6e44c3bec237336e14305e4f29bbf800b599253057fbb79ed"}, - {file = "pytz-2020.1.tar.gz", hash = "sha256:c35965d010ce31b23eeb663ed3cc8c906275d6be1a34393a1d73a41febf4a048"}, + {file = "pytz-2020.5-py2.py3-none-any.whl", hash = "sha256:16962c5fb8db4a8f63a26646d8886e9d769b6c511543557bc84e9569fb9a9cb4"}, + {file = "pytz-2020.5.tar.gz", hash = "sha256:180befebb1927b16f6b57101720075a984c019ac16b1b7575673bea42c6c3da5"}, ] requests = [ - {file = "requests-2.24.0-py2.py3-none-any.whl", hash = "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898"}, - {file = "requests-2.24.0.tar.gz", hash = "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b"}, + {file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"}, + {file = "requests-2.25.1.tar.gz", hash = "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804"}, ] requests-oauthlib = [ {file = "requests-oauthlib-0.8.0.tar.gz", hash = "sha256:883ac416757eada6d3d07054ec7092ac21c7f35cb1d2cf82faf205637081f468"}, @@ -501,6 +627,8 @@ requests-toolbelt = [ rsa = [ {file = "rsa-4.4-py2.py3-none-any.whl", hash = "sha256:4afbaaecc3e9550c7351fdf0ab3fea1857ff616b85bab59215f00fb42e0e9582"}, {file = "rsa-4.4.tar.gz", hash = "sha256:5d95293bbd0fbee1dd9cb4b72d27b723942eb50584abc8c4f5f00e4bcfa55307"}, + {file = "rsa-4.7-py3-none-any.whl", hash = "sha256:a8774e55b59fd9fc893b0d05e9bfc6f47081f46ff5b46f39ccf24631b7be356b"}, + {file = "rsa-4.7.tar.gz", hash = "sha256:69805d6b69f56eb05b62daea3a7dbd7aa44324ad1306445e05da8060232d00f4"}, ] schema = [ {file = "schema-0.7.3-py2.py3-none-any.whl", hash = "sha256:c331438b60f634cab5664ab720d3083cc444f924d55269530c36b33e3354276f"}, @@ -514,8 +642,8 @@ tzlocal = [ {file = "tzlocal-1.5.1.tar.gz", hash = "sha256:4ebeb848845ac898da6519b9b31879cf13b6626f7184c496037b818e238f2c4e"}, ] unidecode = [ - {file = "Unidecode-1.1.1-py2.py3-none-any.whl", hash = "sha256:1d7a042116536098d05d599ef2b8616759f02985c85b4fef50c78a5aaf10822a"}, - {file = "Unidecode-1.1.1.tar.gz", hash = "sha256:2b6aab710c2a1647e928e36d69c21e76b453cd455f4e2621000e54b2a9b8cce8"}, + {file = "Unidecode-1.1.2-py2.py3-none-any.whl", hash = "sha256:4c9d15d2f73eb0d2649a151c566901f80a030da1ccb0a2043352e1dbf647586b"}, + {file = "Unidecode-1.1.2.tar.gz", hash = "sha256:a039f89014245e0cad8858976293e23501accc9ff5a7bdbc739a14a2b7b85cdc"}, ] uritemplate = [ {file = "uritemplate-3.0.1-py2.py3-none-any.whl", hash = "sha256:07620c3f3f8eed1f12600845892b0e036a2420acf513c53f7de0abd911a5894f"}, @@ -524,4 +652,6 @@ uritemplate = [ urllib3 = [ {file = "urllib3-1.22-py2.py3-none-any.whl", hash = "sha256:06330f386d6e4b195fbfc736b297f58c5a892e4440e54d294d7004e3a9bbea1b"}, {file = "urllib3-1.22.tar.gz", hash = "sha256:cc44da8e1145637334317feebd728bd869a35285b93cbb4cca2577da7e62db4f"}, + {file = "urllib3-1.26.2-py2.py3-none-any.whl", hash = "sha256:d8ff90d979214d7b4f8ce956e80f4028fc6860e4431f731ea4a8c08f23f99473"}, + {file = "urllib3-1.26.2.tar.gz", hash = "sha256:19188f96923873c92ccb987120ec4acaa12f0461fa9ce5d3d0772bc965a39e08"}, ] diff --git a/prismedia/upload.py b/prismedia/upload.py index 94ca61d..7220b7d 100755 --- a/prismedia/upload.py +++ b/prismedia/upload.py @@ -49,7 +49,7 @@ Options: If the playlist is not found, spawn an error except if --playlistCreate is set. --playlistCreate Create the playlist if not exists. (default do not create) Only relevant if --playlist is set. - --progress=STRING Set the progress bar view, one of percentage, bigFile, accurate. + --progress=STRING Set the progress bar view, one of percentage, bigFile (MB), accurate (KB). -h --help Show this help. --version Show version. @@ -136,7 +136,7 @@ except ImportError: 'see https://github.com/ahupp/python-magic\n') exit(1) -VERSION = "prismedia v0.10.3" +VERSION = "prismedia v0.11.0" VALID_PRIVACY_STATUSES = ('public', 'private', 'unlisted') VALID_CATEGORIES = ( diff --git a/pyproject.toml b/pyproject.toml index bfd8876..b8e946c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "prismedia" -version = "0.10.3" +version = "0.11.0" description = "scripting your way to upload videos on peertube and youtube" authors = [ "LecygneNoir ",