Make downloading files a lot more friendly with .download_media()

This commit is contained in:
Lonami Exo 2017-08-23 00:48:00 +02:00
parent 4234efcc86
commit fd6a9a4318

View File

@ -1,5 +1,5 @@
import os import os
from datetime import timedelta from datetime import datetime, timedelta
from mimetypes import guess_type from mimetypes import guess_type
from threading import Event, RLock, Thread from threading import Event, RLock, Thread
from time import sleep, time from time import sleep, time
@ -43,7 +43,7 @@ from .tl.types import (
ChatPhotoEmpty, DocumentAttributeAudio, DocumentAttributeFilename, ChatPhotoEmpty, DocumentAttributeAudio, DocumentAttributeFilename,
InputDocumentFileLocation, InputFileLocation, InputDocumentFileLocation, InputFileLocation,
InputMediaUploadedDocument, InputMediaUploadedPhoto, InputPeerEmpty, InputMediaUploadedDocument, InputMediaUploadedPhoto, InputPeerEmpty,
MessageMediaContact, MessageMediaDocument, MessageMediaPhoto, Message, MessageMediaContact, MessageMediaDocument, MessageMediaPhoto,
UserProfilePhotoEmpty, InputUserSelf) UserProfilePhotoEmpty, InputUserSelf)
from .utils import find_user_or_chat, get_extension from .utils import find_user_or_chat, get_extension
@ -595,45 +595,57 @@ class TelegramClient(TelegramBareClient):
) )
return True return True
def download_msg_media(self, def download_media(self,
message_media, message,
file,
add_extension=True,
progress_callback=None):
"""Downloads the given MessageMedia (Photo, Document or Contact)
into the desired file (a stream or str), optionally finding its
extension automatically.
The progress_callback should be a callback function which takes
two parameters, uploaded size and total file size (both in bytes).
This will be called every time a part is downloaded
"""
# TODO Support specifying a message
if type(message_media) == MessageMediaPhoto:
return self.download_photo(message_media, file, add_extension,
progress_callback)
elif type(message_media) == MessageMediaDocument:
return self.download_document(message_media, file,
add_extension, progress_callback)
elif type(message_media) == MessageMediaContact:
return self.download_contact(message_media, file,
add_extension)
# TODO Make these private and only expose download_msg_media?
def download_photo(self,
message_media_photo,
file, file,
add_extension=False, add_extension=True,
progress_callback=None): progress_callback=None):
"""Downloads MessageMediaPhoto's largest size into the desired file """Downloads the media from a specified Message (it can also be
(a stream or str), optionally finding its extension automatically. the message.media) into the desired file (a stream or str),
optionally finding its extension automatically.
The file may be an existing directory, or a full filename.
If the operation succeeds, the path will be returned (since
the extension may have been added automatically). Otherwise,
None is returned.
The progress_callback should be a callback function which takes The progress_callback should be a callback function which takes
two parameters, uploaded size and total file size (both in bytes). two parameters, uploaded size and total file size (both in bytes).
This will be called every time a part is downloaded This will be called every time a part is downloaded
""" """
# TODO This won't work for messageService
if isinstance(message, Message):
message = message.media
date = message.date
else:
date = datetime.now()
if isinstance(message, MessageMediaPhoto):
if isinstance(file, str) and (os.path.isdir(file) or not file):
file = os.path.join(file, 'photo_{}'.format(date))
return self._download_photo(
message, file, add_extension, progress_callback
)
elif isinstance(message, MessageMediaDocument):
# Pass the date if a better filename cannot be inferred
return self._download_document(
message, file, add_extension, date, progress_callback
)
elif isinstance(message, MessageMediaContact):
return self._download_contact(
message, file, add_extension
)
def _download_photo(self,
message_media_photo,
file,
add_extension=False,
progress_callback=None):
"""Specialized version of .download_media() for photos"""
# Determine the photo and its largest size # Determine the photo and its largest size
photo = message_media_photo.photo photo = message_media_photo.photo
@ -657,36 +669,24 @@ class TelegramClient(TelegramBareClient):
) )
return file return file
def download_document(self, def _download_document(self, message_media_document, file,
message_media_document, add_extension, date, progress_callback):
file=None, """Specialized version of .download_media() for documents"""
add_extension=True,
progress_callback=None):
"""Downloads the given MessageMediaDocument into the desired file
(a stream or str), optionally finding its extension automatically.
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 and total file size (both 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 file_size = document.size
# If no file path was given, try to guess it from the attributes if os.path.isdir(file) or not file:
if file is None:
for attr in document.attributes: for attr in document.attributes:
if type(attr) == DocumentAttributeFilename: if type(attr) == DocumentAttributeFilename:
file = attr.file_name file = os.path.join(file, attr.file_name)
break # This attribute has higher preference break # This attribute has higher preference
elif type(attr) == DocumentAttributeAudio: elif type(attr) == DocumentAttributeAudio:
file = '{} - {}'.format(attr.performer, attr.title) file = os.path.join(
file, '{} - {}'.format(attr.performer, attr.title)
)
if file is None: file = os.path.join(file, 'document_{}'.format(date))
raise ValueError('Could not infer a file_path for the document'
'. Please provide a valid file_path manually')
if isinstance(file, str) and add_extension: if isinstance(file, str) and add_extension:
file += get_extension(message_media_document) file += get_extension(message_media_document)
@ -704,14 +704,18 @@ class TelegramClient(TelegramBareClient):
return file return file
@staticmethod @staticmethod
def download_contact(message_media_contact, file, add_extension=True): def _download_contact(message_media_contact, file, add_extension=True):
"""Downloads a media contact using the vCard 4.0 format""" """Specialized version of .download_media() for contacts.
Will make use of the vCard 4.0 format
"""
first_name = message_media_contact.first_name first_name = message_media_contact.first_name
last_name = message_media_contact.last_name last_name = message_media_contact.last_name
phone_number = message_media_contact.phone_number phone_number = message_media_contact.phone_number
if isinstance(file, str): if isinstance(file, str):
if not file:
file = phone_number
# The only way we can save a contact in an understandable # The only way we can save a contact in an understandable
# way by phones is by using the .vCard format # way by phones is by using the .vCard format
if add_extension: if add_extension: