Add progress and progress_args parameters to Client.send_media_group and Message.reply_media_group

Signed-off-by: Ling-ex <nekochan@rizkiofficial.com>
This commit is contained in:
Ling-ex 2025-04-24 20:19:32 +00:00 committed by wulan17
parent 1b6d86ea77
commit 04c25b760f
No known key found for this signature in database
GPG key ID: 737814D4B5FF0420
2 changed files with 158 additions and 89 deletions

View file

@ -22,7 +22,12 @@ import os
import re import re
from datetime import datetime from datetime import datetime
from pymediainfo import MediaInfo from pymediainfo import MediaInfo
from typing import Union, List, Optional from typing import (
Union,
List,
Optional,
Callable,
)
import pyrogram import pyrogram
from pyrogram import enums from pyrogram import enums
@ -35,7 +40,6 @@ log = logging.getLogger(__name__)
class SendMediaGroup: class SendMediaGroup:
# TODO: Add progress parameter
async def send_media_group( async def send_media_group(
self: "pyrogram.Client", self: "pyrogram.Client",
chat_id: Union[int, str], chat_id: Union[int, str],
@ -59,7 +63,9 @@ class SendMediaGroup:
protect_content: bool = None, protect_content: bool = None,
allow_paid_broadcast: bool = None, allow_paid_broadcast: bool = None,
message_effect_id: int = None, message_effect_id: int = None,
invert_media: bool = None invert_media: bool = None,
progress: Callable = None,
progress_args: tuple = (),
) -> List["types.Message"]: ) -> List["types.Message"]:
"""Send a group of photos or videos as an album. """Send a group of photos or videos as an album.
@ -89,7 +95,7 @@ class SendMediaGroup:
reply_to_message_id (``int``, *optional*): reply_to_message_id (``int``, *optional*):
If the message is a reply, ID of the original message. If the message is a reply, ID of the original message.
reply_to_story_id (``int``, *optional*): reply_to_story_id (``int``, *optional*):
Unique identifier for the target story. Unique identifier for the target story.
@ -126,6 +132,28 @@ class SendMediaGroup:
invert_media (``bool``, *optional*): invert_media (``bool``, *optional*):
Inverts the position of the media and caption. Inverts the position of the media and caption.
progress (``Callable``, *optional*):
Pass a callback function to view the file transmission progress.
The function must take *(current, total)* as positional arguments (look at Other Parameters below for a
detailed description) and will be called back each time a new file chunk has been successfully
transmitted.
progress_args (``tuple``, *optional*):
Extra custom arguments for the progress callback function.
You can pass anything you need to be available in the progress callback scope; for example, a Message
object or a Client instance in order to edit the message with the updated progress status.
Other Parameters:
current (``int``):
The amount of bytes transmitted so far.
total (``int``):
The total size of the file.
*args (``tuple``, *optional*):
Extra custom arguments as defined in the ``progress_args`` parameter.
You can either keep ``*args`` or add every single extra argument in your function signature.
Returns: Returns:
List of :obj:`~pyrogram.types.Message`: On success, a list of the sent messages is returned. List of :obj:`~pyrogram.types.Message`: On success, a list of the sent messages is returned.
@ -154,21 +182,22 @@ class SendMediaGroup:
reply_to_chat_id=reply_to_chat_id, reply_to_chat_id=reply_to_chat_id,
quote_text=quote_text, quote_text=quote_text,
quote_entities=quote_entities, quote_entities=quote_entities,
parse_mode=parse_mode parse_mode=parse_mode,
) )
for i in media: for i in media:
if isinstance(i, types.InputMediaPhoto): if isinstance(i, types.InputMediaPhoto):
if isinstance(i.media, str): if isinstance(i.media, str):
if os.path.isfile(i.media): if os.path.isfile(i.media):
file = await self.save_file(i.media, progress=progress, progress_args=progress_args)
media = await self.invoke( media = await self.invoke(
raw.functions.messages.UploadMedia( raw.functions.messages.UploadMedia(
peer=await self.resolve_peer(chat_id), peer=await self.resolve_peer(chat_id),
media=raw.types.InputMediaUploadedPhoto( media=raw.types.InputMediaUploadedPhoto(
file=await self.save_file(i.media), file=file,
spoiler=i.has_spoiler spoiler=i.has_spoiler,
) ),
) ),
) )
media = raw.types.InputMediaPhoto( media = raw.types.InputMediaPhoto(
@ -177,7 +206,7 @@ class SendMediaGroup:
access_hash=media.photo.access_hash, access_hash=media.photo.access_hash,
file_reference=media.photo.file_reference file_reference=media.photo.file_reference
), ),
spoiler=i.has_spoiler spoiler=i.has_spoiler,
) )
elif re.match("^https?://", i.media): elif re.match("^https?://", i.media):
media = await self.invoke( media = await self.invoke(
@ -185,9 +214,9 @@ class SendMediaGroup:
peer=await self.resolve_peer(chat_id), peer=await self.resolve_peer(chat_id),
media=raw.types.InputMediaPhotoExternal( media=raw.types.InputMediaPhotoExternal(
url=i.media, url=i.media,
spoiler=i.has_spoiler spoiler=i.has_spoiler,
) ),
) ),
) )
media = raw.types.InputMediaPhoto( media = raw.types.InputMediaPhoto(
@ -196,19 +225,20 @@ class SendMediaGroup:
access_hash=media.photo.access_hash, access_hash=media.photo.access_hash,
file_reference=media.photo.file_reference file_reference=media.photo.file_reference
), ),
spoiler=i.has_spoiler spoiler=i.has_spoiler,
) )
else: else:
media = utils.get_input_media_from_file_id(i.media, FileType.PHOTO) media = utils.get_input_media_from_file_id(i.media, FileType.PHOTO)
else: else:
file = await self.save_file(i.media, progress=progress, progress_args=progress_args)
media = await self.invoke( media = await self.invoke(
raw.functions.messages.UploadMedia( raw.functions.messages.UploadMedia(
peer=await self.resolve_peer(chat_id), peer=await self.resolve_peer(chat_id),
media=raw.types.InputMediaUploadedPhoto( media=raw.types.InputMediaUploadedPhoto(
file=await self.save_file(i.media), file=file,
spoiler=i.has_spoiler spoiler=i.has_spoiler,
) ),
) ),
) )
media = raw.types.InputMediaPhoto( media = raw.types.InputMediaPhoto(
@ -217,7 +247,7 @@ class SendMediaGroup:
access_hash=media.photo.access_hash, access_hash=media.photo.access_hash,
file_reference=media.photo.file_reference file_reference=media.photo.file_reference
), ),
spoiler=i.has_spoiler spoiler=i.has_spoiler,
) )
elif ( elif (
isinstance(i, types.InputMediaVideo) isinstance(i, types.InputMediaVideo)
@ -241,22 +271,25 @@ class SendMediaGroup:
w=i.width, w=i.width,
h=i.height h=i.height
), ),
raw.types.DocumentAttributeFilename(file_name=os.path.basename(i.media)) raw.types.DocumentAttributeFilename(file_name=os.path.basename(i.media)),
] ]
if is_animation: if is_animation:
attributes.append(raw.types.DocumentAttributeAnimated()) attributes.append(raw.types.DocumentAttributeAnimated())
thumb = await self.save_file(i.thumb)
file = await self.save_file(i.media, progress=progress, progress_args=progress_args)
media = await self.invoke( media = await self.invoke(
raw.functions.messages.UploadMedia( raw.functions.messages.UploadMedia(
peer=await self.resolve_peer(chat_id), peer=await self.resolve_peer(chat_id),
media=raw.types.InputMediaUploadedDocument( media=raw.types.InputMediaUploadedDocument(
file=await self.save_file(i.media), file=file,
thumb=await self.save_file(i.thumb), thumb=thumb,
spoiler=i.has_spoiler, spoiler=i.has_spoiler,
mime_type=self.guess_mime_type(i.media) or "video/mp4", mime_type=self.guess_mime_type(i.media) or "video/mp4",
nosound_video=is_animation, nosound_video=is_animation,
attributes=attributes attributes=attributes,
) ),
) ),
) )
media = raw.types.InputMediaDocument( media = raw.types.InputMediaDocument(
@ -265,7 +298,7 @@ class SendMediaGroup:
access_hash=media.document.access_hash, access_hash=media.document.access_hash,
file_reference=media.document.file_reference file_reference=media.document.file_reference
), ),
spoiler=i.has_spoiler spoiler=i.has_spoiler,
) )
elif re.match("^https?://", i.media): elif re.match("^https?://", i.media):
media = await self.invoke( media = await self.invoke(
@ -273,9 +306,9 @@ class SendMediaGroup:
peer=await self.resolve_peer(chat_id), peer=await self.resolve_peer(chat_id),
media=raw.types.InputMediaDocumentExternal( media=raw.types.InputMediaDocumentExternal(
url=i.media, url=i.media,
spoiler=i.has_spoiler spoiler=i.has_spoiler,
) ),
) ),
) )
media = raw.types.InputMediaDocument( media = raw.types.InputMediaDocument(
@ -284,17 +317,19 @@ class SendMediaGroup:
access_hash=media.document.access_hash, access_hash=media.document.access_hash,
file_reference=media.document.file_reference file_reference=media.document.file_reference
), ),
spoiler=i.has_spoiler spoiler=i.has_spoiler,
) )
else: else:
media = utils.get_input_media_from_file_id(i.media, FileType.VIDEO) media = utils.get_input_media_from_file_id(i.media, FileType.VIDEO)
else: else:
thumb = await self.save_file(i.thumb)
file = await self.save_file(i.media, progress=progress, progress_args=progress_args)
media = await self.invoke( media = await self.invoke(
raw.functions.messages.UploadMedia( raw.functions.messages.UploadMedia(
peer=await self.resolve_peer(chat_id), peer=await self.resolve_peer(chat_id),
media=raw.types.InputMediaUploadedDocument( media=raw.types.InputMediaUploadedDocument(
file=await self.save_file(i.media), file=file,
thumb=await self.save_file(i.thumb), thumb=thumb,
spoiler=i.has_spoiler, spoiler=i.has_spoiler,
mime_type=self.guess_mime_type(getattr(i.media, "name", "video.mp4")) or "video/mp4", mime_type=self.guess_mime_type(getattr(i.media, "name", "video.mp4")) or "video/mp4",
attributes=[ attributes=[
@ -304,10 +339,10 @@ class SendMediaGroup:
w=i.width, w=i.width,
h=i.height h=i.height
), ),
raw.types.DocumentAttributeFilename(file_name=getattr(i.media, "name", "video.mp4")) raw.types.DocumentAttributeFilename(file_name=getattr(i.media, "name", "video.mp4")),
] ],
) ),
) ),
) )
media = raw.types.InputMediaDocument( media = raw.types.InputMediaDocument(
@ -316,127 +351,135 @@ class SendMediaGroup:
access_hash=media.document.access_hash, access_hash=media.document.access_hash,
file_reference=media.document.file_reference file_reference=media.document.file_reference
), ),
spoiler=i.has_spoiler spoiler=i.has_spoiler,
) )
elif isinstance(i, types.InputMediaAudio): elif isinstance(i, types.InputMediaAudio):
if isinstance(i.media, str): if isinstance(i.media, str):
if os.path.isfile(i.media): if os.path.isfile(i.media):
thumb = await self.save_file(i.thumb)
file = await self.save_file(i.media, progress=progress, progress_args=progress_args)
media = await self.invoke( media = await self.invoke(
raw.functions.messages.UploadMedia( raw.functions.messages.UploadMedia(
peer=await self.resolve_peer(chat_id), peer=await self.resolve_peer(chat_id),
media=raw.types.InputMediaUploadedDocument( media=raw.types.InputMediaUploadedDocument(
mime_type=self.guess_mime_type(i.media) or "audio/mpeg", mime_type=self.guess_mime_type(i.media) or "audio/mpeg",
file=await self.save_file(i.media), file=file,
thumb=await self.save_file(i.thumb), thumb=thumb,
attributes=[ attributes=[
raw.types.DocumentAttributeAudio( raw.types.DocumentAttributeAudio(
duration=i.duration, duration=i.duration,
performer=i.performer, performer=i.performer,
title=i.title title=i.title
), ),
raw.types.DocumentAttributeFilename(file_name=os.path.basename(i.media)) raw.types.DocumentAttributeFilename(file_name=os.path.basename(i.media)),
] ],
) ),
) ),
) )
media = raw.types.InputMediaDocument( media = raw.types.InputMediaDocument(
id=raw.types.InputDocument( id=raw.types.InputDocument(
id=media.document.id, id=media.document.id,
access_hash=media.document.access_hash, access_hash=media.document.access_hash,
file_reference=media.document.file_reference file_reference=media.document.file_reference,
) ),
) )
elif re.match("^https?://", i.media): elif re.match("^https?://", i.media):
media = await self.invoke( media = await self.invoke(
raw.functions.messages.UploadMedia( raw.functions.messages.UploadMedia(
peer=await self.resolve_peer(chat_id), peer=await self.resolve_peer(chat_id),
media=raw.types.InputMediaDocumentExternal( media=raw.types.InputMediaDocumentExternal(
url=i.media url=i.media,
) ),
) ),
) )
media = raw.types.InputMediaDocument( media = raw.types.InputMediaDocument(
id=raw.types.InputDocument( id=raw.types.InputDocument(
id=media.document.id, id=media.document.id,
access_hash=media.document.access_hash, access_hash=media.document.access_hash,
file_reference=media.document.file_reference file_reference=media.document.file_reference,
) ),
) )
else: else:
media = utils.get_input_media_from_file_id(i.media, FileType.AUDIO) media = utils.get_input_media_from_file_id(i.media, FileType.AUDIO)
else: else:
thumb = await self.save_file(i.thumb)
file = await self.save_file(i.media, progress=progress, progress_args=progress_args)
media = await self.invoke( media = await self.invoke(
raw.functions.messages.UploadMedia( raw.functions.messages.UploadMedia(
peer=await self.resolve_peer(chat_id), peer=await self.resolve_peer(chat_id),
media=raw.types.InputMediaUploadedDocument( media=raw.types.InputMediaUploadedDocument(
mime_type=self.guess_mime_type(getattr(i.media, "name", "audio.mp3")) or "audio/mpeg", mime_type=self.guess_mime_type(getattr(i.media, "name", "audio.mp3")) or "audio/mpeg",
file=await self.save_file(i.media), file=file,
thumb=await self.save_file(i.thumb), thumb=thumb,
attributes=[ attributes=[
raw.types.DocumentAttributeAudio( raw.types.DocumentAttributeAudio(
duration=i.duration, duration=i.duration,
performer=i.performer, performer=i.performer,
title=i.title title=i.title
), ),
raw.types.DocumentAttributeFilename(file_name=getattr(i.media, "name", "audio.mp3")) raw.types.DocumentAttributeFilename(file_name=getattr(i.media, "name", "audio.mp3")),
] ],
) ),
) ),
) )
media = raw.types.InputMediaDocument( media = raw.types.InputMediaDocument(
id=raw.types.InputDocument( id=raw.types.InputDocument(
id=media.document.id, id=media.document.id,
access_hash=media.document.access_hash, access_hash=media.document.access_hash,
file_reference=media.document.file_reference file_reference=media.document.file_reference,
) ),
) )
elif isinstance(i, types.InputMediaDocument): elif isinstance(i, types.InputMediaDocument):
if isinstance(i.media, str): if isinstance(i.media, str):
if os.path.isfile(i.media): if os.path.isfile(i.media):
thumb = await self.save_file(i.thumb)
file = await self.save_file(i.media, progress=progress, progress_args=progress_args)
media = await self.invoke( media = await self.invoke(
raw.functions.messages.UploadMedia( raw.functions.messages.UploadMedia(
peer=await self.resolve_peer(chat_id), peer=await self.resolve_peer(chat_id),
media=raw.types.InputMediaUploadedDocument( media=raw.types.InputMediaUploadedDocument(
mime_type=self.guess_mime_type(i.media) or "application/zip", mime_type=self.guess_mime_type(i.media) or "application/zip",
file=await self.save_file(i.media), file=file,
thumb=await self.save_file(i.thumb), thumb=thumb,
attributes=[ attributes=[
raw.types.DocumentAttributeFilename(file_name=os.path.basename(i.media)) raw.types.DocumentAttributeFilename(file_name=os.path.basename(i.media)),
] ],
) ),
) ),
) )
media = raw.types.InputMediaDocument( media = raw.types.InputMediaDocument(
id=raw.types.InputDocument( id=raw.types.InputDocument(
id=media.document.id, id=media.document.id,
access_hash=media.document.access_hash, access_hash=media.document.access_hash,
file_reference=media.document.file_reference file_reference=media.document.file_reference,
) ),
) )
elif re.match("^https?://", i.media): elif re.match("^https?://", i.media):
media = await self.invoke( media = await self.invoke(
raw.functions.messages.UploadMedia( raw.functions.messages.UploadMedia(
peer=await self.resolve_peer(chat_id), peer=await self.resolve_peer(chat_id),
media=raw.types.InputMediaDocumentExternal( media=raw.types.InputMediaDocumentExternal(
url=i.media url=i.media,
) ),
) ),
) )
media = raw.types.InputMediaDocument( media = raw.types.InputMediaDocument(
id=raw.types.InputDocument( id=raw.types.InputDocument(
id=media.document.id, id=media.document.id,
access_hash=media.document.access_hash, access_hash=media.document.access_hash,
file_reference=media.document.file_reference file_reference=media.document.file_reference,
) ),
) )
else: else:
media = utils.get_input_media_from_file_id(i.media, FileType.DOCUMENT) media = utils.get_input_media_from_file_id(i.media, FileType.DOCUMENT)
else: else:
thumb = await self.save_file(i.thumb)
file = await self.save_file(i.media, progress=progress, progress_args=progress_args)
media = await self.invoke( media = await self.invoke(
raw.functions.messages.UploadMedia( raw.functions.messages.UploadMedia(
peer=await self.resolve_peer(chat_id), peer=await self.resolve_peer(chat_id),
@ -444,21 +487,21 @@ class SendMediaGroup:
mime_type=self.guess_mime_type( mime_type=self.guess_mime_type(
getattr(i.media, "name", "file.zip") getattr(i.media, "name", "file.zip")
) or "application/zip", ) or "application/zip",
file=await self.save_file(i.media), file=file,
thumb=await self.save_file(i.thumb), thumb=thumb,
attributes=[ attributes=[
raw.types.DocumentAttributeFilename(file_name=getattr(i.media, "name", "file.zip")) raw.types.DocumentAttributeFilename(file_name=getattr(i.media, "name", "file.zip")),
] ],
) ),
) ),
) )
media = raw.types.InputMediaDocument( media = raw.types.InputMediaDocument(
id=raw.types.InputDocument( id=raw.types.InputDocument(
id=media.document.id, id=media.document.id,
access_hash=media.document.access_hash, access_hash=media.document.access_hash,
file_reference=media.document.file_reference file_reference=media.document.file_reference,
) ),
) )
else: else:
raise ValueError(f"{i.__class__.__name__} is not a supported type for send_media_group") raise ValueError(f"{i.__class__.__name__} is not a supported type for send_media_group")
@ -467,8 +510,8 @@ class SendMediaGroup:
raw.types.InputSingleMedia( raw.types.InputSingleMedia(
media=media, media=media,
random_id=self.rnd_id(), random_id=self.rnd_id(),
**(await utils.parse_text_entities(self, i.caption, i.parse_mode, i.caption_entities)) **(await utils.parse_text_entities(self, i.caption, i.parse_mode, i.caption_entities)),
) ),
) )
rpc = raw.functions.messages.SendMultiMedia( rpc = raw.functions.messages.SendMultiMedia(
@ -480,20 +523,20 @@ class SendMediaGroup:
noforwards=protect_content, noforwards=protect_content,
allow_paid_floodskip=allow_paid_broadcast, allow_paid_floodskip=allow_paid_broadcast,
effect=message_effect_id, effect=message_effect_id,
invert_media=invert_media invert_media=invert_media,
) )
if business_connection_id is not None: if business_connection_id is not None:
r = await self.invoke( r = await self.invoke(
raw.functions.InvokeWithBusinessConnection( raw.functions.InvokeWithBusinessConnection(
connection_id=business_connection_id, connection_id=business_connection_id,
query=rpc query=rpc
), ),
sleep_threshold=60 sleep_threshold=60,
) )
else: else:
r = await self.invoke(rpc, sleep_threshold=60) r = await self.invoke(rpc, sleep_threshold=60)
return await utils.parse_messages( return await utils.parse_messages(
self, self,
raw.types.messages.Messages( raw.types.messages.Messages(
@ -505,7 +548,7 @@ class SendMediaGroup:
r.updates r.updates
)], )],
users=r.users, users=r.users,
chats=r.chats chats=r.chats,
), ),
business_connection_id=business_connection_id business_connection_id=business_connection_id,
) )

