Skip to content

Commit

Permalink
Add thumbnail layout option
Browse files Browse the repository at this point in the history
Signed-off-by: anasty17 <[email protected]>
  • Loading branch information
anasty17 committed Sep 11, 2024
1 parent 198f736 commit d807af6
Show file tree
Hide file tree
Showing 10 changed files with 172 additions and 45 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ substantial modifications and is designed for efficiently mirroring or leeching
destinations, including Google Drive, Telegram, or any rclone-supported cloud. It is built using asynchronous
programming in Python.

- **TELEGRAM CHANNEL:** https://t.me/mltb_official_channel
- **TELEGRAM GROUP:** https://t.me/mltb_official_support

# Features

## qBittorrent
Expand Down Expand Up @@ -40,6 +43,8 @@ programming in Python.
- Download restricted messages (document or link) by tg private/public/super links (task option)
- Choose transfer by bot or user session in case you have a premium plan (global, user option and task option)
- Mix upload between user and bot session with respect to file size (global, user option and task option)
- Upload with custom layout multiple thubnmail (global, user option and task option)
- Topics support

## Google Drive

Expand Down Expand Up @@ -142,6 +147,7 @@ programming in Python.
- Bulk download from telegram txt file or text message contains links seperated by new line (task option)
- Join splitted files that have splitted before by split(linux pkg) (task option)
- Sample video Generator (task option)
- Screenshots Generator (task option)
- Ability to cancel upload/clone/archive/extract/split/queue (task option)
- Cancel all buttons for choosing specific tasks status to cancel (global option)
- Convert videos and audios to specific format with filter (task option)
Expand Down Expand Up @@ -312,6 +318,7 @@ quotes, even if it's `Int`, `Bool` or `List`.
- `MIXED_LEECH`: Upload by user and bot session with respect to file size. Only in superChat. Default is `False`. `Bool`
- `LEECH_FILENAME_PREFIX`: Add custom word to leeched file name. `Str`
- `LEECH_DUMP_CHAT`: ID or USERNAME or PM(private message) to where files would be uploaded. `Int`|`Str`. Add `-100` before channel/superGroup id.
- `THUMBNAIL_LAYOUT`: Thumbnail layout (widthxheight, 2x2, 3x3, 2x4, 4x4, ...) of how many photo arranged for the thumbnail.`Str`

**7. qBittorrent/Aria2c/Sabnzbd**

Expand Down
4 changes: 4 additions & 0 deletions bot/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,9 @@
MIXED_LEECH = environ.get("MIXED_LEECH", "")
MIXED_LEECH = MIXED_LEECH.lower() == "true" and IS_PREMIUM_USER

THUMBNAIL_LAYOUT = environ.get("THUMBNAIL_LAYOUT", "")
THUMBNAIL_LAYOUT = "" if len(THUMBNAIL_LAYOUT) == 0 else THUMBNAIL_LAYOUT

config_dict = {
"AS_DOCUMENT": AS_DOCUMENT,
"AUTHORIZED_CHATS": AUTHORIZED_CHATS,
Expand Down Expand Up @@ -466,6 +469,7 @@
"SUDO_USERS": SUDO_USERS,
"TELEGRAM_API": TELEGRAM_API,
"TELEGRAM_HASH": TELEGRAM_HASH,
"THUMBNAIL_LAYOUT": THUMBNAIL_LAYOUT,
"TORRENT_TIMEOUT": TORRENT_TIMEOUT,
"USER_TRANSMISSION": USER_TRANSMISSION,
"UPSTREAM_REPO": UPSTREAM_REPO,
Expand Down
11 changes: 11 additions & 0 deletions bot/helper/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ def __init__(self):
self.name = ""
self.new_dir = ""
self.name_sub = ""
self.thumbnail_layout = ""
self.split_size = 0
self.max_split_size = 0
self.multi = 0
Expand Down Expand Up @@ -362,6 +363,16 @@ async def before_start(self):
and "as_doc" not in self.user_dict
)

self.thumbnail_layout = (
self.thumbnail_layout
or self.user_dict.get("thumb_layout", False)
or (
config_dict["THUMBNAIL_LAYOUT"]
if "thumb_layout" not in self.user_dict
else ""
)
)

if is_telegram_link(self.thumb):
msg = (await get_tg_link_message(self.thumb))[0]
self.thumb = (
Expand Down
6 changes: 5 additions & 1 deletion bot/helper/ext_utils/help_messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@

screenshot = """<b>ScreenShots</b>: -ss
Create up to 10 screenshots for one video or folder of videos.
Create screenshots for one video or folder of videos.
/cmd -ss (it will take the default values which is 10 photos).
You can control this value. Example: /cmd -ss 6."""

Expand Down Expand Up @@ -207,6 +207,9 @@
mixed_leech = """Mixed leech: -ml
/cmd link -ml (leech by user and bot session with respect to size)"""

thumbnail_layout = """Thumbnail Layout: -tl
/cmd link -tl 3x3 (widthxheight) 3 photos in row and 3 photos in column"""

YT_HELP_DICT = {
"main": yt,
"New-Name": f"{new_name}\nNote: Don't add file extension",
Expand Down Expand Up @@ -253,6 +256,7 @@
"User-Download": user_download,
"Name-Substitute": name_sub,
"Mixed-Leech": mixed_leech,
"Thumbnail-Layout": thumbnail_layout,
}

CLONE_HELP_DICT = {
Expand Down
93 changes: 71 additions & 22 deletions bot/helper/ext_utils/media_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from time import time
from aioshutil import rmtree

from bot import LOGGER, subprocess_lock
from bot import LOGGER, subprocess_lock, DOWNLOAD_DIR
from .bot_utils import cmd_exec, sync_to_async
from .files_utils import ARCH_EXT, get_mime_type

Expand Down Expand Up @@ -103,13 +103,13 @@ async def convert_audio(listener, audio_file, ext):
async def create_thumb(msg, _id=""):
if not _id:
_id = msg.id
path = "Thumbnails/"
path = f"{DOWNLOAD_DIR}Thumbnails"
await makedirs(path, exist_ok=True)
photo_dir = await msg.download()
des_dir = f"{path}{_id}.jpg"
await sync_to_async(Image.open(photo_dir).convert("RGB").save, des_dir, "JPEG")
output = ospath.join(path, f"{_id}.jpg")
await sync_to_async(Image.open(photo_dir).convert("RGB").save, output, "JPEG")
await remove(photo_dir)
return des_dir
return output


async def is_multi_streams(path):
Expand Down Expand Up @@ -223,18 +223,17 @@ async def get_document_type(path):


async def take_ss(video_file, ss_nb) -> bool:
ss_nb = min(ss_nb, 10)
duration = (await get_media_info(video_file))[0]
if duration != 0:
dirpath, name = video_file.rsplit("/", 1)
name, _ = ospath.splitext(name)
dirpath = f"{dirpath}/{name}_mltbss/"
dirpath = f"{dirpath}/{name}_mltbss"
await makedirs(dirpath, exist_ok=True)
interval = duration // (ss_nb + 1)
cap_time = interval
cmds = []
for i in range(ss_nb):
output = f"{dirpath}SS.{name}_{i:02}.png"
output = f"{dirpath}/SS.{name}_{i:02}.png"
cmd = [
"ffmpeg",
"-hide_banner",
Expand Down Expand Up @@ -275,10 +274,10 @@ async def take_ss(video_file, ss_nb) -> bool:
return False


async def get_audio_thumb(audio_file):
des_dir = "Thumbnails/"
await makedirs(des_dir, exist_ok=True)
des_dir = f"Thumbnails/{time()}.jpg"
async def get_audio_thumbnail(audio_file):
output_dir = f"{DOWNLOAD_DIR}Thumbnails"
await makedirs(output_dir, exist_ok=True)
output = ospath.join(output_dir, f"{time()}.jpg")
cmd = [
"ffmpeg",
"-hide_banner",
Expand All @@ -291,21 +290,21 @@ async def get_audio_thumb(audio_file):
"copy",
"-threads",
f"{cpu_count() // 2}",
des_dir,
output,
]
_, err, code = await cmd_exec(cmd)
if code != 0 or not await aiopath.exists(des_dir):
if code != 0 or not await aiopath.exists(output):
LOGGER.error(
f"Error while extracting thumbnail from audio. Name: {audio_file} stderr: {err}"
)
return None
return des_dir
return output


async def create_thumbnail(video_file, duration):
des_dir = "Thumbnails"
await makedirs(des_dir, exist_ok=True)
des_dir = ospath.join(des_dir, f"{time()}.jpg")
async def get_video_thumbnail(video_file, duration):
output_dir = f"{DOWNLOAD_DIR}Thumbnails"
await makedirs(output_dir, exist_ok=True)
output = ospath.join(output_dir, f"{time()}.jpg")
if duration is None:
duration = (await get_media_info(video_file))[0]
if duration == 0:
Expand All @@ -322,15 +321,17 @@ async def create_thumbnail(video_file, duration):
video_file,
"-vf",
"thumbnail",
"-q:v",
"1",
"-frames:v",
"1",
"-threads",
f"{cpu_count() // 2}",
des_dir,
output,
]
try:
_, err, code = await wait_for(cmd_exec(cmd), timeout=60)
if code != 0 or not await aiopath.exists(des_dir):
if code != 0 or not await aiopath.exists(output):
LOGGER.error(
f"Error while extracting thumbnail from video. Name: {video_file} stderr: {err}"
)
Expand All @@ -340,7 +341,55 @@ async def create_thumbnail(video_file, duration):
f"Error while extracting thumbnail from video. Name: {video_file}. Error: Timeout some issues with ffmpeg with specific arch!"
)
return None
return des_dir
return output


async def get_multiple_frames_thumbnail(video_file, layout, keep_screenshots):
ss_nb = layout.split("x")
ss_nb = int(ss_nb[0]) * int(ss_nb[1])
dirpath = await take_ss(video_file, ss_nb)
if not dirpath:
return None
output_dir = f"{DOWNLOAD_DIR}Thumbnails"
await makedirs(output_dir, exist_ok=True)
output = ospath.join(output_dir, f"{time()}.jpg")
cmd = [
"ffmpeg",
"-hide_banner",
"-loglevel",
"error",
"-pattern_type",
"glob",
"-i",
f"{dirpath}/*.png",
"-vf",
f"tile={layout}, thumbnail",
"-q:v",
"1",
"-frames:v",
"1",
"-f",
"mjpeg",
"-threads",
f"{cpu_count() // 2}",
output,
]
try:
_, err, code = await wait_for(cmd_exec(cmd), timeout=60)
if code != 0 or not await aiopath.exists(output):
LOGGER.error(
f"Error while combining thumbnails for video. Name: {video_file} stderr: {err}"
)
return None
except:
LOGGER.error(
f"Error while combining thumbnails from video. Name: {video_file}. Error: Timeout some issues with ffmpeg with specific arch!"
)
return None
finally:
if not keep_screenshots:
await rmtree(dirpath, ignore_errors=True)
return output


async def split_file(
Expand Down
33 changes: 21 additions & 12 deletions bot/helper/mirror_leech_utils/telegram_uploader.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,9 @@
from ..ext_utils.media_utils import (
get_media_info,
get_document_type,
create_thumbnail,
get_audio_thumb,
get_video_thumbnail,
get_audio_thumbnail,
get_multiple_frames_thumbnail,
)

LOGGER = getLogger(__name__)
Expand Down Expand Up @@ -199,13 +200,15 @@ async def _send_screenshots(self, dirpath, outputs):
InputMediaPhoto(ospath.join(dirpath, p), p.rsplit("/", 1)[-1])
for p in outputs
]
self._sent_msg = (
await self._sent_msg.reply_media_group(
media=inputs,
quote=True,
disable_notification=True,
)
)[-1]
for i in range(0, len(inputs), 10):
batch = inputs[i : i + 10]
self._sent_msg = (
await self._sent_msg.reply_media_group(
media=batch,
quote=True,
disable_notification=True,
)
)[-1]

async def _send_media_group(self, subkey, key, msgs):
for index, msg in enumerate(msgs):
Expand Down Expand Up @@ -372,7 +375,7 @@ async def _upload_file(self, cap_mono, file, o_path, force_document=False):
if await aiopath.isfile(thumb_path):
thumb = thumb_path
elif is_audio and not is_video:
thumb = await get_audio_thumb(self._up_path)
thumb = await get_audio_thumbnail(self._up_path)

if (
self._listener.as_doc
Expand All @@ -381,7 +384,7 @@ async def _upload_file(self, cap_mono, file, o_path, force_document=False):
):
key = "documents"
if is_video and thumb is None:
thumb = await create_thumbnail(self._up_path, None)
thumb = await get_video_thumbnail(self._up_path, None)

if self._listener.is_cancelled:
return
Expand All @@ -397,8 +400,14 @@ async def _upload_file(self, cap_mono, file, o_path, force_document=False):
elif is_video:
key = "videos"
duration = (await get_media_info(self._up_path))[0]
if thumb is None and self._listener.thumbnail_layout:
thumb = await get_multiple_frames_thumbnail(
self._up_path,
self._listener.thumbnail_layout,
self._listener.screen_shots,
)
if thumb is None:
thumb = await create_thumbnail(self._up_path, duration)
thumb = await get_video_thumbnail(self._up_path, duration)
if thumb is not None:
with Image.open(thumb) as img:
width, height = img.size
Expand Down
4 changes: 4 additions & 0 deletions bot/modules/bot_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -1181,6 +1181,9 @@ async def load_config():
MIXED_LEECH = environ.get("MIXED_LEECH", "")
MIXED_LEECH = MIXED_LEECH.lower() == "true" and IS_PREMIUM_USER

THUMBNAIL_LAYOUT = environ.get("THUMBNAIL_LAYOUT", "")
THUMBNAIL_LAYOUT = "" if len(THUMBNAIL_LAYOUT) == 0 else THUMBNAIL_LAYOUT

await (await create_subprocess_exec("pkill", "-9", "-f", "gunicorn")).wait()
BASE_URL = environ.get("BASE_URL", "").rstrip("/")
if len(BASE_URL) == 0:
Expand Down Expand Up @@ -1267,6 +1270,7 @@ async def load_config():
"SUDO_USERS": SUDO_USERS,
"TELEGRAM_API": TELEGRAM_API,
"TELEGRAM_HASH": TELEGRAM_HASH,
"THUMBNAIL_LAYOUT": THUMBNAIL_LAYOUT,
"TORRENT_TIMEOUT": TORRENT_TIMEOUT,
"USER_TRANSMISSION": USER_TRANSMISSION,
"UPSTREAM_REPO": UPSTREAM_REPO,
Expand Down
2 changes: 2 additions & 0 deletions bot/modules/mirror_leech.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ async def new_event(self):
"-ca": "",
"-cv": "",
"-ns": "",
"-tl": "",
}

arg_parser(input_list[1:], args)
Expand All @@ -131,6 +132,7 @@ async def new_event(self):
self.convert_video = args["-cv"]
self.name_sub = args["-ns"]
self.mixed_leech = args["-ml"]
self.thumbnail_layout = args["-tl"]

headers = args["-h"]
is_bulk = args["-b"]
Expand Down
Loading

0 comments on commit d807af6

Please sign in to comment.