mirror of
https://github.com/LonamiWebs/Telethon.git
synced 2024-11-22 09:26:37 +03:00
Improvements to file uploading and progress added
Now you can let the file part size be determined by the total file size, rather than manually specifying one. Also, a callback function parameter has been added (which allows to print the progress)
This commit is contained in:
parent
15994d0b78
commit
27ec7292d8
|
@ -27,6 +27,16 @@ def print_title(title):
|
||||||
print('└{}┘'.format('─' * available_cols))
|
print('└{}┘'.format('─' * available_cols))
|
||||||
|
|
||||||
|
|
||||||
|
def bytes_to_string(byte_count):
|
||||||
|
"""Converts a byte count to a string (in KB, MB...)"""
|
||||||
|
suffix_index = 0
|
||||||
|
while byte_count >= 1024:
|
||||||
|
byte_count /= 1024
|
||||||
|
suffix_index += 1
|
||||||
|
|
||||||
|
return '{:.2f}{}'.format(byte_count, [' bytes', 'KB', 'MB', 'GB', 'TB'][suffix_index])
|
||||||
|
|
||||||
|
|
||||||
class InteractiveTelegramClient(TelegramClient):
|
class InteractiveTelegramClient(TelegramClient):
|
||||||
def __init__(self, session_user_id, user_phone, layer, api_id, api_hash):
|
def __init__(self, session_user_id, user_phone, layer, api_id, api_hash):
|
||||||
print_title('Initialization')
|
print_title('Initialization')
|
||||||
|
@ -160,7 +170,7 @@ class InteractiveTelegramClient(TelegramClient):
|
||||||
|
|
||||||
def send_photo(self, path, peer):
|
def send_photo(self, path, peer):
|
||||||
print('Uploading {}...'.format(path))
|
print('Uploading {}...'.format(path))
|
||||||
input_file = self.upload_file(path)
|
input_file = self.upload_file(path, progress_callback=self.upload_progress_callback)
|
||||||
|
|
||||||
# After we have the handle to the uploaded file, send it to our peer
|
# After we have the handle to the uploaded file, send it to our peer
|
||||||
self.send_photo_file(input_file, peer)
|
self.send_photo_file(input_file, peer)
|
||||||
|
@ -168,7 +178,7 @@ class InteractiveTelegramClient(TelegramClient):
|
||||||
|
|
||||||
def send_document(self, path, peer):
|
def send_document(self, path, peer):
|
||||||
print('Uploading {}...'.format(path))
|
print('Uploading {}...'.format(path))
|
||||||
input_file = self.upload_file(path)
|
input_file = self.upload_file(path, progress_callback=self.upload_progress_callback)
|
||||||
|
|
||||||
# After we have the handle to the uploaded file, send it to our peer
|
# After we have the handle to the uploaded file, send it to our peer
|
||||||
self.send_document_file(input_file, peer)
|
self.send_document_file(input_file, peer)
|
||||||
|
@ -185,12 +195,30 @@ class InteractiveTelegramClient(TelegramClient):
|
||||||
# Let the output be the message ID
|
# Let the output be the message ID
|
||||||
output = str('usermedia/{}'.format(msg_media_id))
|
output = str('usermedia/{}'.format(msg_media_id))
|
||||||
print('Downloading media with name {}...'.format(output))
|
print('Downloading media with name {}...'.format(output))
|
||||||
output = self.download_msg_media(msg.media, file_path=output)
|
output = self.download_msg_media(msg.media,
|
||||||
|
file_path=output,
|
||||||
|
progress_callback=self.download_progress_callback)
|
||||||
print('Media downloaded to {}!'.format(output))
|
print('Media downloaded to {}!'.format(output))
|
||||||
|
|
||||||
except ValueError:
|
except ValueError:
|
||||||
print('Invalid media ID given!')
|
print('Invalid media ID given!')
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def download_progress_callback(downloaded_bytes, total_bytes):
|
||||||
|
InteractiveTelegramClient.print_progress('Downloaded', downloaded_bytes, total_bytes)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def upload_progress_callback(uploaded_bytes, total_bytes):
|
||||||
|
InteractiveTelegramClient.print_progress('Uploaded', uploaded_bytes, total_bytes)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def print_progress(progress_type, downloaded_bytes, total_bytes):
|
||||||
|
print('{} {} out of {} ({:.2%})'.format(
|
||||||
|
progress_type,
|
||||||
|
bytes_to_string(downloaded_bytes),
|
||||||
|
bytes_to_string(total_bytes),
|
||||||
|
downloaded_bytes / total_bytes))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def update_handler(update_object):
|
def update_handler(update_object):
|
||||||
if type(update_object) is UpdateShortMessage:
|
if type(update_object) is UpdateShortMessage:
|
||||||
|
|
|
@ -189,7 +189,6 @@ class TelegramClient:
|
||||||
except:
|
except:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
# endregion
|
# endregion
|
||||||
|
|
||||||
# region Dialogs ("chats") requests
|
# region Dialogs ("chats") requests
|
||||||
|
@ -269,9 +268,19 @@ class TelegramClient:
|
||||||
# be handled through a separate session and a separate connection"
|
# be handled through a separate session and a separate connection"
|
||||||
# region Uploading media requests
|
# region Uploading media requests
|
||||||
|
|
||||||
def upload_file(self, file_path, part_size_kb=64, file_name=None):
|
def upload_file(self, file_path, part_size_kb=None, file_name=None, progress_callback=None):
|
||||||
"""Uploads the specified media with the given chunk (part) size, in KB.
|
"""Uploads the specified file_path and returns a handle which can be later used
|
||||||
If no custom file name is specified, the real file name will be used"""
|
|
||||||
|
:param file_path: The file path of the file that will be uploaded
|
||||||
|
:param part_size_kb: The part size when uploading the file. None = Automatic
|
||||||
|
:param file_name: The name of the uploaded file. None = Automatic
|
||||||
|
:param progress_callback: A callback function which takes two parameters,
|
||||||
|
uploaded size (in bytes) and total file size (in bytes)
|
||||||
|
This is called every time a part is uploaded
|
||||||
|
"""
|
||||||
|
file_size = path.getsize(file_path)
|
||||||
|
if not part_size_kb:
|
||||||
|
part_size_kb = self.find_appropiate_part_size(file_size)
|
||||||
|
|
||||||
if part_size_kb > 512:
|
if part_size_kb > 512:
|
||||||
raise ValueError('The part size must be less or equal to 512KB')
|
raise ValueError('The part size must be less or equal to 512KB')
|
||||||
|
@ -282,7 +291,6 @@ class TelegramClient:
|
||||||
|
|
||||||
# Determine whether the file is too big (over 10MB) or not
|
# Determine whether the file is too big (over 10MB) or not
|
||||||
# Telegram does make a distinction between smaller or larger files
|
# Telegram does make a distinction between smaller or larger files
|
||||||
file_size = path.getsize(file_path)
|
|
||||||
is_large = file_size > 10 * 1024 * 1024
|
is_large = file_size > 10 * 1024 * 1024
|
||||||
part_count = (file_size + part_size - 1) // part_size
|
part_count = (file_size + part_size - 1) // part_size
|
||||||
|
|
||||||
|
@ -307,6 +315,8 @@ class TelegramClient:
|
||||||
result = self.invoke(request)
|
result = self.invoke(request)
|
||||||
if result:
|
if result:
|
||||||
hash_md5.update(part)
|
hash_md5.update(part)
|
||||||
|
if progress_callback:
|
||||||
|
progress_callback(file.tell(), file_size)
|
||||||
else:
|
else:
|
||||||
raise ValueError('Could not upload file part #{}'.format(part_index))
|
raise ValueError('Could not upload file part #{}'.format(part_index))
|
||||||
|
|
||||||
|
@ -357,24 +367,34 @@ class TelegramClient:
|
||||||
|
|
||||||
# region Downloading media requests
|
# region Downloading media requests
|
||||||
|
|
||||||
def download_msg_media(self, message_media, file_path, add_extension=True):
|
def download_msg_media(self, message_media, file_path, add_extension=True, progress_callback=None):
|
||||||
"""Downloads the given MessageMedia (Photo, Document or Contact)
|
"""Downloads the given MessageMedia (Photo, Document or Contact)
|
||||||
into the desired file_path, optionally finding its extension automatically"""
|
into the desired file_path, optionally finding its extension automatically
|
||||||
|
The progress_callback should be a callback function which takes two parameters,
|
||||||
|
uploaded size (in bytes) and total file size (in bytes).
|
||||||
|
This will be called every time a part is downloaded"""
|
||||||
if type(message_media) == MessageMediaPhoto:
|
if type(message_media) == MessageMediaPhoto:
|
||||||
return self.download_photo(message_media, file_path, add_extension)
|
return self.download_photo(message_media, file_path, add_extension, progress_callback)
|
||||||
|
|
||||||
elif type(message_media) == MessageMediaDocument:
|
elif type(message_media) == MessageMediaDocument:
|
||||||
return self.download_document(message_media, file_path, add_extension)
|
return self.download_document(message_media, file_path, add_extension, progress_callback)
|
||||||
|
|
||||||
elif type(message_media) == MessageMediaContact:
|
elif type(message_media) == MessageMediaContact:
|
||||||
return self.download_contact(message_media, file_path, add_extension)
|
return self.download_contact(message_media, file_path, add_extension)
|
||||||
|
|
||||||
def download_photo(self, message_media_photo, file_path, add_extension=False):
|
def download_photo(self, message_media_photo, file_path, add_extension=False,
|
||||||
|
progress_callback=None):
|
||||||
"""Downloads MessageMediaPhoto's largest size into the desired
|
"""Downloads MessageMediaPhoto's largest size into the desired
|
||||||
file_path, optionally finding its extension automatically"""
|
file_path, optionally finding its extension automatically
|
||||||
|
The progress_callback should be a callback function which takes two parameters,
|
||||||
|
uploaded size (in bytes) and total file size (in bytes).
|
||||||
|
This will be called every time a part is downloaded"""
|
||||||
|
|
||||||
# Determine the photo and its largest size
|
# Determine the photo and its largest size
|
||||||
photo = message_media_photo.photo
|
photo = message_media_photo.photo
|
||||||
largest_size = photo.sizes[-1].location
|
largest_size = photo.sizes[-1]
|
||||||
|
file_size = largest_size.size
|
||||||
|
largest_size = largest_size.location
|
||||||
|
|
||||||
# Photos are always compressed into a .jpg by Telegram
|
# Photos are always compressed into a .jpg by Telegram
|
||||||
if add_extension:
|
if add_extension:
|
||||||
|
@ -383,14 +403,20 @@ class TelegramClient:
|
||||||
# Download the media with the largest size input file location
|
# Download the media with the largest size input file location
|
||||||
self.download_file_loc(InputFileLocation(volume_id=largest_size.volume_id,
|
self.download_file_loc(InputFileLocation(volume_id=largest_size.volume_id,
|
||||||
local_id=largest_size.local_id,
|
local_id=largest_size.local_id,
|
||||||
secret=largest_size.secret), file_path)
|
secret=largest_size.secret),
|
||||||
|
file_path, file_size, progress_callback)
|
||||||
return file_path
|
return file_path
|
||||||
|
|
||||||
def download_document(self, message_media_document, file_path=None, add_extension=True):
|
def download_document(self, message_media_document, file_path=None, add_extension=True,
|
||||||
|
progress_callback=None):
|
||||||
"""Downloads the given MessageMediaDocument into the desired
|
"""Downloads the given MessageMediaDocument into the desired
|
||||||
file_path, optionally finding its extension automatically.
|
file_path, optionally finding its extension automatically.
|
||||||
If no file_path is given, it will _try_ to be guessed from the document"""
|
If no file_path is given, it will try to be guessed from the document
|
||||||
|
The progress_callback should be a callback function which takes two parameters,
|
||||||
|
uploaded size (in bytes) and total file size (in bytes).
|
||||||
|
This will be called every time a part is downloaded"""
|
||||||
document = message_media_document.document
|
document = message_media_document.document
|
||||||
|
file_size = document.size
|
||||||
|
|
||||||
# If no file path was given, try to guess it from the attributes
|
# If no file path was given, try to guess it from the attributes
|
||||||
if file_path is None:
|
if file_path is None:
|
||||||
|
@ -413,7 +439,8 @@ class TelegramClient:
|
||||||
|
|
||||||
self.download_file_loc(InputDocumentFileLocation(id=document.id,
|
self.download_file_loc(InputDocumentFileLocation(id=document.id,
|
||||||
access_hash=document.access_hash,
|
access_hash=document.access_hash,
|
||||||
version=document.version), file_path)
|
version=document.version),
|
||||||
|
file_path, file_size, progress_callback)
|
||||||
|
|
||||||
return file_path
|
return file_path
|
||||||
|
|
||||||
|
@ -443,8 +470,17 @@ class TelegramClient:
|
||||||
|
|
||||||
return file_path
|
return file_path
|
||||||
|
|
||||||
def download_file_loc(self, input_location, file_path, part_size_kb=64):
|
def download_file_loc(self, input_location, file_path, part_size_kb=64,
|
||||||
"""Downloads media from the given input_file_location to the specified file_path"""
|
file_size=None, progress_callback=None):
|
||||||
|
"""Downloads media from the given input_file_location to the specified file_path.
|
||||||
|
If a progress_callback function is given, it will be called taking two
|
||||||
|
arguments (downloaded bytes count and total file size)"""
|
||||||
|
|
||||||
|
if not part_size_kb:
|
||||||
|
if not file_size:
|
||||||
|
raise ValueError('A part size value must be provided')
|
||||||
|
else:
|
||||||
|
part_size_kb = self.find_appropiate_part_size(file_size)
|
||||||
|
|
||||||
part_size = int(part_size_kb * 1024)
|
part_size = int(part_size_kb * 1024)
|
||||||
if part_size % 1024 != 0:
|
if part_size % 1024 != 0:
|
||||||
|
@ -468,6 +504,8 @@ class TelegramClient:
|
||||||
return result.type # Return some extra information
|
return result.type # Return some extra information
|
||||||
|
|
||||||
file.write(result.bytes)
|
file.write(result.bytes)
|
||||||
|
if progress_callback:
|
||||||
|
progress_callback(file.tell(), file_size)
|
||||||
|
|
||||||
# endregion
|
# endregion
|
||||||
|
|
||||||
|
@ -517,6 +555,21 @@ class TelegramClient:
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def find_appropiate_part_size(file_size):
|
||||||
|
if file_size <= 1048576: # 1MB
|
||||||
|
return 32
|
||||||
|
if file_size <= 10485760: # 10MB
|
||||||
|
return 64
|
||||||
|
if file_size <= 393216000: # 375MB
|
||||||
|
return 128
|
||||||
|
if file_size <= 786432000: # 750MB
|
||||||
|
return 256
|
||||||
|
if file_size <= 1572864000: # 1500MB
|
||||||
|
return 512
|
||||||
|
|
||||||
|
raise ValueError('File size too large')
|
||||||
|
|
||||||
# endregion
|
# endregion
|
||||||
|
|
||||||
# region Updates handling
|
# region Updates handling
|
||||||
|
|
Loading…
Reference in New Issue
Block a user