View file

@ -2779,7 +2779,9 @@ class Message(Object, Update):
allow_paid_broadcast: bool = None, allow_paid_broadcast: bool = None,
message_effect_id: int = None, message_effect_id: int = None,
parse_mode: Optional["enums.ParseMode"] = None, parse_mode: Optional["enums.ParseMode"] = None,
invert_media: bool = None invert_media: bool = None,
progress: Callable = None,
progress_args: tuple = (),
) -> List["types.Message"]: ) -> List["types.Message"]:
"""Bound method *reply_media_group* of :obj:`~pyrogram.types.Message`. """Bound method *reply_media_group* of :obj:`~pyrogram.types.Message`.
@ -2840,6 +2842,28 @@ class Message(Object, Update):
invert_media (``bool``, *optional*): invert_media (``bool``, *optional*):
Inverts the position of the media and caption. Inverts the position of the media and caption.
progress (``Callable``, *optional*):
Pass a callback function to view the file transmission progress.
The function must take *(current, total)* as positional arguments (look at Other Parameters below for a
detailed description) and will be called back each time a new file chunk has been successfully
transmitted.
progress_args (``tuple``, *optional*):
Extra custom arguments for the progress callback function.
You can pass anything you need to be available in the progress callback scope; for example, a Message
object or a Client instance in order to edit the message with the updated progress status.
Other Parameters:
current (``int``):
The amount of bytes transmitted so far.
total (``int``):
The total size of the file.
*args (``tuple``, *optional*):
Extra custom arguments as defined in the ``progress_args`` parameter.
You can either keep ``*args`` or add every single extra argument in your function signature.
Returns: Returns:
On success, a :obj:`~pyrogram.types.Messages` object is returned containing all the On success, a :obj:`~pyrogram.types.Messages` object is returned containing all the
single messages sent. single messages sent.
@ -2878,7 +2902,9 @@ class Message(Object, Update):
quote_entities=quote_entities, quote_entities=quote_entities,
allow_paid_broadcast=allow_paid_broadcast, allow_paid_broadcast=allow_paid_broadcast,
message_effect_id=message_effect_id, message_effect_id=message_effect_id,
invert_media=invert_media invert_media=invert_media,
progress=progress,
progress_args=progress_args,
) )
async def reply_photo( async def reply_photo(