├── LICENSE ├── src ├── __init__.py ├── utils │ ├── __init__.py │ ├── __pycache__ │ │ └── __init__.cpython-311.pyc │ ├── auth.py │ └── upload.py ├── __pycache__ │ └── __init__.cpython-311.pyc └── main.py ├── tests ├── __init__.py ├── test_main.py └── test_auth.py ├── .gitignore ├── dist ├── youtube_uploader-1.0.1.tar.gz └── youtube_uploader-1.0.1-py3-none-any.whl ├── pyproject.toml ├── setup.py ├── .github └── workflows │ └── main.yml ├── README.md └── poetry.lock /LICENSE: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_main.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/utils/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.json 2 | *.mp4 3 | *.mp3 4 | -------------------------------------------------------------------------------- /dist/youtube_uploader-1.0.1.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agent87/youtube-uploader/HEAD/dist/youtube_uploader-1.0.1.tar.gz -------------------------------------------------------------------------------- /src/__pycache__/__init__.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agent87/youtube-uploader/HEAD/src/__pycache__/__init__.cpython-311.pyc -------------------------------------------------------------------------------- /dist/youtube_uploader-1.0.1-py3-none-any.whl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agent87/youtube-uploader/HEAD/dist/youtube_uploader-1.0.1-py3-none-any.whl -------------------------------------------------------------------------------- /src/utils/__pycache__/__init__.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agent87/youtube-uploader/HEAD/src/utils/__pycache__/__init__.cpython-311.pyc -------------------------------------------------------------------------------- /tests/test_auth.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | 4 | class TestAuthenticationFunction(TestCase): 5 | """Test authentication Function""" 6 | 7 | def test_dummy(self): 8 | """Test Dummy Authentication""" 9 | self.assertEqual("foo".upper(), "FOO") 10 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "youtube-uploader" 3 | version = "1.0.1" 4 | description = "A Youtube Uploader CLI using the official youtube api." 5 | authors = ["Arnaud Kayonga "] 6 | license = "MIT-License" 7 | readme = "README.md" 8 | 9 | packages = [ 10 | { include = "src" } 11 | ] 12 | 13 | [tool.poetry.dependencies] 14 | python = "^3.11" 15 | google = "^3.0.0" 16 | httplib2 = "^0.22.0" 17 | uritemplate = "^4.1.1" 18 | google-auth-httplib2 = "^0.2.0" 19 | google-auth-oauthlib = "^1.2.0" 20 | google-api-python-client = "^2.112.0" 21 | 22 | [tool.poetry.group.dev.dependencies] 23 | py-spy = "^0.3.14" 24 | 25 | [build-system] 26 | requires = ["poetry-core"] 27 | build-backend = "poetry.core.masonry.api" 28 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | setup( 4 | name="youtube-uploader", 5 | version="0.1", 6 | packages=["youtube-uploader"], 7 | install_requires=[ 8 | "google-api-python-client", 9 | "google-auth-oauthlib", 10 | "google-auth-httplib2", 11 | ], 12 | author="Arnaud Kayonga", 13 | author_email="arnauldkayonga1@gmail.com", 14 | description="A command-line to upload videos to Youtube via Youtube API", 15 | keywords="Youtube CLI API", 16 | url="https://github.com/agent87/youtube-uploader", 17 | project_urls={ 18 | "Bug Reports": "https://github.com/agent87/youtube-uploader/issues", 19 | "Source": "https://github.com/agent87/youtube-uploader", 20 | }, 21 | ) 22 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Main Branch Automated Testing 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | test: 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | python-version: ['3.11'] 18 | poetry-version: ['1.7.0'] 19 | os: [ubuntu-latest] 20 | runs-on: ${{ matrix.os }} 21 | 22 | steps: 23 | - uses: actions/checkout@v3 24 | - uses: actions/setup-python@v3 25 | with: 26 | python-version: ${{ matrix.python-version }} 27 | 28 | - name: Run image 29 | uses: abatilo/actions-poetry@v2.1.4 30 | with: 31 | poetry-version: ${{ matrix.poetry-version }} 32 | 33 | - name: Run tests 34 | working-directory: tests/ 35 | run: | 36 | python -m unittest 37 | -------------------------------------------------------------------------------- /src/utils/auth.py: -------------------------------------------------------------------------------- 1 | from googleapiclient.discovery import build 2 | from google_auth_oauthlib.flow import InstalledAppFlow 3 | 4 | 5 | def get_authenticated_service( 6 | client_secrets_file: str, scopes: str, api_service_name: str, api_version: str 7 | ) -> build: 8 | """A function to authenticate and store credentials""" 9 | flow = InstalledAppFlow.from_client_secrets_file(client_secrets_file, scopes) 10 | credentials = flow.run_console() 11 | return build(api_service_name, api_version, credentials=credentials) 12 | 13 | 14 | class AuthenticationController: 15 | """The authentication controller""" 16 | 17 | def __init__(self) -> None: 18 | pass 19 | 20 | @staticmethod 21 | def get_authenticated_service( 22 | client_secrets_file: str, scopes: str, api_service_name: str, api_version: str 23 | ) -> build: 24 | """A function to authenticate and store credentials""" 25 | flow = InstalledAppFlow.from_client_secrets_file(client_secrets_file, scopes) 26 | credentials = flow.run_console() 27 | return build(api_service_name, api_version, credentials=credentials) 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # YouTube Uploader CLI Tool 2 | 3 | ## Overview 4 | 5 | The YouTube Uploader is a Python 2 script designed to simplify the process of uploading machine learning model output videos, particularly those generated in Google Colab, directly to YouTube using the YouTube API v3. This tool aims to streamline data-heavy operations, such as sharing computer vision project results. 6 | 7 | ## Prerequisites 8 | Before using the uploader, ensure the following prerequisites are met: 9 | 10 | YouTube Developer Account/Console: Create an account and set up a project at YouTube Developer Console. 11 | 12 | YouTube API v3 Enabled: Enable the YouTube API v3 for your project in the Developer Console. 13 | 14 | Client ID/Secret Key: Generate a Client ID and Secret Key for authentication. 15 | 16 | ## Dependencies 17 | The script will automatically install the required dependencies. No manual installation is needed. 18 | 19 | ## Usage 20 | To run the script from a live notebook in Google Colab, follow these steps: 21 | 22 | ```python 23 | # Clone the repository 24 | !git clone https://github.com/agent87/youtube_uploader.git 25 | 26 | # Change into the project directory 27 | !cd youtube_uploader 28 | 29 | # Setup the project 30 | !python youtube_uploader/setup.py 31 | 32 | # Run the uploader script 33 | !python youtube_uploader/upload.py 34 | ``` 35 | 36 | ## Important Note 37 | Replace your_email@gmail.com with your actual Gmail address associated with the YouTube Developer Console when prompted by the script. 38 | 39 | ## Contribution 40 | Contributions are welcome! Feel free to submit issues or pull requests. 41 | 42 | ## License 43 | This project is licensed under the MIT License - see the LICENSE file for details. 44 | 45 | Happy uploading with YouTube Uploader! -------------------------------------------------------------------------------- /src/utils/upload.py: -------------------------------------------------------------------------------- 1 | import random 2 | import time 3 | 4 | import http.client 5 | import httplib2 6 | 7 | from googleapiclient.errors import HttpError 8 | from googleapiclient.http import MediaFileUpload 9 | from googleapiclient.discovery import build 10 | 11 | 12 | httplib2.RETRIES = 1 13 | 14 | MAX_RETRIES = 10 15 | 16 | RETRIABLE_EXCEPTIONS = ( 17 | httplib2.HttpLib2Error, 18 | IOError, 19 | http.client.NotConnected, 20 | http.client.IncompleteRead, 21 | http.client.ImproperConnectionState, 22 | http.client.CannotSendRequest, 23 | http.client.CannotSendHeader, 24 | http.client.ResponseNotReady, 25 | http.client.BadStatusLine, 26 | ) 27 | 28 | 29 | RETRIABLE_STATUS_CODES = [500, 502, 503, 504] 30 | 31 | 32 | CLIENT_SECRETS_FILE = input("Enter your client credential secret file path:\n") 33 | MEDIA_FILE_PATH = input("Enter the path of the video you wish to upload:\n") 34 | 35 | SCOPES = ["https://www.googleapis.com/auth/youtube.upload"] 36 | API_SERVICE_NAME = "youtube" 37 | API_VERSION = "v3" 38 | 39 | VALID_PRIVACY_STATUSES = ("public", "private", "unlisted") 40 | 41 | 42 | def initialize_upload(youtube: build, options) -> None: 43 | """initialize the upload request""" 44 | tags = None 45 | if options.keywords: 46 | tags = options.keywords.split(",") 47 | 48 | body = dict( 49 | snippet=dict( 50 | title=options.title, 51 | description=options.description, 52 | tags=tags, 53 | categoryId=options.category, 54 | ), 55 | status=dict(privacyStatus=options.privacyStatus), 56 | ) 57 | 58 | insert_request = youtube.videos().insert( 59 | part=",".join(list(body.keys())), 60 | body=body, 61 | media_body=MediaFileUpload(MEDIA_FILE_PATH, chunksize=-1, resumable=True), 62 | ) 63 | 64 | resumable_upload(insert_request) 65 | 66 | 67 | def resumable_upload(request): 68 | """A Resumable error to retry the upload""" 69 | response = None 70 | error = None 71 | retry = 0 72 | while response is None: 73 | try: 74 | print("Uploading file...") 75 | _, response = request.next_chunk() 76 | if response is not None: 77 | if "id" in response: 78 | print(f'Video id {response["id"]} was successfully uploaded.') 79 | else: 80 | exit(f"The upload failed with an unexpected response: {response}") 81 | except HttpError as e: 82 | if e.resp.status in RETRIABLE_STATUS_CODES: 83 | error = "A retriable HTTP error {e.resp.status} occurred:\n{e.content}" 84 | else: 85 | raise 86 | except RETRIABLE_EXCEPTIONS as e: 87 | error = f"A retriable error occurred: {e}" 88 | 89 | if error is not None: 90 | print(error) 91 | retry += 1 92 | if retry > MAX_RETRIES: 93 | exit("No longer attempting to retry.") 94 | 95 | max_sleep = 2**retry 96 | sleep_seconds = random.random() * max_sleep 97 | print(f"Sleeping {sleep_seconds} seconds and then retrying...") 98 | time.sleep(sleep_seconds) 99 | 100 | 101 | class UplooadController: 102 | """Upload Controller""" 103 | 104 | 105 | RETRIABLE_STATUS_CODES = [500, 502, 503, 504] 106 | RETRIABLE_EXCEPTIONS = ( 107 | httplib2.HttpLib2Error, 108 | IOError, 109 | http.client.NotConnected, 110 | http.client.IncompleteRead, 111 | http.client.ImproperConnectionState, 112 | http.client.CannotSendRequest, 113 | http.client.CannotSendHeader, 114 | http.client.ResponseNotReady, 115 | http.client.BadStatusLine 116 | ) 117 | 118 | def __init__(self, *args, **kwargs): 119 | pass 120 | -------------------------------------------------------------------------------- /src/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import argparse 4 | import http.client 5 | import httplib2 6 | import random 7 | import time 8 | 9 | 10 | from googleapiclient.discovery import build 11 | from googleapiclient.errors import HttpError 12 | from googleapiclient.http import MediaFileUpload 13 | from google_auth_oauthlib.flow import InstalledAppFlow 14 | 15 | 16 | # import utils 17 | from src.utils import auth 18 | 19 | 20 | httplib2.RETRIES = 1 21 | 22 | MAX_RETRIES = 10 23 | 24 | RETRIABLE_EXCEPTIONS = ( 25 | httplib2.HttpLib2Error, 26 | IOError, 27 | http.client.NotConnected, 28 | http.client.IncompleteRead, 29 | http.client.ImproperConnectionState, 30 | http.client.CannotSendRequest, 31 | http.client.CannotSendHeader, 32 | http.client.ResponseNotReady, 33 | http.client.BadStatusLine, 34 | ) 35 | 36 | 37 | RETRIABLE_STATUS_CODES = [500, 502, 503, 504] 38 | 39 | 40 | CLIENT_SECRETS_FILE = input("Enter your client credential secret file path:\n") 41 | MEDIA_FILE_PATH = input("Enter the path of the video you wish to upload:\n") 42 | 43 | SCOPES = ["https://www.googleapis.com/auth/youtube.upload"] 44 | API_SERVICE_NAME = "youtube" 45 | API_VERSION = "v3" 46 | 47 | VALID_PRIVACY_STATUSES = ("public", "private", "unlisted") 48 | 49 | 50 | # Authorize the request and store authorization credentials. 51 | def get_authenticated_service(): 52 | flow = InstalledAppFlow.from_client_secrets_file(CLIENT_SECRETS_FILE, SCOPES) 53 | credentials = flow.run_console() 54 | return build(API_SERVICE_NAME, API_VERSION, credentials=credentials) 55 | 56 | 57 | def initialize_upload(youtube, options): 58 | tags = None 59 | if options.keywords: 60 | tags = options.keywords.split(",") 61 | 62 | body = dict( 63 | snippet=dict( 64 | title=options.title, 65 | description=options.description, 66 | tags=tags, 67 | categoryId=options.category, 68 | ), 69 | status=dict(privacyStatus=options.privacyStatus), 70 | ) 71 | 72 | insert_request = youtube.videos().insert( 73 | part=",".join(list(body.keys())), 74 | body=body, 75 | media_body=MediaFileUpload(MEDIA_FILE_PATH, chunksize=-1, resumable=True), 76 | ) 77 | 78 | resumable_upload(insert_request) 79 | 80 | 81 | def resumable_upload(request): 82 | response = None 83 | error = None 84 | retry = 0 85 | while response is None: 86 | try: 87 | print("Uploading file...") 88 | status, response = request.next_chunk() 89 | if response is not None: 90 | if "id" in response: 91 | print(f'Video id {response["id"]} was successfully uploaded.') 92 | else: 93 | exit(f"The upload failed with an unexpected response: {response}") 94 | except HttpError as e: 95 | if e.resp.status in RETRIABLE_STATUS_CODES: 96 | error = "A retriable HTTP error {e.resp.status} occurred:\n{e.content}" 97 | else: 98 | raise 99 | except RETRIABLE_EXCEPTIONS as e: 100 | error = f"A retriable error occurred: {e}" 101 | 102 | if error is not None: 103 | print(error) 104 | retry += 1 105 | if retry > MAX_RETRIES: 106 | exit("No longer attempting to retry.") 107 | 108 | max_sleep = 2**retry 109 | sleep_seconds = random.random() * max_sleep 110 | print(f"Sleeping {sleep_seconds} seconds and then retrying...") 111 | time.sleep(sleep_seconds) 112 | 113 | 114 | if __name__ == "__main__": 115 | parser = argparse.ArgumentParser() 116 | parser.add_argument("--file", required=False, help="Video file to upload") 117 | parser.add_argument("--title", help="Video title", default="Video Title") 118 | parser.add_argument( 119 | "--description", help="Video description", default="Test Description" 120 | ) 121 | parser.add_argument( 122 | "--category", 123 | default="22", 124 | help="Numeric video category. " 125 | + "See https://developers.google.com/youtube/v3/docs/videoCategories/list", 126 | ) 127 | parser.add_argument( 128 | "--keywords", help="Video keywords, comma separated", default="" 129 | ) 130 | parser.add_argument( 131 | "--privacyStatus", 132 | choices=VALID_PRIVACY_STATUSES, 133 | default="unlisted", 134 | help="Video privacy status.", 135 | ) 136 | args = parser.parse_args() 137 | 138 | youtube = get_authenticated_service() 139 | 140 | try: 141 | initialize_upload(youtube, args) 142 | except HttpError as e: 143 | print(f"An HTTP error {e.resp.status} occurred:\n{e.content}") 144 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. 2 | 3 | [[package]] 4 | name = "beautifulsoup4" 5 | version = "4.12.2" 6 | description = "Screen-scraping library" 7 | optional = false 8 | python-versions = ">=3.6.0" 9 | files = [ 10 | {file = "beautifulsoup4-4.12.2-py3-none-any.whl", hash = "sha256:bd2520ca0d9d7d12694a53d44ac482d181b4ec1888909b035a3dbf40d0f57d4a"}, 11 | {file = "beautifulsoup4-4.12.2.tar.gz", hash = "sha256:492bbc69dca35d12daac71c4db1bfff0c876c00ef4a2ffacce226d4638eb72da"}, 12 | ] 13 | 14 | [package.dependencies] 15 | soupsieve = ">1.2" 16 | 17 | [package.extras] 18 | html5lib = ["html5lib"] 19 | lxml = ["lxml"] 20 | 21 | [[package]] 22 | name = "cachetools" 23 | version = "5.3.2" 24 | description = "Extensible memoizing collections and decorators" 25 | optional = false 26 | python-versions = ">=3.7" 27 | files = [ 28 | {file = "cachetools-5.3.2-py3-none-any.whl", hash = "sha256:861f35a13a451f94e301ce2bec7cac63e881232ccce7ed67fab9b5df4d3beaa1"}, 29 | {file = "cachetools-5.3.2.tar.gz", hash = "sha256:086ee420196f7b2ab9ca2db2520aca326318b68fe5ba8bc4d49cca91add450f2"}, 30 | ] 31 | 32 | [[package]] 33 | name = "certifi" 34 | version = "2023.11.17" 35 | description = "Python package for providing Mozilla's CA Bundle." 36 | optional = false 37 | python-versions = ">=3.6" 38 | files = [ 39 | {file = "certifi-2023.11.17-py3-none-any.whl", hash = "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474"}, 40 | {file = "certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1"}, 41 | ] 42 | 43 | [[package]] 44 | name = "charset-normalizer" 45 | version = "3.3.2" 46 | description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." 47 | optional = false 48 | python-versions = ">=3.7.0" 49 | files = [ 50 | {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, 51 | {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, 52 | {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, 53 | {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, 54 | {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, 55 | {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, 56 | {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, 57 | {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, 58 | {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, 59 | {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, 60 | {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, 61 | {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, 62 | {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, 63 | {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, 64 | {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, 65 | {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, 66 | {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, 67 | {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, 68 | {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, 69 | {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, 70 | {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, 71 | {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, 72 | {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, 73 | {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, 74 | {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, 75 | {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, 76 | {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, 77 | {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, 78 | {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, 79 | {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, 80 | {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, 81 | {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, 82 | {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, 83 | {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, 84 | {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, 85 | {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, 86 | {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, 87 | {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, 88 | {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, 89 | {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, 90 | {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, 91 | {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, 92 | {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, 93 | {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, 94 | {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, 95 | {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, 96 | {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, 97 | {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, 98 | {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, 99 | {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, 100 | {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, 101 | {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, 102 | {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, 103 | {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, 104 | {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, 105 | {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, 106 | {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, 107 | {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, 108 | {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, 109 | {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, 110 | {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, 111 | {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, 112 | {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, 113 | {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, 114 | {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, 115 | {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, 116 | {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, 117 | {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, 118 | {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, 119 | {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, 120 | {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, 121 | {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, 122 | {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, 123 | {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, 124 | {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, 125 | {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, 126 | {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, 127 | {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, 128 | {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, 129 | {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, 130 | {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, 131 | {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, 132 | {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, 133 | {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, 134 | {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, 135 | {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, 136 | {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, 137 | {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, 138 | {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, 139 | {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, 140 | ] 141 | 142 | [[package]] 143 | name = "google" 144 | version = "3.0.0" 145 | description = "Python bindings to the Google search engine." 146 | optional = false 147 | python-versions = "*" 148 | files = [ 149 | {file = "google-3.0.0-py2.py3-none-any.whl", hash = "sha256:889cf695f84e4ae2c55fbc0cfdaf4c1e729417fa52ab1db0485202ba173e4935"}, 150 | {file = "google-3.0.0.tar.gz", hash = "sha256:143530122ee5130509ad5e989f0512f7cb218b2d4eddbafbad40fd10e8d8ccbe"}, 151 | ] 152 | 153 | [package.dependencies] 154 | beautifulsoup4 = "*" 155 | 156 | [[package]] 157 | name = "google-api-core" 158 | version = "2.15.0" 159 | description = "Google API client core library" 160 | optional = false 161 | python-versions = ">=3.7" 162 | files = [ 163 | {file = "google-api-core-2.15.0.tar.gz", hash = "sha256:abc978a72658f14a2df1e5e12532effe40f94f868f6e23d95133bd6abcca35ca"}, 164 | {file = "google_api_core-2.15.0-py3-none-any.whl", hash = "sha256:2aa56d2be495551e66bbff7f729b790546f87d5c90e74781aa77233bcb395a8a"}, 165 | ] 166 | 167 | [package.dependencies] 168 | google-auth = ">=2.14.1,<3.0.dev0" 169 | googleapis-common-protos = ">=1.56.2,<2.0.dev0" 170 | protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" 171 | requests = ">=2.18.0,<3.0.0.dev0" 172 | 173 | [package.extras] 174 | grpc = ["grpcio (>=1.33.2,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "grpcio-status (>=1.33.2,<2.0.dev0)", "grpcio-status (>=1.49.1,<2.0.dev0)"] 175 | grpcgcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] 176 | grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] 177 | 178 | [[package]] 179 | name = "google-api-python-client" 180 | version = "2.112.0" 181 | description = "Google API Client Library for Python" 182 | optional = false 183 | python-versions = ">=3.7" 184 | files = [ 185 | {file = "google-api-python-client-2.112.0.tar.gz", hash = "sha256:c3bcb5fd70d57f4c94b30c0dbeade53c216febfbf1d771eeb1a2fa74bd0d6756"}, 186 | {file = "google_api_python_client-2.112.0-py2.py3-none-any.whl", hash = "sha256:f5e45d9812376deb7e04cda8d8ca5233aa608038bdbf1253ad8f7edcb7f6d595"}, 187 | ] 188 | 189 | [package.dependencies] 190 | google-api-core = ">=1.31.5,<2.0.dev0 || >2.3.0,<3.0.0.dev0" 191 | google-auth = ">=1.19.0,<3.0.0.dev0" 192 | google-auth-httplib2 = ">=0.1.0" 193 | httplib2 = ">=0.15.0,<1.dev0" 194 | uritemplate = ">=3.0.1,<5" 195 | 196 | [[package]] 197 | name = "google-auth" 198 | version = "2.26.1" 199 | description = "Google Authentication Library" 200 | optional = false 201 | python-versions = ">=3.7" 202 | files = [ 203 | {file = "google-auth-2.26.1.tar.gz", hash = "sha256:54385acca5c0fbdda510cd8585ba6f3fcb06eeecf8a6ecca39d3ee148b092590"}, 204 | {file = "google_auth-2.26.1-py2.py3-none-any.whl", hash = "sha256:2c8b55e3e564f298122a02ab7b97458ccfcc5617840beb5d0ac757ada92c9780"}, 205 | ] 206 | 207 | [package.dependencies] 208 | cachetools = ">=2.0.0,<6.0" 209 | pyasn1-modules = ">=0.2.1" 210 | rsa = ">=3.1.4,<5" 211 | 212 | [package.extras] 213 | aiohttp = ["aiohttp (>=3.6.2,<4.0.0.dev0)", "requests (>=2.20.0,<3.0.0.dev0)"] 214 | enterprise-cert = ["cryptography (==36.0.2)", "pyopenssl (==22.0.0)"] 215 | pyopenssl = ["cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"] 216 | reauth = ["pyu2f (>=0.1.5)"] 217 | requests = ["requests (>=2.20.0,<3.0.0.dev0)"] 218 | 219 | [[package]] 220 | name = "google-auth-httplib2" 221 | version = "0.2.0" 222 | description = "Google Authentication Library: httplib2 transport" 223 | optional = false 224 | python-versions = "*" 225 | files = [ 226 | {file = "google-auth-httplib2-0.2.0.tar.gz", hash = "sha256:38aa7badf48f974f1eb9861794e9c0cb2a0511a4ec0679b1f886d108f5640e05"}, 227 | {file = "google_auth_httplib2-0.2.0-py2.py3-none-any.whl", hash = "sha256:b65a0a2123300dd71281a7bf6e64d65a0759287df52729bdd1ae2e47dc311a3d"}, 228 | ] 229 | 230 | [package.dependencies] 231 | google-auth = "*" 232 | httplib2 = ">=0.19.0" 233 | 234 | [[package]] 235 | name = "google-auth-oauthlib" 236 | version = "1.2.0" 237 | description = "Google Authentication Library" 238 | optional = false 239 | python-versions = ">=3.6" 240 | files = [ 241 | {file = "google-auth-oauthlib-1.2.0.tar.gz", hash = "sha256:292d2d3783349f2b0734a0a0207b1e1e322ac193c2c09d8f7c613fb7cc501ea8"}, 242 | {file = "google_auth_oauthlib-1.2.0-py2.py3-none-any.whl", hash = "sha256:297c1ce4cb13a99b5834c74a1fe03252e1e499716718b190f56bcb9c4abc4faf"}, 243 | ] 244 | 245 | [package.dependencies] 246 | google-auth = ">=2.15.0" 247 | requests-oauthlib = ">=0.7.0" 248 | 249 | [package.extras] 250 | tool = ["click (>=6.0.0)"] 251 | 252 | [[package]] 253 | name = "googleapis-common-protos" 254 | version = "1.62.0" 255 | description = "Common protobufs used in Google APIs" 256 | optional = false 257 | python-versions = ">=3.7" 258 | files = [ 259 | {file = "googleapis-common-protos-1.62.0.tar.gz", hash = "sha256:83f0ece9f94e5672cced82f592d2a5edf527a96ed1794f0bab36d5735c996277"}, 260 | {file = "googleapis_common_protos-1.62.0-py2.py3-none-any.whl", hash = "sha256:4750113612205514f9f6aa4cb00d523a94f3e8c06c5ad2fee466387dc4875f07"}, 261 | ] 262 | 263 | [package.dependencies] 264 | protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" 265 | 266 | [package.extras] 267 | grpc = ["grpcio (>=1.44.0,<2.0.0.dev0)"] 268 | 269 | [[package]] 270 | name = "httplib2" 271 | version = "0.22.0" 272 | description = "A comprehensive HTTP client library." 273 | optional = false 274 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 275 | files = [ 276 | {file = "httplib2-0.22.0-py3-none-any.whl", hash = "sha256:14ae0a53c1ba8f3d37e9e27cf37eabb0fb9980f435ba405d546948b009dd64dc"}, 277 | {file = "httplib2-0.22.0.tar.gz", hash = "sha256:d7a10bc5ef5ab08322488bde8c726eeee5c8618723fdb399597ec58f3d82df81"}, 278 | ] 279 | 280 | [package.dependencies] 281 | pyparsing = {version = ">=2.4.2,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.0.2 || >3.0.2,<3.0.3 || >3.0.3,<4", markers = "python_version > \"3.0\""} 282 | 283 | [[package]] 284 | name = "idna" 285 | version = "3.6" 286 | description = "Internationalized Domain Names in Applications (IDNA)" 287 | optional = false 288 | python-versions = ">=3.5" 289 | files = [ 290 | {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, 291 | {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, 292 | ] 293 | 294 | [[package]] 295 | name = "oauthlib" 296 | version = "3.2.2" 297 | description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" 298 | optional = false 299 | python-versions = ">=3.6" 300 | files = [ 301 | {file = "oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca"}, 302 | {file = "oauthlib-3.2.2.tar.gz", hash = "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918"}, 303 | ] 304 | 305 | [package.extras] 306 | rsa = ["cryptography (>=3.0.0)"] 307 | signals = ["blinker (>=1.4.0)"] 308 | signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"] 309 | 310 | [[package]] 311 | name = "protobuf" 312 | version = "4.25.1" 313 | description = "" 314 | optional = false 315 | python-versions = ">=3.8" 316 | files = [ 317 | {file = "protobuf-4.25.1-cp310-abi3-win32.whl", hash = "sha256:193f50a6ab78a970c9b4f148e7c750cfde64f59815e86f686c22e26b4fe01ce7"}, 318 | {file = "protobuf-4.25.1-cp310-abi3-win_amd64.whl", hash = "sha256:3497c1af9f2526962f09329fd61a36566305e6c72da2590ae0d7d1322818843b"}, 319 | {file = "protobuf-4.25.1-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:0bf384e75b92c42830c0a679b0cd4d6e2b36ae0cf3dbb1e1dfdda48a244f4bcd"}, 320 | {file = "protobuf-4.25.1-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:0f881b589ff449bf0b931a711926e9ddaad3b35089cc039ce1af50b21a4ae8cb"}, 321 | {file = "protobuf-4.25.1-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:ca37bf6a6d0046272c152eea90d2e4ef34593aaa32e8873fc14c16440f22d4b7"}, 322 | {file = "protobuf-4.25.1-cp38-cp38-win32.whl", hash = "sha256:abc0525ae2689a8000837729eef7883b9391cd6aa7950249dcf5a4ede230d5dd"}, 323 | {file = "protobuf-4.25.1-cp38-cp38-win_amd64.whl", hash = "sha256:1484f9e692091450e7edf418c939e15bfc8fc68856e36ce399aed6889dae8bb0"}, 324 | {file = "protobuf-4.25.1-cp39-cp39-win32.whl", hash = "sha256:8bdbeaddaac52d15c6dce38c71b03038ef7772b977847eb6d374fc86636fa510"}, 325 | {file = "protobuf-4.25.1-cp39-cp39-win_amd64.whl", hash = "sha256:becc576b7e6b553d22cbdf418686ee4daa443d7217999125c045ad56322dda10"}, 326 | {file = "protobuf-4.25.1-py3-none-any.whl", hash = "sha256:a19731d5e83ae4737bb2a089605e636077ac001d18781b3cf489b9546c7c80d6"}, 327 | {file = "protobuf-4.25.1.tar.gz", hash = "sha256:57d65074b4f5baa4ab5da1605c02be90ac20c8b40fb137d6a8df9f416b0d0ce2"}, 328 | ] 329 | 330 | [[package]] 331 | name = "py-spy" 332 | version = "0.3.14" 333 | description = "Sampling profiler for Python programs" 334 | optional = false 335 | python-versions = "*" 336 | files = [ 337 | {file = "py_spy-0.3.14-py2.py3-none-macosx_10_7_x86_64.whl", hash = "sha256:5b342cc5feb8d160d57a7ff308de153f6be68dcf506ad02b4d67065f2bae7f45"}, 338 | {file = "py_spy-0.3.14-py2.py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:fe7efe6c91f723442259d428bf1f9ddb9c1679828866b353d539345ca40d9dd2"}, 339 | {file = "py_spy-0.3.14-py2.py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:590905447241d789d9de36cff9f52067b6f18d8b5e9fb399242041568d414461"}, 340 | {file = "py_spy-0.3.14-py2.py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fd6211fe7f587b3532ba9d300784326d9a6f2b890af7bf6fff21a029ebbc812b"}, 341 | {file = "py_spy-0.3.14-py2.py3-none-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3e8e48032e71c94c3dd51694c39e762e4bbfec250df5bf514adcdd64e79371e0"}, 342 | {file = "py_spy-0.3.14-py2.py3-none-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:f59b0b52e56ba9566305236375e6fc68888261d0d36b5addbe3cf85affbefc0e"}, 343 | {file = "py_spy-0.3.14-py2.py3-none-win_amd64.whl", hash = "sha256:8f5b311d09f3a8e33dbd0d44fc6e37b715e8e0c7efefafcda8bfd63b31ab5a31"}, 344 | ] 345 | 346 | [[package]] 347 | name = "pyasn1" 348 | version = "0.5.1" 349 | description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" 350 | optional = false 351 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" 352 | files = [ 353 | {file = "pyasn1-0.5.1-py2.py3-none-any.whl", hash = "sha256:4439847c58d40b1d0a573d07e3856e95333f1976294494c325775aeca506eb58"}, 354 | {file = "pyasn1-0.5.1.tar.gz", hash = "sha256:6d391a96e59b23130a5cfa74d6fd7f388dbbe26cc8f1edf39fdddf08d9d6676c"}, 355 | ] 356 | 357 | [[package]] 358 | name = "pyasn1-modules" 359 | version = "0.3.0" 360 | description = "A collection of ASN.1-based protocols modules" 361 | optional = false 362 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" 363 | files = [ 364 | {file = "pyasn1_modules-0.3.0-py2.py3-none-any.whl", hash = "sha256:d3ccd6ed470d9ffbc716be08bd90efbd44d0734bc9303818f7336070984a162d"}, 365 | {file = "pyasn1_modules-0.3.0.tar.gz", hash = "sha256:5bd01446b736eb9d31512a30d46c1ac3395d676c6f3cafa4c03eb54b9925631c"}, 366 | ] 367 | 368 | [package.dependencies] 369 | pyasn1 = ">=0.4.6,<0.6.0" 370 | 371 | [[package]] 372 | name = "pyparsing" 373 | version = "3.1.1" 374 | description = "pyparsing module - Classes and methods to define and execute parsing grammars" 375 | optional = false 376 | python-versions = ">=3.6.8" 377 | files = [ 378 | {file = "pyparsing-3.1.1-py3-none-any.whl", hash = "sha256:32c7c0b711493c72ff18a981d24f28aaf9c1fb7ed5e9667c9e84e3db623bdbfb"}, 379 | {file = "pyparsing-3.1.1.tar.gz", hash = "sha256:ede28a1a32462f5a9705e07aea48001a08f7cf81a021585011deba701581a0db"}, 380 | ] 381 | 382 | [package.extras] 383 | diagrams = ["jinja2", "railroad-diagrams"] 384 | 385 | [[package]] 386 | name = "requests" 387 | version = "2.31.0" 388 | description = "Python HTTP for Humans." 389 | optional = false 390 | python-versions = ">=3.7" 391 | files = [ 392 | {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, 393 | {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, 394 | ] 395 | 396 | [package.dependencies] 397 | certifi = ">=2017.4.17" 398 | charset-normalizer = ">=2,<4" 399 | idna = ">=2.5,<4" 400 | urllib3 = ">=1.21.1,<3" 401 | 402 | [package.extras] 403 | socks = ["PySocks (>=1.5.6,!=1.5.7)"] 404 | use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] 405 | 406 | [[package]] 407 | name = "requests-oauthlib" 408 | version = "1.3.1" 409 | description = "OAuthlib authentication support for Requests." 410 | optional = false 411 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 412 | files = [ 413 | {file = "requests-oauthlib-1.3.1.tar.gz", hash = "sha256:75beac4a47881eeb94d5ea5d6ad31ef88856affe2332b9aafb52c6452ccf0d7a"}, 414 | {file = "requests_oauthlib-1.3.1-py2.py3-none-any.whl", hash = "sha256:2577c501a2fb8d05a304c09d090d6e47c306fef15809d102b327cf8364bddab5"}, 415 | ] 416 | 417 | [package.dependencies] 418 | oauthlib = ">=3.0.0" 419 | requests = ">=2.0.0" 420 | 421 | [package.extras] 422 | rsa = ["oauthlib[signedtoken] (>=3.0.0)"] 423 | 424 | [[package]] 425 | name = "rsa" 426 | version = "4.9" 427 | description = "Pure-Python RSA implementation" 428 | optional = false 429 | python-versions = ">=3.6,<4" 430 | files = [ 431 | {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"}, 432 | {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"}, 433 | ] 434 | 435 | [package.dependencies] 436 | pyasn1 = ">=0.1.3" 437 | 438 | [[package]] 439 | name = "soupsieve" 440 | version = "2.5" 441 | description = "A modern CSS selector implementation for Beautiful Soup." 442 | optional = false 443 | python-versions = ">=3.8" 444 | files = [ 445 | {file = "soupsieve-2.5-py3-none-any.whl", hash = "sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7"}, 446 | {file = "soupsieve-2.5.tar.gz", hash = "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690"}, 447 | ] 448 | 449 | [[package]] 450 | name = "uritemplate" 451 | version = "4.1.1" 452 | description = "Implementation of RFC 6570 URI Templates" 453 | optional = false 454 | python-versions = ">=3.6" 455 | files = [ 456 | {file = "uritemplate-4.1.1-py2.py3-none-any.whl", hash = "sha256:830c08b8d99bdd312ea4ead05994a38e8936266f84b9a7878232db50b044e02e"}, 457 | {file = "uritemplate-4.1.1.tar.gz", hash = "sha256:4346edfc5c3b79f694bccd6d6099a322bbeb628dbf2cd86eea55a456ce5124f0"}, 458 | ] 459 | 460 | [[package]] 461 | name = "urllib3" 462 | version = "2.1.0" 463 | description = "HTTP library with thread-safe connection pooling, file post, and more." 464 | optional = false 465 | python-versions = ">=3.8" 466 | files = [ 467 | {file = "urllib3-2.1.0-py3-none-any.whl", hash = "sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3"}, 468 | {file = "urllib3-2.1.0.tar.gz", hash = "sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54"}, 469 | ] 470 | 471 | [package.extras] 472 | brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] 473 | socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] 474 | zstd = ["zstandard (>=0.18.0)"] 475 | 476 | [metadata] 477 | lock-version = "2.0" 478 | python-versions = "^3.11" 479 | content-hash = "8d818814d0101eda7d74e199a7e136ff6a37227f1ccd8c37d201015f018030e0" 480 | --------------------------------------------------------------------------------