Skip to content

Commit

Permalink
Add mediainfo, frames and collage modules
Browse files Browse the repository at this point in the history
  • Loading branch information
l3v11 authored Mar 20, 2023
1 parent 4a31cf9 commit 3041d49
Show file tree
Hide file tree
Showing 10 changed files with 504 additions and 2 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ ENV LANGUAGE=en_US:en \

RUN apt-get -qq update && apt-get -qq install -y \
python3 python3-pip locales libmagic-dev \
p7zip-full p7zip-rar unzip && \
p7zip-full p7zip-rar unzip mediainfo ffmpeg && \
locale-gen en_US.UTF-8

COPY requirements.txt .
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ SearchX is a multipurpose Telegram bot written in Python for Google Drive
- Compress data from Google Drive, AppDrive and GDToT
- Extract data from Google Drive, AppDrive and GDToT
- Count data from Google Drive
- Generate mediainfo of media files from Google Drive and URL
- Generate frames of media files from Google Drive and URL
- Generate collage of media files from Google Drive and URL
- Delete data from Google Drive
- Set data permission in Google Drive
- Size Limit support for Clone, Compression and Extraction tasks
Expand Down Expand Up @@ -60,6 +63,9 @@ clone - Clone data to Google Drive
compress - Compress data to Google Drive
extract - Extract data to Google Drive
count - Count data from Google Drive
minfo - Generate mediainfo of a media file
ss - Generate frames of a media file
col - Generate collage of a media file
cancel - Cancel a task
status - Get status of all tasks
share - Set data permission in Google Drive
Expand Down
8 changes: 7 additions & 1 deletion bot/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from telegram.ext import CommandHandler

from bot import bot, LOGGER, botStartTime, TELEGRAPH, Interval, dispatcher, updater
from bot.modules import archive, auth, bookmark, cancel, clone, count, delete, eval, list, permission, shell, status
from bot.modules import archive, auth, bookmark, cancel, clone, collage, count, delete, eval, frames, list, mediainfo, permission, shell, status
from bot.helper.ext_utils.bot_utils import get_readable_file_size, get_readable_time
from bot.helper.ext_utils.fs_utils import start_cleanup, clean_all, exit_clean_up
from bot.helper.telegram_helper.bot_commands import BotCommands
Expand Down Expand Up @@ -78,6 +78,12 @@ def restart(update, context):
<br><br>
• <b>/{BotCommands.CountCommand}</b> &lt;drive_url&gt;: Count data from Google Drive
<br><br>
• <b>/{BotCommands.MediainfoCommand}</b> &lt;url&gt;: Generate mediainfo of a media file from Google Drive and URL
<br><br>
• <b>/{BotCommands.FramesCommand}</b> &lt;url&gt; &lt;count&gt;: Generate frames of a media file from Google Drive and URL (Count optional)
<br><br>
• <b>/{BotCommands.CollageCommand}</b> &lt;url&gt; &lt;grid&gt;: Generate collage of a media file from Google Drive and URL (Grid optional)
<br><br>
• <b>/{BotCommands.CancelCommand}</b> &lt;gid&gt;: Cancel a task
<br><br>
• <b>/{BotCommands.StatusCommand}</b>: Get status of all tasks
Expand Down
38 changes: 38 additions & 0 deletions bot/helper/drive_utils/gdriveTools.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,14 @@ def __alt_authorize(self):
LOGGER.error("The token.json file is missing")
return None

def __get_access_token(self):
creds = None
if os.path.exists('token.json'):
creds = Credentials.from_authorized_user_file('token.json', self.__OAUTH_SCOPE)
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
return creds.token

def __switchServiceAccount(self):
if self.__service_account_index == SERVICE_ACCOUNTS_NUMBER - 1:
self.__service_account_index = 0
Expand Down Expand Up @@ -169,6 +177,36 @@ def __getFilesByFolderId(self, folder_id):
break
return files

def fileinfo(self, link):
try:
file_id = self.__getIdFromUrl(link)
except (KeyError, IndexError):
msg = "Drive ID not found"
LOGGER.error(msg)
return msg, "", "", "", "", ""
try:
access_token = self.__get_access_token()
meta = self.__getFileMetadata(file_id)
name = meta.get("name")
size = get_readable_file_size(int(meta.get("size", 0)))
mime_type = meta.get("mimeType")
except Exception as err:
if isinstance(err, RetryError):
LOGGER.info(f"Total attempts: {err.last_attempt.attempt_number}")
err = err.last_attempt.exception()
err = str(err).replace('>', '').replace('<', '')
if "File not found" in err:
token_service = self.__alt_authorize()
if token_service is not None:
self.__service = token_service
return self.fileinfo(link)
msg = "File not found"
else:
msg = err
LOGGER.error(msg)
return msg, "", "", "", "", ""
return "", file_id, access_token, name, size, mime_type

def __gDrive_file(self, filee):
size = int(filee.get('size', 0))
self.__total_bytes += size
Expand Down
34 changes: 34 additions & 0 deletions bot/helper/ext_utils/bot_utils.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import os
import re
import requests
import threading
import time

from html import escape
from psutil import virtual_memory, cpu_percent, disk_usage
from requests_toolbelt import MultipartEncoder

from bot.helper.telegram_helper.bot_commands import BotCommands
from bot import botStartTime, DOWNLOAD_DIR, download_dict, download_dict_lock
Expand Down Expand Up @@ -115,6 +118,37 @@ def get_readable_time(seconds: int) -> str:
result += f'{seconds}s'
return result

def slowpics_collection(path):
img_list = os.listdir(path)
data = {
"collectionName": "SearchX",
"hentai": "false",
"optimizeImages": "false",
"public": "false"
}
for i in range(0, len(img_list)):
data[f"images[{i}].name"] = img_list[i]
data[f"images[{i}].file"] = (img_list[i], open(f"{path}/{img_list[i]}", 'rb'), 'image/png')
with requests.Session() as client:
client.get('https://slow.pics/api/collection')
files = MultipartEncoder(data)
length = str(files.len)
headers = {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate, br",
"Accept-Language": "en-US,en;q=0.5",
"Content-Length": length,
"Content-Type": files.content_type,
"Origin": "https://slow.pics/",
"Referer": "https://slow.pics/collection",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "same-origin",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.88 Safari/537.36",
"X-XSRF-TOKEN": client.cookies.get_dict()["XSRF-TOKEN"]
}
response = client.post("https://slow.pics/api/collection", data=files, headers=headers)
return f"https://slow.pics/c/{response.text}"

def is_url(url: str):
url = re.findall(URL_REGEX, url)
return bool(url)
Expand Down
3 changes: 3 additions & 0 deletions bot/helper/telegram_helper/bot_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ def __init__(self):
self.CompressCommand = 'compress'
self.ExtractCommand = 'extract'
self.CountCommand = 'count'
self.MediainfoCommand = 'minfo'
self.FramesCommand = 'ss'
self.CollageCommand = 'col'
self.CancelCommand = 'cancel'
self.StatusCommand = 'status'
self.PermissionCommand = 'share'
Expand Down
189 changes: 189 additions & 0 deletions bot/modules/collage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
import datetime
import mimetypes
import os
import random
import requests
import string
import subprocess

from PIL import Image, ImageFont, ImageDraw, ImageOps
from telegram.ext import CommandHandler
from urllib.parse import unquote_plus

from bot import LOGGER, DOWNLOAD_DIR, dispatcher
from bot.helper.drive_utils.gdriveTools import GoogleDriveHelper
from bot.helper.ext_utils.bot_utils import new_thread, get_readable_file_size, slowpics_collection, is_url, is_gdrive_link
from bot.helper.ext_utils.exceptions import DDLExceptionHandler
from bot.helper.ext_utils.fs_utils import clean_download
from bot.helper.telegram_helper.message_utils import sendMessage, deleteMessage
from bot.helper.telegram_helper.bot_commands import BotCommands
from bot.helper.telegram_helper.button_builder import ButtonMaker
from bot.helper.telegram_helper.filters import CustomFilters

@new_thread
def collageNode(update, context):
args = update.message.text.split()
reply_to = update.message.reply_to_message
link = ''
grid = ''
row = 4
col = 4
if len(args) > 1:
link = args[1].strip()
try:
grid = args[2].split("x", maxsplit=1)
row = int(grid[0])
col = int(grid[1])
except (IndexError, ValueError):
pass
if reply_to:
link = reply_to.text.split(maxsplit=1)[0].strip()
try:
grid = args[1].split("x", maxsplit=1)
row = int(grid[0])
col = int(grid[1])
except (IndexError, ValueError):
pass
if row > 5 or col > 5:
row = 5
col = 5
elif row != col:
row = 4
col = 4
if is_url(link):
msg = sendMessage(f"<b>Getting {row}x{col} collage:</b> <code>{link}</code>", context.bot, update.message)
LOGGER.info(f"Getting {row}x{col} collage: {link}")
if is_gdrive_link(link):
try:
gd = GoogleDriveHelper()
res, file_id, access_token, name, size, mime_type = gd.fileinfo(link)
if res != "":
deleteMessage(context.bot, msg)
return sendMessage(res, context.bot, update.message)
if mime_type == "application/vnd.google-apps.folder":
raise DDLExceptionHandler("Folder is not supported")
file_dl = f"https://www.googleapis.com/drive/v3/files/{file_id}\?supportsAllDrives\=true\&alt\=media"
header = f"Authorization: Bearer {access_token}"
out = subprocess.run(f"ffprobe -headers '{header}' -i {file_dl} -show_entries format=duration -v error -of csv=p=0", capture_output=True, shell=True)
stderr = out.stderr.decode('utf-8')
if "403 Forbidden" in stderr:
raise DDLExceptionHandler("Download quota exceeded")
duration = out.stdout.decode('utf-8')
if duration == '':
raise ValueError("Unsupported media file")
durationhms = str(datetime.timedelta(seconds=int(float(duration))))
uid = ''.join(random.SystemRandom().choices(string.ascii_letters + string.digits, k=12))
path = f"{DOWNLOAD_DIR}{uid}"
os.makedirs(path)
for seconds in random.sample(range(int(float(duration))), int(row*col)):
img = ''.join(random.SystemRandom().choices(string.ascii_letters + string.digits, k=3))
genss = subprocess.run(f"ffmpeg -headers '{header}' -hide_banner -ss {seconds} -i {file_dl} -frames:v 1 -q:v 2 -y {path}/{img}.png", capture_output=True, shell=True)
if "403 Forbidden" in genss.stderr.decode('utf-8'):
raise DDLExceptionHandler("Download quota exceeded fucker")
img_list = os.listdir(path)
images = [Image.open(fp) for fp in [os.path.join(path, file) for file in img_list]]
widths, heights = zip(*(i.size for i in images))
max_width = max(widths)
max_height = max(heights)
canvas_width = max_width*row
canvas_height = max_height*col
canvas = Image.new(mode="RGB", size=(canvas_width, canvas_height), color=(0, 0, 0))
tile_count=0
for j in range(0, canvas_height - 1, max_height):
for i in range(0, canvas_width - 1, max_width):
im = Image.open(f'{path}/{img_list[tile_count]}')
canvas.paste(im, box=(i, j))
tile_count+=1
canvas_exp = ImageOps.expand(canvas, border=(0,int(canvas_height/6.5),0,0), fill=(0, 0, 0))
draw = ImageDraw.Draw(canvas_exp)
text = f"{name}\nSize: {size}\nDuration: {durationhms}\nDimension: {max_width}x{max_height}"
font = ImageFont.truetype(font="/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", size=int(canvas_height/30))
draw.text((30,10), text=text, fill=(255, 255, 255), font=font)
collage_path = f"{path}/collage"
os.makedirs(collage_path)
img = ''.join(random.SystemRandom().choices(string.ascii_letters + string.digits, k=4))
canvas_res = canvas_exp.resize(size=(max_width, max_height))
canvas_res.save(f"{collage_path}/{img}.png")
img_link = slowpics_collection(collage_path)
LOGGER.info(f"img limk {img_link}")
result = ""
result += f"<b>Name:</b> <code>{name}</code>"
result += f"\n<b>Size:</b> <code>{size}</code>"
result += f"\n<b>Type:</b> <code>{mime_type}</code>"
button = ButtonMaker()
button.build_button("VIEW COLLAGE 🗂️", f"{img_link}")
clean_download(path)
deleteMessage(context.bot, msg)
sendMessage(result, context.bot, update.message, button.build_menu(1))
except Exception as err:
deleteMessage(context.bot, msg)
LOGGER.error(str(err))
return sendMessage(str(err), context.bot, update.message)
else:
try:
res = requests.head(link, stream=True)
name = unquote_plus(link).rsplit('/', 1)[-1]
size = get_readable_file_size(int(res.headers["Content-Length"].strip()))
mime_type = res.headers.get("Content-Type", mimetypes.guess_type(name)).rsplit(";", 1)[0]
out = subprocess.run(["ffprobe", "-i", f"{link}", "-show_entries", "format=duration", "-v", "quiet", "-of", "csv=p=0"], capture_output=True)
duration = out.stdout.decode('utf-8')
if duration == '':
raise ValueError("Unsupported media file")
durationhms = str(datetime.timedelta(seconds=int(float(duration))))
uid = ''.join(random.SystemRandom().choices(string.ascii_letters + string.digits, k=12))
path = f"{DOWNLOAD_DIR}{uid}"
os.makedirs(path)
sscount=1
for seconds in random.sample(range(int(float(duration))), int(row*col)):
img = ''.join(random.SystemRandom().choices(string.ascii_letters + string.digits, k=4))
genss = subprocess.run(["ffmpeg", "-hide_banner", "-ss", f"{seconds}", "-i", f"{link}", "-frames:v", "1", "-q:v", "2", "-y", f"{path}/{img}.png"], capture_output=True)
sscount+=1
img_list = os.listdir(path)
images = [Image.open(fp) for fp in [os.path.join(path, file) for file in img_list]]
widths, heights = zip(*(i.size for i in images))
max_width = max(widths)
max_height = max(heights)
canvas_width = max_width*row
canvas_height = max_height*col
canvas = Image.new(mode="RGB", size=(canvas_width, canvas_height), color=(0, 0, 0))
tile_count=0
for j in range(0, canvas_height - 1, max_height):
for i in range(0, canvas_width - 1, max_width):
im = Image.open(f'{path}/{img_list[tile_count]}')
canvas.paste(im, box=(i, j))
tile_count+=1
canvas_exp = ImageOps.expand(canvas, border=(0,int(canvas_height/6.5),0,0), fill=(0, 0, 0))
draw = ImageDraw.Draw(canvas_exp)
text = f"{name}\nSize: {size}\nDuration: {durationhms}\nDimension: {max_width}x{max_height}"
font = ImageFont.truetype(font="/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", size=int(canvas_height/30))
draw.text((30,10), text=text, fill=(255, 255, 255), font=font)
collage_path = f"{path}/collage"
os.makedirs(collage_path)
img = ''.join(random.SystemRandom().choices(string.ascii_letters + string.digits, k=4))
canvas_res = canvas_exp.resize(size=(max_width, max_height))
canvas_res.save(f"{collage_path}/{img}.png")
img_link = slowpics_collection(collage_path)
result = ""
result += f"<b>Name:</b> <code>{name}</code>"
result += f"\n<b>Size:</b> <code>{size}</code>"
result += f"\n<b>Type:</b> <code>{mime_type}</code>"
button = ButtonMaker()
button.build_button("VIEW COLLAGE 🗂️", f"{img_link}")
clean_download(path)
deleteMessage(context.bot, msg)
sendMessage(result, context.bot, update.message, button.build_menu(1))
except KeyError:
deleteMessage(context.bot, msg)
err = "Invalid link"
LOGGER.error(str(err))
return sendMessage(str(err), context.bot, update.message)
except Exception as err:
deleteMessage(context.bot, msg)
LOGGER.error(str(err))
return sendMessage(str(err), context.bot, update.message)
else:
sendMessage("<b>Send a link along with command</b>", context.bot, update.message)

collage_handler = CommandHandler(BotCommands.CollageCommand, collageNode,
filters=CustomFilters.authorized_user | CustomFilters.authorized_chat)
dispatcher.add_handler(collage_handler)
Loading

0 comments on commit 3041d49

Please sign in to comment.