From a725e848ab85a74df7c290efc758f448035c0a66 Mon Sep 17 00:00:00 2001 From: Zykino Date: Mon, 8 Feb 2021 16:30:44 +0100 Subject: [PATCH 01/15] Add an option to use some credits easiely This tells youtube the apikey is still used and they should not remove all of the quota. --- prismedia/upload.py | 10 ++++++++++ prismedia/yt_upload.py | 15 +++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/prismedia/upload.py b/prismedia/upload.py index 7220b7d..2ed5283 100755 --- a/prismedia/upload.py +++ b/prismedia/upload.py @@ -7,6 +7,7 @@ prismedia - tool to upload videos to Peertube and Youtube Usage: prismedia --file= [options] prismedia -f --tags=STRING [options] + prismedia --hearthbeat prismedia -h | --help prismedia --version @@ -50,6 +51,9 @@ Options: --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 (MB), accurate (KB). + + --hearthbeat Use some credits to show some activity for you apikey so the platform know it is used and would not put your quota to 0 (only Youtube currently) + -h --help Show this help. --version Show version. @@ -396,11 +400,17 @@ def main(): Optional('--playlist'): Or(None, str), Optional('--playlistCreate'): bool, Optional('--progress'): Or(None, And(str, validateProgress, error="Sorry, progress visualisation not supported")), + '--hearthbeat': bool, '--help': bool, '--version': bool, # This allow to return all other options for further use: https://github.com/keleshev/schema#extra-keys object: object }) + + if options.get('--hearthbeat'): + yt_upload.hearthbeat() + exit(0) + # We need to validate early options first as withNFO and logs options should be prioritized try: options = earlyoptionSchema.validate(options) diff --git a/prismedia/yt_upload.py b/prismedia/yt_upload.py index 22db640..d050336 100644 --- a/prismedia/yt_upload.py +++ b/prismedia/yt_upload.py @@ -331,6 +331,21 @@ def resumable_upload(request, resource, method, options): time.sleep(sleep_seconds) +def hearthbeat(): + """Use the minimums credits possibles of the API so google does not readuce to 0 the allowed credits. + This apparently happens after 90 days without any usage of credits. + For more info see the official documentations : + - General informations about quotas : https://developers.google.com/youtube/v3/getting-started#quota + - Quota costs for API requests : https://developers.google.com/youtube/v3/determine_quota_cost + - ToS (Americas) #Usage and Quotas : https://developers.google.com/youtube/terms/api-services-terms-of-service#usage-and-quotas""" + youtube = get_authenticated_service() + try: + get_playlist_by_name(youtube, "Foo") + except HttpError as e: + logger.error('Youtube : An HTTP error %d occurred on hearthbeat:\n%s' % + (e.resp.status, e.content)) + + def run(options): youtube = get_authenticated_service() try: From ea39fe9854f6f5bce4804d4272a788583bf60d0d Mon Sep 17 00:00:00 2001 From: Zykino Date: Mon, 8 Feb 2021 16:31:10 +0100 Subject: [PATCH 02/15] Fix some spacing --- prismedia/yt_upload.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/prismedia/yt_upload.py b/prismedia/yt_upload.py index d050336..be23702 100644 --- a/prismedia/yt_upload.py +++ b/prismedia/yt_upload.py @@ -60,6 +60,7 @@ def get_authenticated_service(): check_authenticated_scopes() flow = InstalledAppFlow.from_client_secrets_file( CLIENT_SECRETS_FILE, SCOPES) + if exists(CREDENTIALS_PATH): with open(CREDENTIALS_PATH, 'r') as f: credential_params = json.load(f) @@ -76,7 +77,7 @@ def get_authenticated_service(): p = copy.deepcopy(vars(credentials)) del p["expiry"] json.dump(p, f) - return build(API_SERVICE_NAME, API_VERSION, credentials=credentials, cache_discovery=False) + return build(API_SERVICE_NAME, API_VERSION, credentials=credentials, cache_discovery=False) def check_authenticated_scopes(): From 29b1747c3ed17ddcb81a45bed24810bc80b12506 Mon Sep 17 00:00:00 2001 From: Zykino Date: Mon, 8 Feb 2021 16:33:41 +0100 Subject: [PATCH 03/15] =?UTF-8?q?Visit=20all=20of=20Youtube=E2=80=99s=20pl?= =?UTF-8?q?aylists?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- prismedia/yt_upload.py | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/prismedia/yt_upload.py b/prismedia/yt_upload.py index be23702..07c9688 100644 --- a/prismedia/yt_upload.py +++ b/prismedia/yt_upload.py @@ -182,14 +182,24 @@ def initialize_upload(youtube, options): def get_playlist_by_name(youtube, playlist_name): - response = youtube.playlists().list( - part='snippet,id', - mine=True, - maxResults=50 - ).execute() - for playlist in response["items"]: - if playlist["snippet"]['title'] == playlist_name: - return playlist['id'] + pageToken = "" + while pageToken != None: + response = youtube.playlists().list( + part='snippet,id', + mine=True, + maxResults=50, + pageToken=pageToken + ).execute() + + for playlist in response["items"]: + if playlist["snippet"]["title"] == playlist_name: + return playlist["id"] + + # Ask next page if there are any + if "nextPageToken" in response: + pageToken = response["nextPageToken"] + else: + pageToken = None def create_playlist(youtube, playlist_name): From a4f162320dcdfc6cc6109ae84115e8172b6cdda8 Mon Sep 17 00:00:00 2001 From: Zykino Date: Sun, 14 Feb 2021 00:58:52 +0100 Subject: [PATCH 04/15] Fix some spacing formatting and documentation --- README.md | 63 ++++++++++++++++++++++-------------------- prismedia/pt_upload.py | 2 +- prismedia/yt_upload.py | 26 ++++++++--------- 3 files changed, 47 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index 4139c9f..c10e6f7 100644 --- a/README.md +++ b/README.md @@ -23,23 +23,20 @@ Scripting your way to upload videos to peertube and youtube. Works with Python 3 ### From pip -Simply install with - -```bash +Simply install with +```sh pip install prismedia ``` -Upgrade with - -```bash +Upgrade with +```sh pip install --upgrade prismedia ``` ### From source -Get the source: - -```bash +Get the source: +```sh git clone https://git.lecygnenoir.info/LecygneNoir/prismedia.git prismedia ``` @@ -49,11 +46,10 @@ You may use pip to install requirements: `pip install -r requirements.txt` if yo Otherwise, you can use [poetry](https://python-poetry.org), which create a virtualenv for the project directly (Or use the existing virtualenv if one is activated) -``` +```sh poetry install ``` - ## Configuration Generate sample files with `python -m prismedia.genconfig`. @@ -72,7 +68,7 @@ Youtube uses combination of oauth and API access to identify. The first time you connect, prismedia will open your browser to ask you to authenticate to Youtube and allow the app to use your Youtube channel. **It is here you choose which channel you will upload to**. -Once authenticated, the token is stored inside the file ``.youtube_credentials.json``. +Once authenticated, the token is stored inside the file `.youtube_credentials.json`. Prismedia will try to use this file at each launch, and re-ask for authentication if it does not exist. **Oauth**: @@ -95,35 +91,42 @@ Support only mp4 for cross compatibility between Youtube and Peertube. Here are some demonstration of main usage you would like! Upload a video: -``` -prismedia --file="yourvideo.mp4" +```sh +python -m prismedia --file="yourvideo.mp4" ``` Specify description and tags: -``` -prismedia --file="yourvideo.mp4" -d "My supa description" -t "tag1,tag2,foo" +```sh +python -m 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" +```sh +python -m 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 +```sh +python -m 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) -``` -prismedia --file="yourvideo.mp4" --nfo /path/to/your/nfo.txt +```sh +python -m prismedia --file="yourvideo.mp4" --nfo /path/to/your/nfo.txt ``` +Use some credits to show some activity for you apikey so the platform know it is used and would not put your quota to 0 (only Youtube currently). -Take a look at all available options with `--help`! +To prevent Youtube from inactivating your apikey after 90days of inactivity it is recommended to launch this command automatically from a script around once a month. It will mwke a call to use a few credits from your daily quota. +On Linux and MacOS, you can use cron, on Windows the "Task Scheduler". +```sh +python -m prismedia --hearthbeat ``` -prismedia --help + +Take a look at all available options with `--help`! +```sh +python -m prismedia --help ``` ## Enhanced use of NFO @@ -136,7 +139,7 @@ Basically, Prismedia will now load options in this order, using the last value f `nfo.txt < directory_name.txt < video_name.txt < command line NFO < command line argument` You'll find a complete set of samples in the [prismedia/samples](prismedia/samples) directory so let's take it as an example: -``` +```sh $ tree Recipes/ Recipes/ ├── cli_nfo.txt @@ -149,9 +152,9 @@ Recipes/ └── yourvideo2.txt ``` -By using -``` -prismedia --file=/path/to/Recipes/yourvideo1.mp4 --nfo=/path/to/Recipes/cli_nfo.txt --cca +By using +```sh +python -m prismedia --file=/path/to/Recipes/yourvideo1.mp4 --nfo=/path/to/Recipes/cli_nfo.txt --cca ``` Prismedia will: @@ -186,7 +189,7 @@ Available strict options: - --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 + - --withChannel Prevent upload if no channel ## Features @@ -224,4 +227,4 @@ Available strict options: Inspired by [peeror](https://git.rigelk.eu/rigelk/peeror) and [youtube-upload](https://github.com/tokland/youtube-upload) ## Contributors -Thanks to: @Zykino, @meewan, @rigelk 😘 \ No newline at end of file +Thanks to: @Zykino, @meewan, @rigelk 😘 diff --git a/prismedia/pt_upload.py b/prismedia/pt_upload.py index 6b43151..adb5a36 100644 --- a/prismedia/pt_upload.py +++ b/prismedia/pt_upload.py @@ -373,7 +373,7 @@ def create_callback(encoder, progress_type): else: # Print a blank line to not (partly) override the progress bar print() - logger.info("Peertube : Upload finish, Processing…") + logger.info("Peertube: Upload finish, Processing…") return callback diff --git a/prismedia/yt_upload.py b/prismedia/yt_upload.py index 07c9688..e8809c5 100644 --- a/prismedia/yt_upload.py +++ b/prismedia/yt_upload.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # coding: utf-8 -# From Youtube samples : https://raw.githubusercontent.com/youtube/api-samples/master/python/upload_video.py # noqa +# From Youtube samples: https://raw.githubusercontent.com/youtube/api-samples/master/python/upload_video.py # noqa import http.client import httplib2 @@ -304,7 +304,7 @@ def resumable_upload(request, resource, method, options): status, response = request.next_chunk() if response is not None: if method == 'insert' and 'id' in response: - logger.info('Youtube : Video was successfully uploaded.') + logger.info('Youtube: Video was successfully uploaded.') template = 'Youtube: Watch it at https://youtu.be/%s (post-encoding could take some time)' logger.info(template % response['id']) template_stdout = 'https://youtu.be/%s' @@ -316,28 +316,28 @@ def resumable_upload(request, resource, method, options): elif method != 'insert' or "id" not in response: logger.info('Youtube: Thumbnail was successfully set.') else: - template = ('Youtube : The upload failed with an ' + template = ('Youtube: The upload failed with an ' 'unexpected response: %s') logger.critical(template % response) exit(1) except HttpError as e: if e.resp.status in RETRIABLE_STATUS_CODES: - template = 'Youtube : A retriable HTTP error %d occurred:\n%s' + template = 'Youtube: A retriable HTTP error %d occurred:\n%s' error = template % (e.resp.status, e.content) else: raise except RETRIABLE_EXCEPTIONS as e: - error = 'Youtube : A retriable error occurred: %s' % e + error = 'Youtube: A retriable error occurred: %s' % e if error is not None: logger.warning(error) retry += 1 if retry > MAX_RETRIES: - logger.error('Youtube : No longer attempting to retry.') + logger.error('Youtube: No longer attempting to retry.') max_sleep = 2 ** retry sleep_seconds = random.random() * max_sleep - logger.warning('Youtube : Sleeping %f seconds and then retrying...' + logger.warning('Youtube: Sleeping %f seconds and then retrying...' % sleep_seconds) time.sleep(sleep_seconds) @@ -345,15 +345,15 @@ def resumable_upload(request, resource, method, options): def hearthbeat(): """Use the minimums credits possibles of the API so google does not readuce to 0 the allowed credits. This apparently happens after 90 days without any usage of credits. - For more info see the official documentations : - - General informations about quotas : https://developers.google.com/youtube/v3/getting-started#quota - - Quota costs for API requests : https://developers.google.com/youtube/v3/determine_quota_cost - - ToS (Americas) #Usage and Quotas : https://developers.google.com/youtube/terms/api-services-terms-of-service#usage-and-quotas""" + For more info see the official documentations: + - General informations about quotas: https://developers.google.com/youtube/v3/getting-started#quota + - Quota costs for API requests: https://developers.google.com/youtube/v3/determine_quota_cost + - ToS (Americas) #Usage and Quotas: https://developers.google.com/youtube/terms/api-services-terms-of-service#usage-and-quotas""" youtube = get_authenticated_service() try: get_playlist_by_name(youtube, "Foo") except HttpError as e: - logger.error('Youtube : An HTTP error %d occurred on hearthbeat:\n%s' % + logger.error('Youtube: An HTTP error %d occurred on hearthbeat:\n%s' % (e.resp.status, e.content)) @@ -362,5 +362,5 @@ def run(options): try: initialize_upload(youtube, options) except HttpError as e: - logger.error('Youtube : An HTTP error %d occurred:\n%s' % (e.resp.status, + logger.error('Youtube: An HTTP error %d occurred:\n%s' % (e.resp.status, e.content)) From cbf3386bac6f60921d658350ffab51cfa4eb9900 Mon Sep 17 00:00:00 2001 From: LecygneNoir Date: Sun, 14 Feb 2021 11:52:12 +0100 Subject: [PATCH 05/15] Remove confusing warning when using genconfig, see #55 --- prismedia/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/prismedia/__init__.py b/prismedia/__init__.py index 7ec26e8..1c5c2fa 100644 --- a/prismedia/__init__.py +++ b/prismedia/__init__.py @@ -2,4 +2,3 @@ from future import standard_library standard_library.install_aliases() from . import upload -from . import genconfig From cdef038323455f36be10b75d1e3b4229cada9ea8 Mon Sep 17 00:00:00 2001 From: LecygneNoir Date: Sat, 27 Feb 2021 13:09:40 +0100 Subject: [PATCH 06/15] Move logger initialization in __init__.py to be able to use it from any module --- prismedia/__init__.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/prismedia/__init__.py b/prismedia/__init__.py index 1c5c2fa..d1dde1f 100644 --- a/prismedia/__init__.py +++ b/prismedia/__init__.py @@ -1,4 +1,12 @@ from future import standard_library standard_library.install_aliases() +import logging +logger = logging.getLogger('Prismedia') +logger.setLevel(logging.INFO) +ch = logging.StreamHandler() +ch.setLevel(logging.INFO) +formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s: %(message)s') +ch.setFormatter(formatter) +logger.addHandler(ch) from . import upload From 1a006f3b6c4cf6bc34eb7c0a672113b92603346e Mon Sep 17 00:00:00 2001 From: LecygneNoir Date: Sat, 27 Feb 2021 13:10:14 +0100 Subject: [PATCH 07/15] Improve genconfig system and documentation for easier use, cf #55 --- README.md | 28 +++++++++++++++++----------- prismedia/genconfig.py | 22 ++++++++++++++++++++-- prismedia/upload.py | 6 ------ pyproject.toml | 1 + 4 files changed, 38 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index c10e6f7..3add1af 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ git clone https://git.lecygnenoir.info/LecygneNoir/prismedia.git prismedia ``` You may use pip to install requirements: `pip install -r requirements.txt` if you want to use the script directly. -(*note:* requirements are generated via `poetry export -f requirements.txt`) +(**note:** requirements are generated via `poetry export -f requirements.txt`) Otherwise, you can use [poetry](https://python-poetry.org), which create a virtualenv for the project directly (Or use the existing virtualenv if one is activated) @@ -50,18 +50,24 @@ Otherwise, you can use [poetry](https://python-poetry.org), which create a virtu poetry install ``` + ## Configuration -Generate sample files with `python -m prismedia.genconfig`. -Then rename and edit `peertube_secret` and `youtube_secret.json` with your credentials. (see below) +Generate configuration files by running `prismedia-init`. + +Then, edit them to fill your credential as explained below. ### Peertube -Set your credentials, peertube server URL. -You can get client_id and client_secret by logging in your peertube website and reaching the URL: +Configuration is in **peertube_secret** file. +You need your usual credentials and Peertube instance URL, in addition with API client_id and client_secret. + +You can get client_id and client_secret by logging in your peertube instance and reaching the URL: https://domain.example/api/v1/oauth-clients/local -You can set ``OAUTHLIB_INSECURE_TRANSPORT`` to 1 if you do not use https (not recommended) + +*Alternatively, you can set ``OAUTHLIB_INSECURE_TRANSPORT`` to 1 if you do not use https (not recommended)* ### Youtube +Configuration is in **youtube_secret.json** file. Youtube uses combination of oauth and API access to identify. **Credentials** @@ -88,7 +94,7 @@ 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)) -Here are some demonstration of main usage you would like! +Here are some demonstration of main usage: Upload a video: ```sh @@ -105,7 +111,7 @@ Provide a thumbnail: python -m 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.: +Publish on Peertube only, while using a channel and a playlist, creating them if they do not exist: ```sh python -m prismedia --file="yourvideo.mp4" --platform=peertube --channel="Cooking recipes" --playlist="Cake recipes" --channelCreate --playlistCreate ``` @@ -131,9 +137,9 @@ python -m prismedia --help ## Enhanced use of NFO Since Prismedia v0.9.0, the NFO system has been improved to allow hierarchical loading. -First of all, **if you already used nfo**, either with `--nfo` or by using `videoname.txt`, nothing changes :-) +First, **if you already used nfo**, either with `--nfo` or by using `videoname.txt`, nothing changes :-) -But you are now able to use a more flexible NFO system, by using priorities. This allow you to set some defaults to avoid recreating a full nfo for each video +But you are now able to use a more flexible NFO system, by using priorities. This allows you to set some defaults to avoid recreating a full nfo for each video Basically, Prismedia will now load options in this order, using the last value found in case of conflict: `nfo.txt < directory_name.txt < video_name.txt < command line NFO < command line argument` @@ -227,4 +233,4 @@ Available strict options: Inspired by [peeror](https://git.rigelk.eu/rigelk/peeror) and [youtube-upload](https://github.com/tokland/youtube-upload) ## Contributors -Thanks to: @Zykino, @meewan, @rigelk 😘 +Thanks to: @LecygneNoir, @Zykino, @meewan, @rigelk 😘 \ No newline at end of file diff --git a/prismedia/genconfig.py b/prismedia/genconfig.py index f03e423..3f05dbb 100644 --- a/prismedia/genconfig.py +++ b/prismedia/genconfig.py @@ -1,6 +1,17 @@ -from os.path import join, abspath, isfile, dirname +from os.path import join, abspath, isfile, dirname, exists from os import listdir from shutil import copyfile +import logging +logger = logging.getLogger('Prismedia') + + +def overwrite_or_not(question): + while True: + reply = str(input(question + ' (Yes/[No]): ') or "No").lower().strip() + if reply[:1] == 'y': + return True + if reply[:1] == 'n': + return False def genconfig(): @@ -8,7 +19,14 @@ def genconfig(): files = [f for f in listdir(path) if isfile(join(path, f))] for f in files: - copyfile(join(path, f), f) + final_f = f.replace(".sample", "") + overwrite = True + if exists(final_f): + overwrite = overwrite_or_not(final_f + " already exists. Do you want to overwrite it?") + + if overwrite: + copyfile(join(path, f), final_f) + logger.info(str(final_f) + " correctly generated, you may now edit it to fill your credentials.") if __name__ == '__main__': diff --git a/prismedia/upload.py b/prismedia/upload.py index 2ed5283..a020eea 100755 --- a/prismedia/upload.py +++ b/prismedia/upload.py @@ -110,12 +110,6 @@ import os import datetime import logging logger = logging.getLogger('Prismedia') -logger.setLevel(logging.INFO) -ch = logging.StreamHandler() -ch.setLevel(logging.INFO) -formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s: %(message)s') -ch.setFormatter(formatter) -logger.addHandler(ch) from docopt import docopt diff --git a/pyproject.toml b/pyproject.toml index b8e946c..2a4c393 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,6 +44,7 @@ urllib3 = "^1.22" [tool.poetry.scripts] prismedia = 'prismedia.upload:main' +prismedia-init = 'prismedia.genconfig:genconfig' [build-system] requires = ["poetry>=0.12"] build-backend = "poetry.masonry.api" From 85f0fe9b6fbf9561e04f1708de133c99c9465455 Mon Sep 17 00:00:00 2001 From: LecygneNoir Date: Sun, 28 Feb 2021 10:09:54 +0100 Subject: [PATCH 08/15] Add ask_overwirte function to utils in order to be more general and usable in other modules --- prismedia/genconfig.py | 12 ++---------- prismedia/utils.py | 9 +++++++++ 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/prismedia/genconfig.py b/prismedia/genconfig.py index 3f05dbb..03273ac 100644 --- a/prismedia/genconfig.py +++ b/prismedia/genconfig.py @@ -4,15 +4,7 @@ from shutil import copyfile import logging logger = logging.getLogger('Prismedia') - -def overwrite_or_not(question): - while True: - reply = str(input(question + ' (Yes/[No]): ') or "No").lower().strip() - if reply[:1] == 'y': - return True - if reply[:1] == 'n': - return False - +from . import utils def genconfig(): path = join(dirname(__file__), 'config') @@ -22,7 +14,7 @@ def genconfig(): final_f = f.replace(".sample", "") overwrite = True if exists(final_f): - overwrite = overwrite_or_not(final_f + " already exists. Do you want to overwrite it?") + overwrite = utils.ask_overwrite(final_f + " already exists. Do you want to overwrite it?") if overwrite: copyfile(join(path, f), final_f) diff --git a/prismedia/utils.py b/prismedia/utils.py index 86d4a33..9cb87c2 100644 --- a/prismedia/utils.py +++ b/prismedia/utils.py @@ -100,6 +100,15 @@ def getLanguage(language, platform): return PEERTUBE_LANGUAGE[language.lower()] +def ask_overwrite(question): + while True: + reply = str(input(question + ' (Yes/[No]): ') or "No").lower().strip() + if reply[:1] == 'y': + return True + if reply[:1] == 'n': + return False + + def remove_empty_kwargs(**kwargs): good_kwargs = {} if kwargs is not None: From cf3d4c32c3be07a6b0f8e1b16e0b15bde23a1195 Mon Sep 17 00:00:00 2001 From: LecygneNoir Date: Sun, 28 Feb 2021 10:27:40 +0100 Subject: [PATCH 09/15] Better resilience for the genconfig function thanks to happy path and @Zykino suggestion! --- prismedia/genconfig.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/prismedia/genconfig.py b/prismedia/genconfig.py index 03273ac..d3a29c7 100644 --- a/prismedia/genconfig.py +++ b/prismedia/genconfig.py @@ -6,19 +6,18 @@ logger = logging.getLogger('Prismedia') from . import utils + def genconfig(): path = join(dirname(__file__), 'config') files = [f for f in listdir(path) if isfile(join(path, f))] for f in files: final_f = f.replace(".sample", "") - overwrite = True - if exists(final_f): - overwrite = utils.ask_overwrite(final_f + " already exists. Do you want to overwrite it?") + if exists(final_f) and not utils.ask_overwrite(final_f + " already exists. Do you want to overwrite it?"): + continue - if overwrite: - copyfile(join(path, f), final_f) - logger.info(str(final_f) + " correctly generated, you may now edit it to fill your credentials.") + copyfile(join(path, f), final_f) + logger.info(str(final_f) + " correctly generated, you may now edit it to fill your credentials.") if __name__ == '__main__': From e0a63ed4b26a1a88cd86596bccb07dc478e360a4 Mon Sep 17 00:00:00 2001 From: LecygneNoir Date: Sat, 13 Mar 2021 10:36:16 +0100 Subject: [PATCH 10/15] Revert README to use prismedia as it's indeed the binary script installed through pip --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 3add1af..03c9de6 100644 --- a/README.md +++ b/README.md @@ -98,28 +98,28 @@ Here are some demonstration of main usage: Upload a video: ```sh -python -m prismedia --file="yourvideo.mp4" +prismedia --file="yourvideo.mp4" ``` Specify description and tags: ```sh -python -m prismedia --file="yourvideo.mp4" -d "My supa description" -t "tag1,tag2,foo" +prismedia --file="yourvideo.mp4" -d "My supa description" -t "tag1,tag2,foo" ``` Provide a thumbnail: ```sh -python -m prismedia --file="yourvideo.mp4" -d "Video with thumbnail" --thumbnail="/path/to/your/thumbnail.jpg" +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 do not exist: ```sh -python -m prismedia --file="yourvideo.mp4" --platform=peertube --channel="Cooking recipes" --playlist="Cake recipes" --channelCreate --playlistCreate +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) ```sh -python -m prismedia --file="yourvideo.mp4" --nfo /path/to/your/nfo.txt +prismedia --file="yourvideo.mp4" --nfo /path/to/your/nfo.txt ``` Use some credits to show some activity for you apikey so the platform know it is used and would not put your quota to 0 (only Youtube currently). @@ -127,12 +127,12 @@ Use some credits to show some activity for you apikey so the platform know it is To prevent Youtube from inactivating your apikey after 90days of inactivity it is recommended to launch this command automatically from a script around once a month. It will mwke a call to use a few credits from your daily quota. On Linux and MacOS, you can use cron, on Windows the "Task Scheduler". ```sh -python -m prismedia --hearthbeat +prismedia --hearthbeat ``` Take a look at all available options with `--help`! ```sh -python -m prismedia --help +prismedia --help ``` ## Enhanced use of NFO @@ -160,7 +160,7 @@ Recipes/ By using ```sh -python -m prismedia --file=/path/to/Recipes/yourvideo1.mp4 --nfo=/path/to/Recipes/cli_nfo.txt --cca +prismedia --file=/path/to/Recipes/yourvideo1.mp4 --nfo=/path/to/Recipes/cli_nfo.txt --cca ``` Prismedia will: From 2f7629ef1e7db2d04a7172d00eb6a74a44d2bdf5 Mon Sep 17 00:00:00 2001 From: LecygneNoir Date: Sat, 10 Apr 2021 11:34:36 +0200 Subject: [PATCH 11/15] Remove format checks for videos and thumbnail as Youtube and Peertube have no limitation anymore --- prismedia/upload.py | 35 ++--------------------------------- 1 file changed, 2 insertions(+), 33 deletions(-) diff --git a/prismedia/upload.py b/prismedia/upload.py index a020eea..c7dc4c3 100755 --- a/prismedia/upload.py +++ b/prismedia/upload.py @@ -125,14 +125,6 @@ except ImportError: ' is installed: \n' 'see https://github.com/halst/schema\n') exit(1) -try: - # noinspection PyUnresolvedReferences - import magic -except ImportError: - logger.critical('This program requires that the `python-magic` library' - ' is installed, NOT the Python bindings to libmagic API \n' - 'see https://github.com/ahupp/python-magic\n') - exit(1) VERSION = "prismedia v0.11.0" @@ -152,20 +144,6 @@ VALID_LANGUAGES = ('arabic', 'english', 'french', VALID_PROGRESS = ('percentage', 'bigfile', 'accurate') -def validateVideo(path): - supported_types = ['video/mp4'] - detected_type = magic.from_file(path, mime=True) - if detected_type not in supported_types: - print("File", path, "detected type is", detected_type, "which is not one of", supported_types) - - force_file = ['y', 'yes'] - is_forcing = input("Are you sure you selected the correct file? (y/N)") - if is_forcing.lower() not in force_file: - return False - - return path - - def validateCategory(category): if category.lower() in VALID_CATEGORIES: return True @@ -219,15 +197,6 @@ def validateOriginalDate(originalDate): return True -def validateThumbnail(thumbnail): - supported_types = ['image/jpg', 'image/jpeg'] - if os.path.exists(thumbnail) and \ - magic.from_file(thumbnail, mime=True) in supported_types: - return thumbnail - else: - return False - - def validateLogLevel(loglevel): numeric_level = getattr(logging, loglevel, None) if not isinstance(numeric_level, int): @@ -316,7 +285,7 @@ def main(): }) schema = Schema({ - '--file': And(str, os.path.exists, validateVideo, error='file is not supported, please use mp4'), + '--file': And(str, os.path.exists, error='file does not exists, please check path'), # Strict option checks - at the moment Schema needs to check Hook and Optional separately # Hook('--name', handler=_optionnalOrStrict): object, Hook('--description', handler=_optionnalOrStrict): object, @@ -387,7 +356,7 @@ def main(): Optional('--disable-comments'): bool, Optional('--nsfw'): bool, Optional('--thumbnail'): Or(None, And( - str, validateThumbnail, error='thumbnail is not supported, please use jpg/jpeg'), + str, os.path.exists, error='Thumbnail does not exists, please check the path.'), ), Optional('--channel'): Or(None, str), Optional('--channelCreate'): bool, From 0a53e77bd6f771511392717f0ed3611afd8d5d36 Mon Sep 17 00:00:00 2001 From: LecygneNoir Date: Sat, 10 Apr 2021 11:40:27 +0200 Subject: [PATCH 12/15] Add auto search for thumbnail based on .png in addition to .jpg and .jpeg --- prismedia/upload.py | 5 ++--- prismedia/utils.py | 4 ++++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/prismedia/upload.py b/prismedia/upload.py index c7dc4c3..a1feb4d 100755 --- a/prismedia/upload.py +++ b/prismedia/upload.py @@ -12,7 +12,7 @@ Usage: prismedia --version Options: - -f, --file=STRING Path to the video file to upload in mp4. This is the only mandatory option. + -f, --file=STRING Path to the video file to upload. 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. @@ -40,8 +40,7 @@ Options: DATE should be in the past --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 + By default, prismedia search for an image based on video name followed by .jpg, .jpeg or .png --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) diff --git a/prismedia/utils.py b/prismedia/utils.py index 9cb87c2..817af5f 100644 --- a/prismedia/utils.py +++ b/prismedia/utils.py @@ -126,6 +126,8 @@ def searchThumbnail(options): options['--thumbnail'] = video_directory + options.get('--name') + ".jpg" elif isfile(video_directory + options.get('--name') + ".jpeg"): options['--thumbnail'] = video_directory + options.get('--name') + ".jpeg" + elif isfile(video_directory + options.get('--name') + ".png"): + options['--thumbnail'] = video_directory + options.get('--name') + ".png" # 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] @@ -133,6 +135,8 @@ def searchThumbnail(options): options['--thumbnail'] = video_directory + video_file + ".jpg" elif isfile(video_directory + video_file + ".jpeg"): options['--thumbnail'] = video_directory + video_file + ".jpeg" + elif isfile(video_directory + video_file + ".png"): + options['--thumbnail'] = video_directory + video_file + ".png" # Display some info after research if not options.get('--thumbnail'): From f8ae2b1c5e02230388c3de076f12b545cdd705f4 Mon Sep 17 00:00:00 2001 From: LecygneNoir Date: Sat, 10 Apr 2021 11:40:59 +0200 Subject: [PATCH 13/15] Update libraries and dependencies for prismedia --- poetry.lock | 318 +++++++++++++++++-------------------------------- pyproject.toml | 30 +++-- 2 files changed, 126 insertions(+), 222 deletions(-) diff --git a/poetry.lock b/poetry.lock index 39dd1d9..05567b2 100644 --- a/poetry.lock +++ b/poetry.lock @@ -14,14 +14,6 @@ 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." @@ -51,15 +43,15 @@ args = "*" [[package]] category = "main" -description = "Updated configparser from Python 3.7 for Python 2.6+." +description = "Updated configparser from Python 3.8 for Python 2.6+." name = "configparser" optional = false -python-versions = ">=2.6" -version = "3.8.1" +python-versions = ">=3.6" +version = "5.0.2" [package.extras] -docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=3.5,<3.7.3 || >3.7.3)", "pytest-checkdocs (>=1.2)", "pytest-flake8"] +docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] +testing = ["pytest (>=4.6)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "pytest-enabler", "pytest-black (>=0.3.7)", "pytest-mypy"] [[package]] category = "main" @@ -83,29 +75,7 @@ description = "Clean single-source support for Python 3 and 2" name = "future" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" -version = "0.17.1" - -[[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.*" -version = "1.23.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 = ">=34.0.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)"] +version = "0.18.2" [[package]] category = "main" @@ -113,11 +83,12 @@ 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" +version = "1.26.3" [package.dependencies] google-auth = ">=1.21.1,<2.0dev" googleapis-common-protos = ">=1.6.0,<2.0dev" +packaging = ">=14.3" protobuf = ">=3.12.0" pytz = "*" requests = ">=2.18.0,<3.0.0dev" @@ -134,45 +105,24 @@ category = "main" 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.2" +python-versions = ">=3.6" +version = "2.1.0" [package.dependencies] google-api-core = ">=1.21.0,<2dev" -google-auth = ">=1.16.0" -google-auth-httplib2 = ">=0.0.3" -httplib2 = ">=0.9.2,<1dev" +google-auth = ">=1.16.0,<2dev" +google-auth-httplib2 = ">=0.1.0" +httplib2 = ">=0.15.0,<1dev" six = ">=1.13.0,<2dev" uritemplate = ">=3.0.0,<4dev" -[[package]] -category = "main" -description = "Google Authentication Library" -name = "google-auth" -optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" -version = "1.23.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.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" +version = "1.28.1" [package.dependencies] cachetools = ">=2.0.0,<5.0" @@ -186,6 +136,7 @@ version = ">=3.1.4,<5" [package.extras] aiohttp = ["aiohttp (>=3.6.2,<4.0.0dev)"] +pyopenssl = ["pyopenssl (>=20.0.0)"] [[package]] category = "main" @@ -193,53 +144,38 @@ description = "Google Authentication Library: httplib2 transport" name = "google-auth-httplib2" optional = false python-versions = "*" -version = "0.0.4" +version = "0.1.0" [package.dependencies] google-auth = "*" -httplib2 = ">=0.9.1" +httplib2 = ">=0.15.0" six = "*" -[[package]] -category = "main" -description = "Google Authentication Library" -name = "google-auth-oauthlib" -optional = false -python-versions = "*" -version = "0.4.1" - -[package.dependencies] -google-auth = "*" -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" +version = "0.4.4" [package.dependencies] -google-auth = "*" +google-auth = ">=1.0.0" requests-oauthlib = ">=0.7.0" [package.extras] -tool = ["click"] +tool = ["click (>=6.0.0)"] [[package]] category = "main" description = "Common protobufs used in Google APIs" name = "googleapis-common-protos" optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" -version = "1.52.0" +python-versions = ">=3.6" +version = "1.53.0" [package.dependencies] -protobuf = ">=3.6.0" +protobuf = ">=3.12.0" [package.extras] grpc = ["grpcio (>=1.0.0)"] @@ -250,7 +186,10 @@ description = "A comprehensive HTTP client library." name = "httplib2" optional = false python-versions = "*" -version = "0.12.3" +version = "0.19.1" + +[package.dependencies] +pyparsing = ">=2.4.2,<3" [[package]] category = "main" @@ -265,14 +204,24 @@ category = "main" description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" name = "oauthlib" optional = false -python-versions = "*" -version = "2.1.0" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "3.1.0" [package.extras] rsa = ["cryptography"] signals = ["blinker"] signedtoken = ["cryptography", "pyjwt (>=1.0.0)"] -test = ["nose", "unittest2", "cryptography", "mock", "pyjwt (>=1.0.0)", "blinker"] + +[[package]] +category = "main" +description = "Core utilities for Python packages" +name = "packaging" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "20.9" + +[package.dependencies] +pyparsing = ">=2.0.2" [[package]] category = "main" @@ -280,7 +229,7 @@ description = "Protocol Buffers" name = "protobuf" optional = false python-versions = "*" -version = "3.14.0" +version = "3.15.8" [package.dependencies] six = ">=1.9" @@ -306,20 +255,11 @@ pyasn1 = ">=0.4.6,<0.5.0" [[package]] category = "main" -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.20" - -[[package]] -category = "main" -description = "File type identification using libmagic binary package" -marker = "platform_system == \"Windows\"" -name = "python-magic-bin" +description = "Python parsing module" +name = "pyparsing" optional = false -python-versions = "*" -version = "0.4.14" +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +version = "2.4.7" [[package]] category = "main" @@ -327,7 +267,7 @@ description = "World timezone definitions, modern and historical" name = "pytz" optional = false python-versions = "*" -version = "2020.5" +version = "2021.1" [[package]] category = "main" @@ -352,15 +292,15 @@ category = "main" description = "OAuthlib authentication support for Requests." name = "requests-oauthlib" optional = false -python-versions = "*" -version = "0.8.0" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "1.3.0" [package.dependencies] -oauthlib = ">=0.6.2" +oauthlib = ">=3.0.0" requests = ">=2.0.0" [package.extras] -rsa = ["oauthlib (>=0.6.2)", "requests (>=2.0.0)"] +rsa = ["oauthlib (>=3.0.0)"] [[package]] category = "main" @@ -385,25 +325,13 @@ 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" name = "schema" optional = false python-versions = "*" -version = "0.7.3" +version = "0.7.4" [package.dependencies] contextlib2 = ">=0.5.5" @@ -422,7 +350,7 @@ description = "tzinfo object for the local timezone" name = "tzlocal" optional = false python-versions = "*" -version = "1.5.1" +version = "2.1" [package.dependencies] pytz = "*" @@ -433,7 +361,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.2" +version = "1.2.0" [[package]] category = "main" @@ -455,22 +383,9 @@ 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 = "41a9471d93da0f5e3d684cdf9e8f981659030d67f29ef6bf55c07a0d49a3ee93" -python-versions = ">=3.5" +content-hash = "30e57c25d84e4981a4a70f5d30f4b8e2a6163f56b44a1ca170372bfbf91ea527" +python-versions = ">=3.6" [metadata.files] args = [ @@ -479,8 +394,6 @@ args = [ 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.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"}, @@ -494,8 +407,8 @@ 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"}, - {file = "configparser-3.8.1.tar.gz", hash = "sha256:bc37850f0cc42a1725a796ef7d92690651bf1af37d744cc63161dac62cabee17"}, + {file = "configparser-5.0.2-py3-none-any.whl", hash = "sha256:af59f2cdd7efbdd5d111c1976ecd0b82db9066653362f0962d7bf1d3ab89a1fa"}, + {file = "configparser-5.0.2.tar.gz", hash = "sha256:85d5de102cfe6d14a5172676f09d19c465ce63d6019cf0a4ef13385fc535e828"}, ] contextlib2 = [ {file = "contextlib2-0.6.0.post1-py2.py3-none-any.whl", hash = "sha256:3355078a159fbb44ee60ea80abd0d87b80b78c248643b49aa6d94673b413609b"}, @@ -505,69 +418,69 @@ docopt = [ {file = "docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491"}, ] future = [ - {file = "future-0.17.1.tar.gz", hash = "sha256:67045236dcfd6816dc439556d009594abf643e5eb48992e36beac09c2ca659b8"}, + {file = "future-0.18.2.tar.gz", hash = "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"}, ] google-api-core = [ - {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"}, + {file = "google-api-core-1.26.3.tar.gz", hash = "sha256:b914345c7ea23861162693a27703bab804a55504f7e6e9abcaff174d80df32ac"}, + {file = "google_api_core-1.26.3-py2.py3-none-any.whl", hash = "sha256:099762d4b4018cd536bcf85136bf337957da438807572db52f21dc61251be089"}, ] google-api-python-client = [ - {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"}, + {file = "google-api-python-client-2.1.0.tar.gz", hash = "sha256:f9ac377efe69571aea1acc9e15760d4204aca23c4464eb63f963ae4defc95d97"}, + {file = "google_api_python_client-2.1.0-py2.py3-none-any.whl", hash = "sha256:921fe10cdff22d1f5b8af7473f9a298efb991fb6ea67dadd6c37fbecfb0575f4"}, ] google-auth = [ - {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"}, + {file = "google-auth-1.28.1.tar.gz", hash = "sha256:70b39558712826e41f65e5f05a8d879361deaf84df8883e5dd0ec3d0da6ab66e"}, + {file = "google_auth-1.28.1-py2.py3-none-any.whl", hash = "sha256:186fe2564634d67fbbb64f3daf8bc8c9cecbb2a7f535ed1a8a71795e50db8d87"}, ] google-auth-httplib2 = [ - {file = "google-auth-httplib2-0.0.4.tar.gz", hash = "sha256:8d092cc60fb16517b12057ec0bba9185a96e3b7169d86ae12eae98e645b7bc39"}, - {file = "google_auth_httplib2-0.0.4-py2.py3-none-any.whl", hash = "sha256:aeaff501738b289717fac1980db9711d77908a6c227f60e4aa1923410b43e2ee"}, + {file = "google-auth-httplib2-0.1.0.tar.gz", hash = "sha256:a07c39fd632becacd3f07718dfd6021bf396978f03ad3ce4321d060015cc30ac"}, + {file = "google_auth_httplib2-0.1.0-py2.py3-none-any.whl", hash = "sha256:31e49c36c6b5643b57e82617cb3e021e3e1d2df9da63af67252c02fa9c1f4a10"}, ] 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"}, + {file = "google-auth-oauthlib-0.4.4.tar.gz", hash = "sha256:09832c6e75032f93818edf1affe4746121d640c625a5bef9b5c96af676e98eee"}, + {file = "google_auth_oauthlib-0.4.4-py2.py3-none-any.whl", hash = "sha256:0e92aacacfb94978de3b7972cf4b0f204c3cd206f74ddd0dc0b31e91164e6317"}, ] googleapis-common-protos = [ - {file = "googleapis-common-protos-1.52.0.tar.gz", hash = "sha256:560716c807117394da12cecb0a54da5a451b5cf9866f1d37e9a5e2329a665351"}, - {file = "googleapis_common_protos-1.52.0-py2.py3-none-any.whl", hash = "sha256:c8961760f5aad9a711d37b675be103e0cc4e9a39327e0d6d857872f698403e24"}, + {file = "googleapis-common-protos-1.53.0.tar.gz", hash = "sha256:a88ee8903aa0a81f6c3cec2d5cf62d3c8aa67c06439b0496b49048fb1854ebf4"}, + {file = "googleapis_common_protos-1.53.0-py2.py3-none-any.whl", hash = "sha256:f6d561ab8fb16b30020b940e2dd01cd80082f4762fa9f3ee670f4419b4b8dbd0"}, ] httplib2 = [ - {file = "httplib2-0.12.3-py3-none-any.whl", hash = "sha256:23914b5487dfe8ef09db6656d6d63afb0cf3054ad9ebc50868ddc8e166b5f8e8"}, - {file = "httplib2-0.12.3.tar.gz", hash = "sha256:a18121c7c72a56689efbf1aef990139ad940fee1e64c6f2458831736cd593600"}, + {file = "httplib2-0.19.1-py3-none-any.whl", hash = "sha256:2ad195faf9faf079723f6714926e9a9061f694d07724b846658ce08d40f522b4"}, + {file = "httplib2-0.19.1.tar.gz", hash = "sha256:0b12617eeca7433d4c396a100eaecfa4b08ee99aa881e6df6e257a7aad5d533d"}, ] idna = [ {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"}, {file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"}, ] oauthlib = [ - {file = "oauthlib-2.1.0-py2.py3-none-any.whl", hash = "sha256:d883b36b21a6ad813953803edfa563b1b579d79ca758fe950d1bc9e8b326025b"}, - {file = "oauthlib-2.1.0.tar.gz", hash = "sha256:ac35665a61c1685c56336bda97d5eefa246f1202618a1d6f34fccb1bdd404162"}, + {file = "oauthlib-3.1.0-py2.py3-none-any.whl", hash = "sha256:df884cd6cbe20e32633f1db1072e9356f53638e4361bef4e8b03c9127c9328ea"}, + {file = "oauthlib-3.1.0.tar.gz", hash = "sha256:bee41cc35fcca6e988463cacc3bcb8a96224f470ca547e697b604cc697b2f889"}, +] +packaging = [ + {file = "packaging-20.9-py2.py3-none-any.whl", hash = "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"}, + {file = "packaging-20.9.tar.gz", hash = "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5"}, ] protobuf = [ - {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"}, + {file = "protobuf-3.15.8-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:fad4f971ec38d8df7f4b632c819bf9bbf4f57cfd7312cf526c69ce17ef32436a"}, + {file = "protobuf-3.15.8-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:f17b352d7ce33c81773cf81d536ca70849de6f73c96413f17309f4b43ae7040b"}, + {file = "protobuf-3.15.8-cp35-cp35m-macosx_10_9_intel.whl", hash = "sha256:4a054b0b5900b7ea7014099e783fb8c4618e4209fffcd6050857517b3f156e18"}, + {file = "protobuf-3.15.8-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:efa4c4d4fc9ba734e5e85eaced70e1b63fb3c8d08482d839eb838566346f1737"}, + {file = "protobuf-3.15.8-cp35-cp35m-win32.whl", hash = "sha256:07eec4e2ccbc74e95bb9b3afe7da67957947ee95bdac2b2e91b038b832dd71f0"}, + {file = "protobuf-3.15.8-cp35-cp35m-win_amd64.whl", hash = "sha256:f9cadaaa4065d5dd4d15245c3b68b967b3652a3108e77f292b58b8c35114b56c"}, + {file = "protobuf-3.15.8-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:2dc0e8a9e4962207bdc46a365b63a3f1aca6f9681a5082a326c5837ef8f4b745"}, + {file = "protobuf-3.15.8-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:f80afc0a0ba13339bbab25ca0409e9e2836b12bb012364c06e97c2df250c3343"}, + {file = "protobuf-3.15.8-cp36-cp36m-win32.whl", hash = "sha256:c5566f956a26cda3abdfacc0ca2e21db6c9f3d18f47d8d4751f2209d6c1a5297"}, + {file = "protobuf-3.15.8-cp36-cp36m-win_amd64.whl", hash = "sha256:dab75b56a12b1ceb3e40808b5bd9dfdaef3a1330251956e6744e5b6ed8f8830b"}, + {file = "protobuf-3.15.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3053f13207e7f13dc7be5e9071b59b02020172f09f648e85dc77e3fcb50d1044"}, + {file = "protobuf-3.15.8-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:1f0b5d156c3df08cc54bc2c8b8b875648ea4cd7ebb2a9a130669f7547ec3488c"}, + {file = "protobuf-3.15.8-cp37-cp37m-win32.whl", hash = "sha256:90270fe5732c1f1ff664a3bd7123a16456d69b4e66a09a139a00443a32f210b8"}, + {file = "protobuf-3.15.8-cp37-cp37m-win_amd64.whl", hash = "sha256:f42c2f5fb67da5905bfc03733a311f72fa309252bcd77c32d1462a1ad519521e"}, + {file = "protobuf-3.15.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f6077db37bfa16494dca58a4a02bfdacd87662247ad6bc1f7f8d13ff3f0013e1"}, + {file = "protobuf-3.15.8-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:510e66491f1a5ac5953c908aa8300ec47f793130097e4557482803b187a8ee05"}, + {file = "protobuf-3.15.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5ff9fa0e67fcab442af9bc8d4ec3f82cb2ff3be0af62dba047ed4187f0088b7d"}, + {file = "protobuf-3.15.8-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:1c0e9e56202b9dccbc094353285a252e2b7940b74fdf75f1b4e1b137833fabd7"}, + {file = "protobuf-3.15.8-py2.py3-none-any.whl", hash = "sha256:a0a08c6b2e6d6c74a6eb5bf6184968eefb1569279e78714e239d33126e753403"}, + {file = "protobuf-3.15.8.tar.gz", hash = "sha256:0277f62b1e42210cafe79a71628c1d553348da81cbd553402a7f7549c50b11d0"}, ] pyasn1 = [ {file = "pyasn1-0.4.8-py2.4.egg", hash = "sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3"}, @@ -599,26 +512,22 @@ pyasn1-modules = [ {file = "pyasn1_modules-0.2.8-py3.6.egg", hash = "sha256:cbac4bc38d117f2a49aeedec4407d23e8866ea4ac27ff2cf7fb3e5b570df19e0"}, {file = "pyasn1_modules-0.2.8-py3.7.egg", hash = "sha256:c29a5e5cc7a3f05926aff34e097e84f8589cd790ce0ed41b67aed6857b26aafd"}, ] -python-magic = [ - {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"}, - {file = "python_magic_bin-0.4.14-py2.py3-none-win32.whl", hash = "sha256:34a788c03adde7608028203e2dbb208f1f62225ad91518787ae26d603ae68892"}, - {file = "python_magic_bin-0.4.14-py2.py3-none-win_amd64.whl", hash = "sha256:90be6206ad31071a36065a2fc169c5afb5e0355cbe6030e87641c6c62edc2b69"}, +pyparsing = [ + {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, + {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, ] pytz = [ - {file = "pytz-2020.5-py2.py3-none-any.whl", hash = "sha256:16962c5fb8db4a8f63a26646d8886e9d769b6c511543557bc84e9569fb9a9cb4"}, - {file = "pytz-2020.5.tar.gz", hash = "sha256:180befebb1927b16f6b57101720075a984c019ac16b1b7575673bea42c6c3da5"}, + {file = "pytz-2021.1-py2.py3-none-any.whl", hash = "sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798"}, + {file = "pytz-2021.1.tar.gz", hash = "sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da"}, ] requests = [ {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"}, - {file = "requests_oauthlib-0.8.0-py2.py3-none-any.whl", hash = "sha256:50a8ae2ce8273e384895972b56193c7409601a66d4975774c60c2aed869639ca"}, + {file = "requests-oauthlib-1.3.0.tar.gz", hash = "sha256:b4261601a71fd721a8bd6d7aa1cc1d6a8a93b4a9f5e96626f8e4d91e8beeaa6a"}, + {file = "requests_oauthlib-1.3.0-py2.py3-none-any.whl", hash = "sha256:7f71572defaecd16372f9006f33c2ec8c077c3cfa6f5911a9a90202beb513f3d"}, + {file = "requests_oauthlib-1.3.0-py3.7.egg", hash = "sha256:fa6c47b933f01060936d87ae9327fead68768b69c6c9ea2109c48be30f2d4dbc"}, ] requests-toolbelt = [ {file = "requests-toolbelt-0.9.1.tar.gz", hash = "sha256:968089d4584ad4ad7c171454f0a5c6dac23971e9472521ea3b6d49d610aa6fc0"}, @@ -627,23 +536,22 @@ 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"}, - {file = "schema-0.7.3.tar.gz", hash = "sha256:4cf529318cfd1e844ecbe02f41f7e5aa027463e7403666a52746f31f04f47a5e"}, + {file = "schema-0.7.4-py2.py3-none-any.whl", hash = "sha256:cf97e4cd27e203ab6bb35968532de1ed8991bce542a646f0ff1d643629a4945d"}, + {file = "schema-0.7.4.tar.gz", hash = "sha256:fbb6a52eb2d9facf292f233adcc6008cffd94343c63ccac9a1cb1f3e6de1db17"}, ] six = [ {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"}, {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"}, ] tzlocal = [ - {file = "tzlocal-1.5.1.tar.gz", hash = "sha256:4ebeb848845ac898da6519b9b31879cf13b6626f7184c496037b818e238f2c4e"}, + {file = "tzlocal-2.1-py2.py3-none-any.whl", hash = "sha256:e2cb6c6b5b604af38597403e9852872d7f534962ae2954c7f35efcb1ccacf4a4"}, + {file = "tzlocal-2.1.tar.gz", hash = "sha256:643c97c5294aedc737780a49d9df30889321cbe1204eac2c2ec6134035a92e44"}, ] unidecode = [ - {file = "Unidecode-1.1.2-py2.py3-none-any.whl", hash = "sha256:4c9d15d2f73eb0d2649a151c566901f80a030da1ccb0a2043352e1dbf647586b"}, - {file = "Unidecode-1.1.2.tar.gz", hash = "sha256:a039f89014245e0cad8858976293e23501accc9ff5a7bdbc739a14a2b7b85cdc"}, + {file = "Unidecode-1.2.0-py2.py3-none-any.whl", hash = "sha256:12435ef2fc4cdfd9cf1035a1db7e98b6b047fe591892e81f34e94959591fad00"}, + {file = "Unidecode-1.2.0.tar.gz", hash = "sha256:8d73a97d387a956922344f6b74243c2c6771594659778744b2dbdaad8f6b727d"}, ] uritemplate = [ {file = "uritemplate-3.0.1-py2.py3-none-any.whl", hash = "sha256:07620c3f3f8eed1f12600845892b0e036a2420acf513c53f7de0abd911a5894f"}, @@ -652,6 +560,4 @@ 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/pyproject.toml b/pyproject.toml index 2a4c393..91cb2e0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,27 +17,25 @@ homepage = "https://git.lecygnenoir.info/LecygneNoir/prismedia" 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" +python = ">=3.6" +clint = ">=0.5.1" +configparser = ">=3.7.1" +docopt = ">=0.6.2" +future = ">=0.17.1" google-api-python-client = ">=1.7.6" google-auth = ">=1.6.1" google-auth-httplib2 = ">=0.0.3" google-auth-oauthlib = ">=0.2.0" -httplib2 = "^0.12.1" -oauthlib = "^2.1.0" -python-magic = "^0.4.15" -python-magic-bin = { version = "^0.4.14", markers = "platform_system == 'Windows'" } -requests = "^2.18.4" -requests-oauthlib = "^0.8.0" -requests-toolbelt = "^0.9.1" +httplib2 = ">=0.12.1" +oauthlib = ">=2.1.0" +requests = ">=2.18.4" +requests-oauthlib = ">=0.8.0" +requests-toolbelt = ">=0.9.1" schema = ">=0.7.1" -tzlocal = "^1.5.1" -Unidecode = "^1.0.23" -uritemplate = "^3.0.0" -urllib3 = "^1.22" +tzlocal = ">=1.5.1" +Unidecode = ">=1.0.23" +uritemplate = ">=3.0.0" +urllib3 = ">=1.22" [tool.poetry.dev-dependencies] From 0a1360d8e278722443b39fec555fc26306a0e080 Mon Sep 17 00:00:00 2001 From: LecygneNoir Date: Sat, 10 Apr 2021 11:41:15 +0200 Subject: [PATCH 14/15] Prepare changelo for v0.12.0 --- CHANGELOG.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b507c12..6addf5d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,20 @@ # Changelog +## v0.12.0 + +### Features + - Add `--heartbeat` option to send request to youtube API, avoiding youtube to disabling you API account if you do not upload video often (Thanks @Zykino see #54) + - Rework and improve genconfig process to avoid erasing existing configuration and deploy a `prismedia-init` script when installing prismedia (see #55) + - Update multiple dependencies used for prismedia as they were very old. + - Add auto search for thumbnail in `.png` in addition to `.jpg` and `.jepg`. + +### Fixes + - Add pagination for youtube playlist to search for all user playlists (Thanks @Zykino) + - Remove file format check for both videos and thumbnail as Youtube and Peertube now accepts more than .mp4 and .jpg (see #60) + ## v0.11.0 -## Features +### 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) From 8bc79853c836548a6fe26e946a93c1a948441081 Mon Sep 17 00:00:00 2001 From: LecygneNoir Date: Sat, 10 Apr 2021 12:36:46 +0200 Subject: [PATCH 15/15] Bump version to v0.12.0 and prepare poetry build for v0.12.0 --- CHANGELOG.md | 3 +- prismedia/upload.py | 2 +- pyproject.toml | 2 +- requirements.txt | 185 +++++++++++++++++++++++--------------------- 4 files changed, 101 insertions(+), 91 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6addf5d..441aa2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,8 @@ ### Features - Add `--heartbeat` option to send request to youtube API, avoiding youtube to disabling you API account if you do not upload video often (Thanks @Zykino see #54) - - Rework and improve genconfig process to avoid erasing existing configuration and deploy a `prismedia-init` script when installing prismedia (see #55) + - Rework and improve genconfig process to avoid erasing existing configuration and make it more easy to use + - Add a `prismedia-init` script when installing prismedia to easily generate basic configuration (see #55) - Update multiple dependencies used for prismedia as they were very old. - Add auto search for thumbnail in `.png` in addition to `.jpg` and `.jepg`. diff --git a/prismedia/upload.py b/prismedia/upload.py index a1feb4d..bae465b 100755 --- a/prismedia/upload.py +++ b/prismedia/upload.py @@ -125,7 +125,7 @@ except ImportError: 'see https://github.com/halst/schema\n') exit(1) -VERSION = "prismedia v0.11.0" +VERSION = "prismedia v0.12.0" VALID_PRIVACY_STATUSES = ('public', 'private', 'unlisted') VALID_CATEGORIES = ( diff --git a/pyproject.toml b/pyproject.toml index 91cb2e0..329aa51 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "prismedia" -version = "0.11.0" +version = "0.12.0" description = "scripting your way to upload videos on peertube and youtube" authors = [ "LecygneNoir ", diff --git a/requirements.txt b/requirements.txt index be9ff52..fcbe966 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,66 +1,77 @@ +args==0.1.0 \ + --hash=sha256:a785b8d837625e9b61c39108532d95b85274acd679693b71ebb5156848fcf814 cachetools==3.1.1 \ --hash=sha256:428266a1c0d36dc5aca63a2d7c5942e88c2c898d72139fca0e97fdd2380517ae \ --hash=sha256:8ea2d3ce97850f31e4a08b0e2b5e6c34997d7216a9d2c98e0f3978630d4da69a -certifi==2020.4.5.1 \ - --hash=sha256:1d987a998c75633c40847cc966fcf5904906c920a7f17ef374f5aa4282abd304 \ - --hash=sha256:51fcb31174be6e6664c5f69e3e1691a2d72a1a12e90f872cbdb1567eb47b6519 -chardet==3.0.4 \ - --hash=sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691 \ - --hash=sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae -clint==0.5.1 -configparser==3.8.1 \ - --hash=sha256:45d1272aad6cfd7a8a06cf5c73f2ceb6a190f6acc1fa707e7f82a4c053b28b18 \ - --hash=sha256:bc37850f0cc42a1725a796ef7d92690651bf1af37d744cc63161dac62cabee17 +certifi==2020.12.5 \ + --hash=sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830 \ + --hash=sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c +chardet==4.0.0 \ + --hash=sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5 \ + --hash=sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa +clint==0.5.1 \ + --hash=sha256:05224c32b1075563d0b16d0015faaf9da43aa214e4a2140e51f08789e7a4c5aa +configparser==5.0.2 \ + --hash=sha256:af59f2cdd7efbdd5d111c1976ecd0b82db9066653362f0962d7bf1d3ab89a1fa \ + --hash=sha256:85d5de102cfe6d14a5172676f09d19c465ce63d6019cf0a4ef13385fc535e828 +contextlib2==0.6.0.post1 \ + --hash=sha256:3355078a159fbb44ee60ea80abd0d87b80b78c248643b49aa6d94673b413609b \ + --hash=sha256:01f490098c18b19d2bd5bb5dc445b2054d2fa97f09a4280ba2c5f3c394c8162e docopt==0.6.2 \ --hash=sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491 -future==0.17.1 \ - --hash=sha256:67045236dcfd6816dc439556d009594abf643e5eb48992e36beac09c2ca659b8 -google-api-core==1.16.0 \ - --hash=sha256:92e962a087f1c4b8d1c5c88ade1c1dfd550047dcffb320c57ef6a534a20403e2 \ - --hash=sha256:859f7392676761f2b160c6ee030c3422135ada4458f0948c5690a6a7c8d86294 -google-api-python-client==1.8.0 \ - --hash=sha256:0f5b42a14e2d2f7dee40f2e4514531dbe95ebde9c2173b1c4040a65c427e7900 \ - --hash=sha256:5032ad1af5046889649b3848f2e871889fbb6ae440198a549fe1699581300386 -google-auth==1.13.1 \ - --hash=sha256:a5ee4c40fef77ea756cf2f1c0adcf475ecb53af6700cf9c133354cdc9b267148 \ - --hash=sha256:cab6c707e6ee20e567e348168a5c69dc6480384f777a9e5159f4299ad177dcc0 -google-auth-httplib2==0.0.3 \ - --hash=sha256:098fade613c25b4527b2c08fa42d11f3c2037dda8995d86de0745228e965d445 \ - --hash=sha256:f1c437842155680cf9918df9bc51c1182fda41feef88c34004bd1978c8157e08 -google-auth-oauthlib==0.2.0 \ - --hash=sha256:226d1d0960f86ba5d9efd426a70b291eaba96f47d071657e0254ea969025728a \ - --hash=sha256:81ba22acada4d13b1d83f9371ab19fd61f1250a542d21cf49e4dcf0637a7344a -googleapis-common-protos==1.51.0 \ - --hash=sha256:013c91704279119150e44ef770086fdbba158c1f978a6402167d47d5409e226e -httplib2==0.12.3 \ - --hash=sha256:23914b5487dfe8ef09db6656d6d63afb0cf3054ad9ebc50868ddc8e166b5f8e8 \ - --hash=sha256:a18121c7c72a56689efbf1aef990139ad940fee1e64c6f2458831736cd593600 -idna==2.9 \ - --hash=sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa \ - --hash=sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb -oauthlib==2.1.0 \ - --hash=sha256:d883b36b21a6ad813953803edfa563b1b579d79ca758fe950d1bc9e8b326025b \ - --hash=sha256:ac35665a61c1685c56336bda97d5eefa246f1202618a1d6f34fccb1bdd404162 -protobuf==3.11.3 \ - --hash=sha256:ef2c2e56aaf9ee914d3dccc3408d42661aaf7d9bb78eaa8f17b2e6282f214481 \ - --hash=sha256:dd9aa4401c36785ea1b6fff0552c674bdd1b641319cb07ed1fe2392388e9b0d7 \ - --hash=sha256:310a7aca6e7f257510d0c750364774034272538d51796ca31d42c3925d12a52a \ - --hash=sha256:e512b7f3a4dd780f59f1bf22c302740e27b10b5c97e858a6061772668cd6f961 \ - --hash=sha256:fdfb6ad138dbbf92b5dbea3576d7c8ba7463173f7d2cb0ca1bd336ec88ddbd80 \ - --hash=sha256:e2f8a75261c26b2f5f3442b0525d50fd79a71aeca04b5ec270fc123536188306 \ - --hash=sha256:c40973a0aee65422d8cb4e7d7cbded95dfeee0199caab54d5ab25b63bce8135a \ - --hash=sha256:adf0e4d57b33881d0c63bb11e7f9038f98ee0c3e334c221f0858f826e8fb0151 \ - --hash=sha256:0bae429443cc4748be2aadfdaf9633297cfaeb24a9a02d0ab15849175ce90fab \ - --hash=sha256:e11df1ac6905e81b815ab6fd518e79be0a58b5dc427a2cf7208980f30694b956 \ - --hash=sha256:7774bbbaac81d3ba86de646c39f154afc8156717972bf0450c9dbfa1dc8dbea2 \ - --hash=sha256:8eb9c93798b904f141d9de36a0ba9f9b73cc382869e67c9e642c0aba53b0fc07 \ - --hash=sha256:fac513a9dc2a74b99abd2e17109b53945e364649ca03d9f7a0b96aa8d1807d0a \ - --hash=sha256:82d7ac987715d8d1eb4068bf997f3053468e0ce0287e2729c30601feb6602fee \ - --hash=sha256:73152776dc75f335c476d11d52ec6f0f6925774802cd48d6189f4d5d7fe753f4 \ - --hash=sha256:52e586072612c1eec18e1174f8e3bb19d08f075fc2e3f91d3b16c919078469d0 \ - --hash=sha256:2affcaba328c4662f3bc3c0e9576ea107906b2c2b6422344cdad961734ff6b93 \ - --hash=sha256:24e3b6ad259544d717902777b33966a1a069208c885576254c112663e6a5bb0f \ - --hash=sha256:c77c974d1dadf246d789f6dad1c24426137c9091e930dbf50e0a29c1fcf00b1f +future==0.18.2 \ + --hash=sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d +google-api-core==1.26.3 \ + --hash=sha256:b914345c7ea23861162693a27703bab804a55504f7e6e9abcaff174d80df32ac \ + --hash=sha256:099762d4b4018cd536bcf85136bf337957da438807572db52f21dc61251be089 +google-api-python-client==2.1.0 \ + --hash=sha256:f9ac377efe69571aea1acc9e15760d4204aca23c4464eb63f963ae4defc95d97 \ + --hash=sha256:921fe10cdff22d1f5b8af7473f9a298efb991fb6ea67dadd6c37fbecfb0575f4 +google-auth==1.28.1 \ + --hash=sha256:70b39558712826e41f65e5f05a8d879361deaf84df8883e5dd0ec3d0da6ab66e \ + --hash=sha256:186fe2564634d67fbbb64f3daf8bc8c9cecbb2a7f535ed1a8a71795e50db8d87 +google-auth-httplib2==0.1.0 \ + --hash=sha256:a07c39fd632becacd3f07718dfd6021bf396978f03ad3ce4321d060015cc30ac \ + --hash=sha256:31e49c36c6b5643b57e82617cb3e021e3e1d2df9da63af67252c02fa9c1f4a10 +google-auth-oauthlib==0.4.4 \ + --hash=sha256:09832c6e75032f93818edf1affe4746121d640c625a5bef9b5c96af676e98eee \ + --hash=sha256:0e92aacacfb94978de3b7972cf4b0f204c3cd206f74ddd0dc0b31e91164e6317 +googleapis-common-protos==1.53.0 \ + --hash=sha256:a88ee8903aa0a81f6c3cec2d5cf62d3c8aa67c06439b0496b49048fb1854ebf4 \ + --hash=sha256:f6d561ab8fb16b30020b940e2dd01cd80082f4762fa9f3ee670f4419b4b8dbd0 +httplib2==0.19.1 \ + --hash=sha256:2ad195faf9faf079723f6714926e9a9061f694d07724b846658ce08d40f522b4 \ + --hash=sha256:0b12617eeca7433d4c396a100eaecfa4b08ee99aa881e6df6e257a7aad5d533d +idna==2.10 \ + --hash=sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0 \ + --hash=sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6 +oauthlib==3.1.0 \ + --hash=sha256:df884cd6cbe20e32633f1db1072e9356f53638e4361bef4e8b03c9127c9328ea \ + --hash=sha256:bee41cc35fcca6e988463cacc3bcb8a96224f470ca547e697b604cc697b2f889 +packaging==20.9 \ + --hash=sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a \ + --hash=sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5 +protobuf==3.15.8 \ + --hash=sha256:fad4f971ec38d8df7f4b632c819bf9bbf4f57cfd7312cf526c69ce17ef32436a \ + --hash=sha256:f17b352d7ce33c81773cf81d536ca70849de6f73c96413f17309f4b43ae7040b \ + --hash=sha256:4a054b0b5900b7ea7014099e783fb8c4618e4209fffcd6050857517b3f156e18 \ + --hash=sha256:efa4c4d4fc9ba734e5e85eaced70e1b63fb3c8d08482d839eb838566346f1737 \ + --hash=sha256:07eec4e2ccbc74e95bb9b3afe7da67957947ee95bdac2b2e91b038b832dd71f0 \ + --hash=sha256:f9cadaaa4065d5dd4d15245c3b68b967b3652a3108e77f292b58b8c35114b56c \ + --hash=sha256:2dc0e8a9e4962207bdc46a365b63a3f1aca6f9681a5082a326c5837ef8f4b745 \ + --hash=sha256:f80afc0a0ba13339bbab25ca0409e9e2836b12bb012364c06e97c2df250c3343 \ + --hash=sha256:c5566f956a26cda3abdfacc0ca2e21db6c9f3d18f47d8d4751f2209d6c1a5297 \ + --hash=sha256:dab75b56a12b1ceb3e40808b5bd9dfdaef3a1330251956e6744e5b6ed8f8830b \ + --hash=sha256:3053f13207e7f13dc7be5e9071b59b02020172f09f648e85dc77e3fcb50d1044 \ + --hash=sha256:1f0b5d156c3df08cc54bc2c8b8b875648ea4cd7ebb2a9a130669f7547ec3488c \ + --hash=sha256:90270fe5732c1f1ff664a3bd7123a16456d69b4e66a09a139a00443a32f210b8 \ + --hash=sha256:f42c2f5fb67da5905bfc03733a311f72fa309252bcd77c32d1462a1ad519521e \ + --hash=sha256:f6077db37bfa16494dca58a4a02bfdacd87662247ad6bc1f7f8d13ff3f0013e1 \ + --hash=sha256:510e66491f1a5ac5953c908aa8300ec47f793130097e4557482803b187a8ee05 \ + --hash=sha256:5ff9fa0e67fcab442af9bc8d4ec3f82cb2ff3be0af62dba047ed4187f0088b7d \ + --hash=sha256:1c0e9e56202b9dccbc094353285a252e2b7940b74fdf75f1b4e1b137833fabd7 \ + --hash=sha256:a0a08c6b2e6d6c74a6eb5bf6184968eefb1569279e78714e239d33126e753403 \ + --hash=sha256:0277f62b1e42210cafe79a71628c1d553348da81cbd553402a7f7549c50b11d0 pyasn1==0.4.8 \ --hash=sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3 \ --hash=sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf \ @@ -89,39 +100,37 @@ pyasn1-modules==0.2.8 \ --hash=sha256:426edb7a5e8879f1ec54a1864f16b882c2837bfd06eee62f2c982315ee2473ed \ --hash=sha256:cbac4bc38d117f2a49aeedec4407d23e8866ea4ac27ff2cf7fb3e5b570df19e0 \ --hash=sha256:c29a5e5cc7a3f05926aff34e097e84f8589cd790ce0ed41b67aed6857b26aafd -python-magic==0.4.15 \ - --hash=sha256:f3765c0f582d2dfc72c15f3b5a82aecfae9498bd29ca840d72f37d7bd38bfcd5 \ - --hash=sha256:f2674dcfad52ae6c49d4803fa027809540b130db1dec928cfbb9240316831375 -python-magic-bin==0.4.14; platform_system == "Windows" \ - --hash=sha256:7b1743b3dbf16601d6eedf4e7c2c9a637901b0faaf24ad4df4d4527e7d8f66a4 \ - --hash=sha256:34a788c03adde7608028203e2dbb208f1f62225ad91518787ae26d603ae68892 \ - --hash=sha256:90be6206ad31071a36065a2fc169c5afb5e0355cbe6030e87641c6c62edc2b69 -pytz==2019.3 \ - --hash=sha256:1c557d7d0e871de1f5ccd5833f60fb2550652da6be2693c1e02300743d21500d \ - --hash=sha256:b02c06db6cf09c12dd25137e563b31700d3b80fcc4ad23abb7a315f2789819be -requests==2.23.0 \ - --hash=sha256:43999036bfa82904b6af1d99e4882b560e5e2c68e5c4b0aa03b655f3d7d73fee \ - --hash=sha256:b3f43d496c6daba4493e7c431722aeb7dbc6288f52a6e04e7b6023b0247817e6 -requests-oauthlib==0.8.0 \ - --hash=sha256:883ac416757eada6d3d07054ec7092ac21c7f35cb1d2cf82faf205637081f468 \ - --hash=sha256:50a8ae2ce8273e384895972b56193c7409601a66d4975774c60c2aed869639ca +pyparsing==2.4.7 \ + --hash=sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b \ + --hash=sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1 +pytz==2021.1 \ + --hash=sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798 \ + --hash=sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da +requests==2.25.1 \ + --hash=sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e \ + --hash=sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804 +requests-oauthlib==1.3.0 \ + --hash=sha256:b4261601a71fd721a8bd6d7aa1cc1d6a8a93b4a9f5e96626f8e4d91e8beeaa6a \ + --hash=sha256:7f71572defaecd16372f9006f33c2ec8c077c3cfa6f5911a9a90202beb513f3d \ + --hash=sha256:fa6c47b933f01060936d87ae9327fead68768b69c6c9ea2109c48be30f2d4dbc requests-toolbelt==0.9.1 \ --hash=sha256:968089d4584ad4ad7c171454f0a5c6dac23971e9472521ea3b6d49d610aa6fc0 \ --hash=sha256:380606e1d10dc85c3bd47bf5a6095f815ec007be7a8b69c878507068df059e6f -rsa==4.0 \ - --hash=sha256:14ba45700ff1ec9eeb206a2ce76b32814958a98e372006c8fb76ba820211be66 \ - --hash=sha256:1a836406405730121ae9823e19c6e806c62bbad73f890574fff50efa4122c487 -schema==0.6.8 \ - --hash=sha256:d994b0dc4966000037b26898df638e3e2a694cc73636cb2050e652614a350687 \ - --hash=sha256:fa1a53fe5f3b6929725a4e81688c250f46838e25d8c1885a10a590c8c01a7b74 -six==1.14.0 \ - --hash=sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c \ - --hash=sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a -tzlocal==1.5.1 \ - --hash=sha256:4ebeb848845ac898da6519b9b31879cf13b6626f7184c496037b818e238f2c4e -unidecode==1.1.1 \ - --hash=sha256:1d7a042116536098d05d599ef2b8616759f02985c85b4fef50c78a5aaf10822a \ - --hash=sha256:2b6aab710c2a1647e928e36d69c21e76b453cd455f4e2621000e54b2a9b8cce8 +rsa==4.4; python_version >= "3.6" \ + --hash=sha256:4afbaaecc3e9550c7351fdf0ab3fea1857ff616b85bab59215f00fb42e0e9582 \ + --hash=sha256:5d95293bbd0fbee1dd9cb4b72d27b723942eb50584abc8c4f5f00e4bcfa55307 +schema==0.7.4 \ + --hash=sha256:cf97e4cd27e203ab6bb35968532de1ed8991bce542a646f0ff1d643629a4945d \ + --hash=sha256:fbb6a52eb2d9facf292f233adcc6008cffd94343c63ccac9a1cb1f3e6de1db17 +six==1.15.0 \ + --hash=sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced \ + --hash=sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259 +tzlocal==2.1 \ + --hash=sha256:e2cb6c6b5b604af38597403e9852872d7f534962ae2954c7f35efcb1ccacf4a4 \ + --hash=sha256:643c97c5294aedc737780a49d9df30889321cbe1204eac2c2ec6134035a92e44 +unidecode==1.2.0 \ + --hash=sha256:12435ef2fc4cdfd9cf1035a1db7e98b6b047fe591892e81f34e94959591fad00 \ + --hash=sha256:8d73a97d387a956922344f6b74243c2c6771594659778744b2dbdaad8f6b727d uritemplate==3.0.1 \ --hash=sha256:07620c3f3f8eed1f12600845892b0e036a2420acf513c53f7de0abd911a5894f \ --hash=sha256:5af8ad10cec94f215e3f48112de2022e1d5a37ed427fbd88652fa908f2ab7cae