Make sending files a lot more friendly with a simple .send_file()

This commit is contained in:
Lonami Exo 2017-08-23 00:27:33 +02:00
parent 81ccd21cdb
commit 4234efcc86
2 changed files with 79 additions and 35 deletions

View File

@ -315,6 +315,10 @@ class TelegramBareClient:
"""Uploads the specified file_path and returns a handle (an instance """Uploads the specified file_path and returns a handle (an instance
of InputFile or InputFileBig, as required) which can be later used. of InputFile or InputFileBig, as required) which can be later used.
Uploading a file will simply return a "handle" to the file stored
remotely in the Telegram servers, which can be later used on. This
will NOT upload the file to your own chat.
If 'progress_callback' is not None, it should be a function that If 'progress_callback' is not None, it should be a function that
takes two parameters, (bytes_uploaded, total_bytes). takes two parameters, (bytes_uploaded, total_bytes).
@ -322,6 +326,7 @@ class TelegramBareClient:
part_size_kb = get_appropriated_part_size(file_size) part_size_kb = get_appropriated_part_size(file_size)
file_name = path.basename(file_path) file_name = path.basename(file_path)
""" """
# TODO Support both streams and bytes instead just "file_path"
file_size = path.getsize(file_path) file_size = path.getsize(file_path)
if not part_size_kb: if not part_size_kb:
part_size_kb = get_appropriated_part_size(file_size) part_size_kb = get_appropriated_part_size(file_size)

View File

@ -1,4 +1,4 @@
import errno import os
from datetime import timedelta from datetime import timedelta
from mimetypes import guess_type from mimetypes import guess_type
from threading import Event, RLock, Thread from threading import Event, RLock, Thread
@ -126,6 +126,9 @@ class TelegramClient(TelegramBareClient):
self._updates_thread = None self._updates_thread = None
self._phone_code_hashes = {} self._phone_code_hashes = {}
# Uploaded files cache so subsequent calls are instant
self._upload_cache = {}
# endregion # endregion
# region Connecting # region Connecting
@ -475,53 +478,86 @@ class TelegramClient(TelegramBareClient):
# region Uploading files # region Uploading files
def send_photo_file(self, input_file, entity, caption=''): def send_file(self, entity, file, caption='',
"""Sends a previously uploaded input_file force_document=False, callback=None):
(which should be a photo) to the given entity (or input peer)""" """Sends a file to the specified entity.
self.send_media_file( The file may either be a path, a byte array, or a stream.
InputMediaUploadedPhoto(input_file, caption), entity)
def send_document_file(self, input_file, entity, caption=''): An optional caption can also be specified for said file.
"""Sends a previously uploaded input_file
(which should be a document) to the given entity. If "force_document" is False, the file will be sent as a photo
if it's recognised to have a common image format (e.g. .png, .jpg).
Otherwise, the file will always be sent as an uncompressed document.
Subsequent calls with the very same file will result in
immediate uploads, unless .clear_file_cache() is called.
If "progress_callback" is not None, it should be a function that
takes two parameters, (bytes_uploaded, total_bytes).
The entity may be a phone or an username at the expense of The entity may be a phone or an username at the expense of
some performance loss. some performance loss.
""" """
as_photo = False
if isinstance(file, str):
lowercase_file = file.lower()
as_photo = any(
lowercase_file.endswith(ext)
for ext in ('.png', '.jpg', '.gif', '.jpeg')
)
# Determine mime-type and attributes file_hash = hash(file)
# Take the first element by using [0] since it returns a tuple if file_hash in self._upload_cache:
mime_type = guess_type(input_file.name)[0] file_handle = self._upload_cache[file_hash]
attributes = [ else:
DocumentAttributeFilename(input_file.name) file_handle = self.upload_file(file, progress_callback=callback)
# TODO If the input file is an audio, find out: self._upload_cache[file_hash] = file_handle
# Performer and song title and add DocumentAttributeAudio
] if as_photo and not force_document:
# Ensure we have a mime type, any; but it cannot be None media = InputMediaUploadedPhoto(file_handle, caption)
# 'The "octet-stream" subtype is used to indicate that a body else:
# contains arbitrary binary data.' mime_type = None
if not mime_type: if isinstance(file, str):
mime_type = 'application/octet-stream' # Determine mime-type and attributes
self.send_media_file( # Take the first element by using [0] since it returns a tuple
InputMediaUploadedDocument( mime_type = guess_type(file)[0]
file=input_file, attributes = [
DocumentAttributeFilename(os.path.abspath(file))
# TODO If the input file is an audio, find out:
# Performer and song title and add DocumentAttributeAudio
]
else:
attributes = [DocumentAttributeFilename('unnamed')]
# Ensure we have a mime type, any; but it cannot be None
# 'The "octet-stream" subtype is used to indicate that a body
# contains arbitrary binary data.'
if not mime_type:
mime_type = 'application/octet-stream'
media = InputMediaUploadedDocument(
file=file_handle,
mime_type=mime_type, mime_type=mime_type,
attributes=attributes, attributes=attributes,
caption=caption), caption=caption
entity) )
def send_media_file(self, input_media, entity): # Once the media type is properly specified and the file uploaded,
"""Sends any input_media (contact, document, photo...) # send the media message to the desired entity.
to the given entity.
The entity may be a phone or an username at the expense of
some performance loss.
"""
self(SendMediaRequest( self(SendMediaRequest(
peer=self._get_entity(entity), peer=self._get_entity(entity),
media=input_media media=media
)) ))
def clear_file_cache(self):
"""Calls to .send_file() will cache the remote location of the
uploaded files so that subsequent files can be immediate, so
uploading the same file path will result in using the cached
version. To avoid this a call to this method should be made.
"""
self._upload_cache.clear()
# endregion # endregion
# region Downloading media requests # region Downloading media requests
@ -723,6 +759,9 @@ class TelegramClient(TelegramBareClient):
If the entity is neither, and it's not a TLObject, an If the entity is neither, and it's not a TLObject, an
error will be raised. error will be raised.
""" """
# TODO Maybe cache both the contacts and the entities.
# If an user cannot be found, force a cache update through
# a public method (since users may change their username)
if isinstance(entity, TLObject): if isinstance(entity, TLObject):
return entity return entity