Rework in-memory uploads

This commit is contained in:
Dan 2020-07-09 02:56:09 +02:00
parent de8f784f78
commit b3faf21c95
9 changed files with 260 additions and 126 deletions

View File

@ -16,6 +16,7 @@
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
import io
import logging
import math
import os
@ -30,7 +31,7 @@ from importlib import import_module, reload
from pathlib import Path
from signal import signal, SIGINT, SIGTERM, SIGABRT
from threading import Thread
from typing import Union, List
from typing import Union, List, BinaryIO
from pyrogram.api import functions, types
from pyrogram.api.core import TLObject
@ -39,9 +40,9 @@ from pyrogram.client.handlers.handler import Handler
from pyrogram.client.methods.password.utils import compute_check
from pyrogram.crypto import AES
from pyrogram.errors import (
PhoneMigrate, NetworkMigrate, SessionPasswordNeeded,
FloodWait, PeerIdInvalid, VolumeLocNotFound, UserMigrate, ChannelPrivate, AuthBytesInvalid,
BadRequest)
PhoneMigrate, NetworkMigrate, SessionPasswordNeeded, PeerIdInvalid, VolumeLocNotFound, UserMigrate, ChannelPrivate,
AuthBytesInvalid, BadRequest
)
from pyrogram.session import Auth, Session
from .ext import utils, Syncer, BaseClient, Dispatcher
from .methods import Methods
@ -1713,7 +1714,7 @@ class Client(Methods, BaseClient):
def save_file(
self,
path: str,
path: Union[str, BinaryIO],
file_id: int = None,
file_part: int = 0,
progress: callable = None,
@ -1767,7 +1768,19 @@ class Client(Methods, BaseClient):
RPCError: In case of a Telegram RPC error.
"""
part_size = 512 * 1024
file_size = os.path.getsize(path)
if isinstance(path, str):
fp = open(path, "rb")
elif isinstance(path, io.IOBase):
fp = path
else:
raise ValueError("Invalid file. Expected a file path as string or a binary (not text) file pointer")
file_name = fp.name
fp.seek(0, os.SEEK_END)
file_size = fp.tell()
fp.seek(0)
if file_size == 0:
raise ValueError("File size equals to 0 B")
@ -1785,11 +1798,11 @@ class Client(Methods, BaseClient):
session.start()
try:
with open(path, "rb") as f:
f.seek(part_size * file_part)
with fp:
fp.seek(part_size * file_part)
while True:
chunk = f.read(part_size)
chunk = fp.read(part_size)
if not chunk:
if not is_big:
@ -1835,14 +1848,14 @@ class Client(Methods, BaseClient):
return types.InputFileBig(
id=file_id,
parts=file_total_parts,
name=os.path.basename(path),
name=file_name,
)
else:
return types.InputFile(
id=file_id,
parts=file_total_parts,
name=os.path.basename(path),
name=file_name,
md5_checksum=md5_sum
)
finally:

View File

@ -18,7 +18,7 @@
import os
import re
from typing import Union
from typing import Union, BinaryIO
import pyrogram
from pyrogram.api import functions, types
@ -30,7 +30,7 @@ class SendAnimation(BaseClient):
def send_animation(
self,
chat_id: Union[int, str],
animation: str,
animation: Union[str, BinaryIO],
file_ref: str = None,
caption: str = "",
unsave: bool = False,
@ -38,7 +38,7 @@ class SendAnimation(BaseClient):
duration: int = 0,
width: int = 0,
height: int = 0,
thumb: str = None,
thumb: Union[str, BinaryIO] = None,
file_name: str = None,
disable_notification: bool = None,
reply_to_message_id: int = None,
@ -60,11 +60,12 @@ class SendAnimation(BaseClient):
For your personal cloud (Saved Messages) you can simply use "me" or "self".
For a contact that exists in your Telegram address book you can use his phone number (str).
animation (``str``):
animation (``str`` | ``BinaryIO``):
Animation to send.
Pass a file_id as string to send an animation that exists on the Telegram servers,
pass an HTTP URL as a string for Telegram to get an animation from the Internet, or
pass a file path as string to upload a new animation that exists on your local machine.
pass an HTTP URL as a string for Telegram to get an animation from the Internet,
pass a file path as string to upload a new animation that exists on your local machine, or
pass a binary file-like object with its attribute ".name" set for in-memory uploads.
file_ref (``str``, *optional*):
A valid file reference obtained by a recently fetched media message.
@ -93,7 +94,7 @@ class SendAnimation(BaseClient):
height (``int``, *optional*):
Animation height.
thumb (``str``, *optional*):
thumb (``str`` | ``BinaryIO``, *optional*):
Thumbnail of the animation file sent.
The thumbnail should be in JPEG format and less than 200 KB in size.
A thumbnail's width and height should not exceed 320 pixels.
@ -164,6 +165,7 @@ class SendAnimation(BaseClient):
file = None
try:
if isinstance(animation, str):
if os.path.isfile(animation):
thumb = None if thumb is None else self.save_file(thumb)
file = self.save_file(animation, progress=progress, progress_args=progress_args)
@ -188,6 +190,24 @@ class SendAnimation(BaseClient):
)
else:
media = utils.get_input_media_from_file_id(animation, file_ref, 10)
else:
thumb = None if thumb is None else self.save_file(thumb)
file = self.save_file(animation, progress=progress, progress_args=progress_args)
media = types.InputMediaUploadedDocument(
mime_type=self.guess_mime_type(animation.name) or "video/mp4",
file=file,
thumb=thumb,
attributes=[
types.DocumentAttributeVideo(
supports_streaming=True,
duration=duration,
w=width,
h=height
),
types.DocumentAttributeFilename(file_name=animation.name),
types.DocumentAttributeAnimated()
]
)
while True:
try:

View File

@ -18,7 +18,7 @@
import os
import re
from typing import Union
from typing import Union, BinaryIO
import pyrogram
from pyrogram.api import functions, types
@ -30,14 +30,14 @@ class SendAudio(BaseClient):
def send_audio(
self,
chat_id: Union[int, str],
audio: str,
audio: Union[str, BinaryIO],
file_ref: str = None,
caption: str = "",
parse_mode: Union[str, None] = object,
duration: int = 0,
performer: str = None,
title: str = None,
thumb: str = None,
thumb: Union[str, BinaryIO] = None,
file_name: str = None,
disable_notification: bool = None,
reply_to_message_id: int = None,
@ -61,11 +61,12 @@ class SendAudio(BaseClient):
For your personal cloud (Saved Messages) you can simply use "me" or "self".
For a contact that exists in your Telegram address book you can use his phone number (str).
audio (``str``):
audio (``str`` | ``BinaryIO``):
Audio file to send.
Pass a file_id as string to send an audio file that exists on the Telegram servers,
pass an HTTP URL as a string for Telegram to get an audio file from the Internet, or
pass a file path as string to upload a new audio file that exists on your local machine.
pass an HTTP URL as a string for Telegram to get an audio file from the Internet,
pass a file path as string to upload a new audio file that exists on your local machine, or
pass a binary file-like object with its attribute ".name" set for in-memory uploads.
file_ref (``str``, *optional*):
A valid file reference obtained by a recently fetched media message.
@ -90,7 +91,7 @@ class SendAudio(BaseClient):
title (``str``, *optional*):
Track name.
thumb (``str``, *optional*):
thumb (``str`` | ``BinaryIO``, *optional*):
Thumbnail of the music file album cover.
The thumbnail should be in JPEG format and less than 200 KB in size.
A thumbnail's width and height should not exceed 320 pixels.
@ -164,6 +165,7 @@ class SendAudio(BaseClient):
file = None
try:
if isinstance(audio, str):
if os.path.isfile(audio):
thumb = None if thumb is None else self.save_file(thumb)
file = self.save_file(audio, progress=progress, progress_args=progress_args)
@ -186,6 +188,22 @@ class SendAudio(BaseClient):
)
else:
media = utils.get_input_media_from_file_id(audio, file_ref, 9)
else:
thumb = None if thumb is None else self.save_file(thumb)
file = self.save_file(audio, progress=progress, progress_args=progress_args)
media = types.InputMediaUploadedDocument(
mime_type=self.guess_mime_type(audio.name) or "audio/mpeg",
file=file,
thumb=thumb,
attributes=[
types.DocumentAttributeAudio(
duration=duration,
performer=performer,
title=title
),
types.DocumentAttributeFilename(file_name=audio.name)
]
)
while True:
try:

View File

@ -18,7 +18,7 @@
import os
import re
from typing import Union
from typing import Union, BinaryIO
import pyrogram
from pyrogram.api import functions, types
@ -30,9 +30,9 @@ class SendDocument(BaseClient):
def send_document(
self,
chat_id: Union[int, str],
document: str,
document: Union[str, BinaryIO],
file_ref: str = None,
thumb: str = None,
thumb: Union[str, BinaryIO] = None,
caption: str = "",
parse_mode: Union[str, None] = object,
file_name: str = None,
@ -56,17 +56,18 @@ class SendDocument(BaseClient):
For your personal cloud (Saved Messages) you can simply use "me" or "self".
For a contact that exists in your Telegram address book you can use his phone number (str).
document (``str``):
document (``str`` | ``BinaryIO``):
File to send.
Pass a file_id as string to send a file that exists on the Telegram servers,
pass an HTTP URL as a string for Telegram to get a file from the Internet, or
pass a file path as string to upload a new file that exists on your local machine.
pass an HTTP URL as a string for Telegram to get a file from the Internet,
pass a file path as string to upload a new file that exists on your local machine, or
pass a binary file-like object with its attribute ".name" set for in-memory uploads.
file_ref (``str``, *optional*):
A valid file reference obtained by a recently fetched media message.
To be used in combination with a file id in case a file reference is needed.
thumb (``str``, *optional*):
thumb (``str`` | ``BinaryIO``, *optional*):
Thumbnail of the file sent.
The thumbnail should be in JPEG format and less than 200 KB in size.
A thumbnail's width and height should not exceed 320 pixels.
@ -144,6 +145,7 @@ class SendDocument(BaseClient):
file = None
try:
if isinstance(document, str):
if os.path.isfile(document):
thumb = None if thumb is None else self.save_file(thumb)
file = self.save_file(document, progress=progress, progress_args=progress_args)
@ -161,6 +163,17 @@ class SendDocument(BaseClient):
)
else:
media = utils.get_input_media_from_file_id(document, file_ref, 5)
else:
thumb = None if thumb is None else self.save_file(thumb)
file = self.save_file(document, progress=progress, progress_args=progress_args)
media = types.InputMediaUploadedDocument(
mime_type=self.guess_mime_type(document.name) or "application/zip",
file=file,
thumb=thumb,
attributes=[
types.DocumentAttributeFilename(file_name=document.name)
]
)
while True:
try:

View File

@ -18,7 +18,7 @@
import os
import re
from typing import Union
from typing import Union, BinaryIO
import pyrogram
from pyrogram.api import functions, types
@ -30,7 +30,7 @@ class SendPhoto(BaseClient):
def send_photo(
self,
chat_id: Union[int, str],
photo: str,
photo: Union[str, BinaryIO],
file_ref: str = None,
caption: str = "",
parse_mode: Union[str, None] = object,
@ -55,11 +55,12 @@ class SendPhoto(BaseClient):
For your personal cloud (Saved Messages) you can simply use "me" or "self".
For a contact that exists in your Telegram address book you can use his phone number (str).
photo (``str``):
photo (``str`` | ``BinaryIO``):
Photo to send.
Pass a file_id as string to send a photo that exists on the Telegram servers,
pass an HTTP URL as a string for Telegram to get a photo from the Internet, or
pass a file path as string to upload a new photo that exists on your local machine.
pass an HTTP URL as a string for Telegram to get a photo from the Internet,
pass a file path as string to upload a new photo that exists on your local machine, or
pass a binary file-like object with its attribute ".name" set for in-memory uploads.
file_ref (``str``, *optional*):
A valid file reference obtained by a recently fetched media message.
@ -138,6 +139,7 @@ class SendPhoto(BaseClient):
file = None
try:
if isinstance(photo, str):
if os.path.isfile(photo):
file = self.save_file(photo, progress=progress, progress_args=progress_args)
media = types.InputMediaUploadedPhoto(
@ -151,6 +153,12 @@ class SendPhoto(BaseClient):
)
else:
media = utils.get_input_media_from_file_id(photo, file_ref, 2)
else:
file = self.save_file(photo, progress=progress, progress_args=progress_args)
media = types.InputMediaUploadedPhoto(
file=file,
ttl_seconds=ttl_seconds
)
while True:
try:

View File

@ -18,7 +18,7 @@
import os
import re
from typing import Union
from typing import Union, BinaryIO
import pyrogram
from pyrogram.api import functions, types
@ -30,7 +30,7 @@ class SendSticker(BaseClient):
def send_sticker(
self,
chat_id: Union[int, str],
sticker: str,
sticker: Union[str, BinaryIO],
file_ref: str = None,
disable_notification: bool = None,
reply_to_message_id: int = None,
@ -52,11 +52,12 @@ class SendSticker(BaseClient):
For your personal cloud (Saved Messages) you can simply use "me" or "self".
For a contact that exists in your Telegram address book you can use his phone number (str).
sticker (``str``):
sticker (``str`` | ``BinaryIO``):
Sticker to send.
Pass a file_id as string to send a sticker that exists on the Telegram servers,
pass an HTTP URL as a string for Telegram to get a .webp sticker file from the Internet, or
pass a file path as string to upload a new sticker that exists on your local machine.
pass an HTTP URL as a string for Telegram to get a .webp sticker file from the Internet,
pass a file path as string to upload a new sticker that exists on your local machine, or
pass a binary file-like object with its attribute ".name" set for in-memory uploads.
file_ref (``str``, *optional*):
A valid file reference obtained by a recently fetched media message.
@ -114,6 +115,7 @@ class SendSticker(BaseClient):
file = None
try:
if isinstance(sticker, str):
if os.path.isfile(sticker):
file = self.save_file(sticker, progress=progress, progress_args=progress_args)
media = types.InputMediaUploadedDocument(
@ -129,6 +131,15 @@ class SendSticker(BaseClient):
)
else:
media = utils.get_input_media_from_file_id(sticker, file_ref, 8)
else:
file = self.save_file(sticker, progress=progress, progress_args=progress_args)
media = types.InputMediaUploadedDocument(
mime_type=self.guess_mime_type(sticker.name) or "image/webp",
file=file,
attributes=[
types.DocumentAttributeFilename(file_name=sticker.name)
]
)
while True:
try:

View File

@ -18,7 +18,7 @@
import os
import re
from typing import Union
from typing import Union, BinaryIO
import pyrogram
from pyrogram.api import functions, types
@ -30,14 +30,14 @@ class SendVideo(BaseClient):
def send_video(
self,
chat_id: Union[int, str],
video: str,
video: Union[str, BinaryIO],
file_ref: str = None,
caption: str = "",
parse_mode: Union[str, None] = object,
duration: int = 0,
width: int = 0,
height: int = 0,
thumb: str = None,
thumb: Union[str, BinaryIO] = None,
file_name: str = None,
supports_streaming: bool = True,
disable_notification: bool = None,
@ -60,11 +60,12 @@ class SendVideo(BaseClient):
For your personal cloud (Saved Messages) you can simply use "me" or "self".
For a contact that exists in your Telegram address book you can use his phone number (str).
video (``str``):
video (``str`` | ``BinaryIO``):
Video to send.
Pass a file_id as string to send a video that exists on the Telegram servers,
pass an HTTP URL as a string for Telegram to get a video from the Internet, or
pass a file path as string to upload a new video that exists on your local machine.
pass an HTTP URL as a string for Telegram to get a video from the Internet,
pass a file path as string to upload a new video that exists on your local machine, or
pass a binary file-like object with its attribute ".name" set for in-memory uploads.
file_ref (``str``, *optional*):
A valid file reference obtained by a recently fetched media message.
@ -89,7 +90,7 @@ class SendVideo(BaseClient):
height (``int``, *optional*):
Video height.
thumb (``str``, *optional*):
thumb (``str`` | ``BinaryIO``, *optional*):
Thumbnail of the video sent.
The thumbnail should be in JPEG format and less than 200 KB in size.
A thumbnail's width and height should not exceed 320 pixels.
@ -161,6 +162,7 @@ class SendVideo(BaseClient):
file = None
try:
if isinstance(video, str):
if os.path.isfile(video):
thumb = None if thumb is None else self.save_file(thumb)
file = self.save_file(video, progress=progress, progress_args=progress_args)
@ -184,6 +186,23 @@ class SendVideo(BaseClient):
)
else:
media = utils.get_input_media_from_file_id(video, file_ref, 4)
else:
thumb = None if thumb is None else self.save_file(thumb)
file = self.save_file(video, progress=progress, progress_args=progress_args)
media = types.InputMediaUploadedDocument(
mime_type=self.guess_mime_type(video.name) or "video/mp4",
file=file,
thumb=thumb,
attributes=[
types.DocumentAttributeVideo(
supports_streaming=supports_streaming or None,
duration=duration,
w=width,
h=height
),
types.DocumentAttributeFilename(file_name=video.name)
]
)
while True:
try:

View File

@ -17,7 +17,7 @@
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
import os
from typing import Union
from typing import Union, BinaryIO
import pyrogram
from pyrogram.api import functions, types
@ -29,11 +29,11 @@ class SendVideoNote(BaseClient):
def send_video_note(
self,
chat_id: Union[int, str],
video_note: str,
video_note: Union[str, BinaryIO],
file_ref: str = None,
duration: int = 0,
length: int = 1,
thumb: str = None,
thumb: Union[str, BinaryIO] = None,
disable_notification: bool = None,
reply_to_message_id: int = None,
schedule_date: int = None,
@ -54,10 +54,11 @@ class SendVideoNote(BaseClient):
For your personal cloud (Saved Messages) you can simply use "me" or "self".
For a contact that exists in your Telegram address book you can use his phone number (str).
video_note (``str``):
video_note (``str`` | ``BinaryIO``):
Video note to send.
Pass a file_id as string to send a video note that exists on the Telegram servers, or
pass a file path as string to upload a new video note that exists on your local machine.
Pass a file_id as string to send a video note that exists on the Telegram servers,
pass a file path as string to upload a new video note that exists on your local machine, or
pass a binary file-like object with its attribute ".name" set for in-memory uploads.
Sending video notes by a URL is currently unsupported.
file_ref (``str``, *optional*):
@ -70,7 +71,7 @@ class SendVideoNote(BaseClient):
length (``int``, *optional*):
Video width and height.
thumb (``str``, *optional*):
thumb (``str`` | ``BinaryIO``, *optional*):
Thumbnail of the video sent.
The thumbnail should be in JPEG format and less than 200 KB in size.
A thumbnail's width and height should not exceed 320 pixels.
@ -128,6 +129,7 @@ class SendVideoNote(BaseClient):
file = None
try:
if isinstance(video_note, str):
if os.path.isfile(video_note):
thumb = None if thumb is None else self.save_file(thumb)
file = self.save_file(video_note, progress=progress, progress_args=progress_args)
@ -146,6 +148,22 @@ class SendVideoNote(BaseClient):
)
else:
media = utils.get_input_media_from_file_id(video_note, file_ref, 13)
else:
thumb = None if thumb is None else self.save_file(thumb)
file = self.save_file(video_note, progress=progress, progress_args=progress_args)
media = types.InputMediaUploadedDocument(
mime_type=self.guess_mime_type(video_note.name) or "video/mp4",
file=file,
thumb=thumb,
attributes=[
types.DocumentAttributeVideo(
round_message=True,
duration=duration,
w=length,
h=length
)
]
)
while True:
try:

View File

@ -18,7 +18,7 @@
import os
import re
from typing import Union
from typing import Union, BinaryIO
import pyrogram
from pyrogram.api import functions, types
@ -30,7 +30,7 @@ class SendVoice(BaseClient):
def send_voice(
self,
chat_id: Union[int, str],
voice: str,
voice: Union[str, BinaryIO],
file_ref=None,
caption: str = "",
parse_mode: Union[str, None] = object,
@ -55,11 +55,12 @@ class SendVoice(BaseClient):
For your personal cloud (Saved Messages) you can simply use "me" or "self".
For a contact that exists in your Telegram address book you can use his phone number (str).
voice (``str``):
voice (``str`` | ``BinaryIO``):
Audio file to send.
Pass a file_id as string to send an audio that exists on the Telegram servers,
pass an HTTP URL as a string for Telegram to get an audio from the Internet, or
pass a file path as string to upload a new audio that exists on your local machine.
pass an HTTP URL as a string for Telegram to get an audio from the Internet,
pass a file path as string to upload a new audio that exists on your local machine, or
pass a binary file-like object with its attribute ".name" set for in-memory uploads.
file_ref (``str``, *optional*):
A valid file reference obtained by a recently fetched media message.
@ -133,6 +134,7 @@ class SendVoice(BaseClient):
file = None
try:
if isinstance(voice, str):
if os.path.isfile(voice):
file = self.save_file(voice, progress=progress, progress_args=progress_args)
media = types.InputMediaUploadedDocument(
@ -151,6 +153,18 @@ class SendVoice(BaseClient):
)
else:
media = utils.get_input_media_from_file_id(voice, file_ref, 3)
else:
file = self.save_file(voice, progress=progress, progress_args=progress_args)
media = types.InputMediaUploadedDocument(
mime_type=self.guess_mime_type(voice.name) or "audio/mpeg",
file=file,
attributes=[
types.DocumentAttributeAudio(
voice=True,
duration=duration
)
]
)
while True:
try: