From f5c704e8f8cebe6e1537302a4a18220e8f125853 Mon Sep 17 00:00:00 2001 From: olivierapivideo Date: Wed, 30 Jun 2021 12:04:45 +0000 Subject: [PATCH] chore(python) add progress listener on upload methods --- apivideo/__init__.py | 2 +- apivideo/api/captions_api.py | 2 ++ apivideo/api/chapters_api.py | 2 ++ apivideo/api/live_streams_api.py | 2 ++ apivideo/api/player_themes_api.py | 2 ++ apivideo/api/raw_statistics_api.py | 2 ++ apivideo/api/upload_tokens_api.py | 2 ++ apivideo/api/videos_api.py | 30 +++++++++++++++++++++++------ apivideo/api/webhooks_api.py | 2 ++ apivideo/api_client.py | 2 +- apivideo/configuration.py | 2 +- apivideo/endpoint.py | 2 +- apivideo/model_utils.py | 4 ++++ setup.py | 2 +- test/test_integration_videos_api.py | 10 ++++++++-- 15 files changed, 55 insertions(+), 13 deletions(-) diff --git a/apivideo/__init__.py b/apivideo/__init__.py index 9e856f4..1eb7aa4 100644 --- a/apivideo/__init__.py +++ b/apivideo/__init__.py @@ -9,7 +9,7 @@ """ -__version__ = "0.0.11" +__version__ = "0.0.12" # import ApiVideoClient from apivideo.auth_api_client import AuthenticatedApiClient diff --git a/apivideo/api/captions_api.py b/apivideo/api/captions_api.py index f000cf7..df0fec2 100644 --- a/apivideo/api/captions_api.py +++ b/apivideo/api/captions_api.py @@ -9,6 +9,8 @@ import os # noqa: F401 import re # noqa: F401 import sys # noqa: F401 +from types import MethodType +from types import FunctionType from apivideo.api_client import ApiClient from apivideo.endpoint import EndPoint as _EndPoint diff --git a/apivideo/api/chapters_api.py b/apivideo/api/chapters_api.py index d33c2bd..d9beb0b 100644 --- a/apivideo/api/chapters_api.py +++ b/apivideo/api/chapters_api.py @@ -9,6 +9,8 @@ import os # noqa: F401 import re # noqa: F401 import sys # noqa: F401 +from types import MethodType +from types import FunctionType from apivideo.api_client import ApiClient from apivideo.endpoint import EndPoint as _EndPoint diff --git a/apivideo/api/live_streams_api.py b/apivideo/api/live_streams_api.py index a60d67e..115e103 100644 --- a/apivideo/api/live_streams_api.py +++ b/apivideo/api/live_streams_api.py @@ -9,6 +9,8 @@ import os # noqa: F401 import re # noqa: F401 import sys # noqa: F401 +from types import MethodType +from types import FunctionType from apivideo.api_client import ApiClient from apivideo.endpoint import EndPoint as _EndPoint diff --git a/apivideo/api/player_themes_api.py b/apivideo/api/player_themes_api.py index a1d7e6e..aa1e7aa 100644 --- a/apivideo/api/player_themes_api.py +++ b/apivideo/api/player_themes_api.py @@ -9,6 +9,8 @@ import os # noqa: F401 import re # noqa: F401 import sys # noqa: F401 +from types import MethodType +from types import FunctionType from apivideo.api_client import ApiClient from apivideo.endpoint import EndPoint as _EndPoint diff --git a/apivideo/api/raw_statistics_api.py b/apivideo/api/raw_statistics_api.py index af572c1..ddafbaa 100644 --- a/apivideo/api/raw_statistics_api.py +++ b/apivideo/api/raw_statistics_api.py @@ -9,6 +9,8 @@ import os # noqa: F401 import re # noqa: F401 import sys # noqa: F401 +from types import MethodType +from types import FunctionType from apivideo.api_client import ApiClient from apivideo.endpoint import EndPoint as _EndPoint diff --git a/apivideo/api/upload_tokens_api.py b/apivideo/api/upload_tokens_api.py index 375bd8b..edfdb3f 100644 --- a/apivideo/api/upload_tokens_api.py +++ b/apivideo/api/upload_tokens_api.py @@ -9,6 +9,8 @@ import os # noqa: F401 import re # noqa: F401 import sys # noqa: F401 +from types import MethodType +from types import FunctionType from apivideo.api_client import ApiClient from apivideo.endpoint import EndPoint as _EndPoint diff --git a/apivideo/api/videos_api.py b/apivideo/api/videos_api.py index 5bf3ff7..774b715 100644 --- a/apivideo/api/videos_api.py +++ b/apivideo/api/videos_api.py @@ -9,6 +9,8 @@ import os # noqa: F401 import re # noqa: F401 import sys # noqa: F401 +from types import MethodType +from types import FunctionType from apivideo.api_client import ApiClient from apivideo.endpoint import EndPoint as _EndPoint @@ -894,6 +896,9 @@ def upload_with_upload_token( number provided, it will be total request timeout. It can also be a pair (tuple) of (connection, read) timeouts. Default is None. + _progress_listener (method): method called each time a chunk is uploaded. Takes 2 parameters: + the first one is the number of bytes uploaded, the second one is the total number of bytes. + Default is None. async_req (bool): execute request asynchronously Returns: @@ -925,7 +930,8 @@ def upload_with_upload_token( 'async_req', '_preload_content', '_request_timeout', - '_return_http_data_only' + '_return_http_data_only', + '_progress_listener', ], 'required': [ 'token', @@ -951,7 +957,8 @@ def upload_with_upload_token( 'async_req': (bool,), '_preload_content': (bool,), '_request_timeout': (none_type, int, (int,), [int]), - '_return_http_data_only': (bool,) + '_return_http_data_only': (bool,), + '_progress_listener': (none_type, MethodType, FunctionType), } attribute_map = { 'token': 'token', @@ -988,7 +995,10 @@ def upload_with_upload_token( self._validate_inputs(kwargs, params_map, allowed_values, validations, openapi_types) params = self._gather_params(kwargs, location_map, attribute_map, openapi_types, collection_format_map) res = None - for content_range, chunk, isLast in self._chunk_file(params['file']): + progress_listener = kwargs.get('_progress_listener', None) + for content_range, chunk, isLast, offset, file_size in self._chunk_file(params['file']): + if progress_listener is not None: + progress_listener(offset, file_size) res = self.api_client.call_api( "/upload", "POST", @@ -1162,6 +1172,9 @@ def upload( number provided, it will be total request timeout. It can also be a pair (tuple) of (connection, read) timeouts. Default is None. + _progress_listener (method): method called each time a chunk is uploaded. Takes 2 parameters: + the first one is the number of bytes uploaded, the second one is the total number of bytes. + Default is None. async_req (bool): execute request asynchronously Returns: @@ -1193,7 +1206,8 @@ def upload( 'async_req', '_preload_content', '_request_timeout', - '_return_http_data_only' + '_return_http_data_only', + '_progress_listener', ], 'required': [ 'video_id', @@ -1219,7 +1233,8 @@ def upload( 'async_req': (bool,), '_preload_content': (bool,), '_request_timeout': (none_type, int, (int,), [int]), - '_return_http_data_only': (bool,) + '_return_http_data_only': (bool,), + '_progress_listener': (none_type, MethodType, FunctionType), } attribute_map = { 'video_id': 'videoId', @@ -1256,7 +1271,10 @@ def upload( self._validate_inputs(kwargs, params_map, allowed_values, validations, openapi_types) params = self._gather_params(kwargs, location_map, attribute_map, openapi_types, collection_format_map) res = None - for content_range, chunk, isLast in self._chunk_file(params['file']): + progress_listener = kwargs.get('_progress_listener', None) + for content_range, chunk, isLast, offset, file_size in self._chunk_file(params['file']): + if progress_listener is not None: + progress_listener(offset, file_size) res = self.api_client.call_api( "/videos/{videoId}/source", "POST", diff --git a/apivideo/api/webhooks_api.py b/apivideo/api/webhooks_api.py index 0d97a96..0d7c46f 100644 --- a/apivideo/api/webhooks_api.py +++ b/apivideo/api/webhooks_api.py @@ -9,6 +9,8 @@ import os # noqa: F401 import re # noqa: F401 import sys # noqa: F401 +from types import MethodType +from types import FunctionType from apivideo.api_client import ApiClient from apivideo.endpoint import EndPoint as _EndPoint diff --git a/apivideo/api_client.py b/apivideo/api_client.py index 24f32cd..9dce4dd 100644 --- a/apivideo/api_client.py +++ b/apivideo/api_client.py @@ -75,7 +75,7 @@ def __init__(self, configuration=None, header_name=None, header_value=None, self.default_headers[header_name] = header_value self.cookie = cookie # Set default User-Agent. - self.user_agent = '"api.video client (python; v:0.0.11; )"' + self.user_agent = '"api.video client (python; v:0.0.12; )"' def __enter__(self): return self diff --git a/apivideo/configuration.py b/apivideo/configuration.py index 3bca66b..a24ab60 100644 --- a/apivideo/configuration.py +++ b/apivideo/configuration.py @@ -384,7 +384,7 @@ def to_debug_report(self): "OS: {env}\n"\ "Python Version: {pyversion}\n"\ "Version of the API: 1\n"\ - "SDK Package Version: 0.0.11".\ + "SDK Package Version: 0.0.12".\ format(env=sys.platform, pyversion=sys.version) def get_host_settings(self): diff --git a/apivideo/endpoint.py b/apivideo/endpoint.py index 943b711..0aefe87 100644 --- a/apivideo/endpoint.py +++ b/apivideo/endpoint.py @@ -107,5 +107,5 @@ def _chunk_file(self, file_info): for chunk in iter(partial(file.read, self.api_client.configuration.chunk_size), b''): offset = index + len(chunk) - yield 'bytes {}-{}/{}'.format(index, offset - 1, file_size), {file_name: [ChunkIO(chunk, file.name)]}, offset == file_size + yield 'bytes {}-{}/{}'.format(index, offset - 1, file_size), {file_name: [ChunkIO(chunk, file.name)]}, offset == file_size, offset, file_size index = offset diff --git a/apivideo/model_utils.py b/apivideo/model_utils.py index 0ae5e14..d1eb724 100644 --- a/apivideo/model_utils.py +++ b/apivideo/model_utils.py @@ -15,6 +15,8 @@ import pprint import re import tempfile +from types import MethodType +from types import FunctionType from dateutil.parser import parse @@ -579,6 +581,8 @@ def __eq__(self, other): date: 10, str: 11, file_type: 12, # 'file_type' is an alias for the built-in 'file' or 'io.IOBase' type. + MethodType: 13, + FunctionType: 14, } # these are used to limit what type conversions we try to do diff --git a/setup.py b/setup.py index aa09ca4..fa34dd1 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ from setuptools import setup, find_packages # noqa: H301 NAME = "api.video" -VERSION = "0.0.11" +VERSION = "0.0.12" # To install the library, run the following # # python setup.py install diff --git a/test/test_integration_videos_api.py b/test/test_integration_videos_api.py index 0762491..3882e1a 100644 --- a/test/test_integration_videos_api.py +++ b/test/test_integration_videos_api.py @@ -50,15 +50,21 @@ def test_metadata(self): self.api.delete(video_with_metadata.video_id) self.api.delete(video_without_metadata.video_id) + + @unittest.skipIf(os.getenv("API_KEY") is None, "No API key") def test_upload(self): + + def listener(uploaded, total): + print('Progress: {}/{}'.format(uploaded, total)) + video = self.api.create(video_creation_payload=VideoCreationPayload( title='upload', public=True, tags=["bunny"])) - file = open("sample.mp4", "rb") - self.api.upload(video.video_id, file, _request_timeout=20) + file = open("sample-mp4-file.mp4", "rb") + self.api.upload(video.video_id, file, _request_timeout=20, _progress_listener=listener) file.close() self.api.delete(video.video_id)