diff --git a/bot/core/config_manager.py b/bot/core/config_manager.py
index 0f38a713b..f12ef0889 100644
--- a/bot/core/config_manager.py
+++ b/bot/core/config_manager.py
@@ -12,9 +12,7 @@ class Config:
DATABASE_URL = ""
DEFAULT_UPLOAD = "rc"
DOWNLOAD_DIR = "/usr/src/app/downloads/"
- DELETE_LINKS = False
EXTENSION_FILTER = ""
- FSUB_IDS = ""
FFMPEG_CMDS = {}
FILELION_API = ""
GDRIVE_ID = ""
@@ -30,8 +28,6 @@ class Config:
MEGA_PASSWORD = ""
NAME_SUBSTITUTE = ""
OWNER_ID = 0
- PAID_CHANNEL_ID = 0
- PAID_CHANNEL_LINK = ""
QUEUE_ALL = 0
QUEUE_DOWNLOAD = 0
QUEUE_UPLOAD = 0
@@ -45,7 +41,6 @@ class Config:
RSS_DELAY = 600
SEARCH_API_LINK = ""
SEARCH_LIMIT = 0
- SET_COMMANDS = True
SEARCH_PLUGINS: ClassVar[list[str]] = []
STOP_DUPLICATE = False
STREAMWISH_API = ""
@@ -54,7 +49,6 @@ class Config:
TELEGRAM_HASH = ""
THUMBNAIL_LAYOUT = ""
TORRENT_TIMEOUT = 0
- TOKEN_TIMEOUT = 0
USER_TRANSMISSION = False
UPSTREAM_REPO = ""
UPSTREAM_BRANCH = "main"
@@ -62,7 +56,16 @@ class Config:
USE_SERVICE_ACCOUNTS = False
WEB_PINCODE = False
YT_DLP_OPTIONS = ""
-
+
+ # INKYPINKY
+ METADATA_KEY = ""
+ SET_COMMANDS = True
+ TOKEN_TIMEOUT = 0
+ PAID_CHANNEL_ID = 0
+ PAID_CHANNEL_LINK = ""
+ DELETE_LINKS = False
+ FSUB_IDS = ""
+
@classmethod
def get(cls, key):
if hasattr(cls, key):
diff --git a/bot/helper/aeon_utils/metadata_editor.py b/bot/helper/aeon_utils/metadata_editor.py
index 1d3179d7a..3f3b98cb8 100644
--- a/bot/helper/aeon_utils/metadata_editor.py
+++ b/bot/helper/aeon_utils/metadata_editor.py
@@ -6,10 +6,7 @@
from bot import LOGGER
-async def change_metadata(file, key):
- LOGGER.info(f"Starting metadata modification for file: {file}")
- temp_file = f"{file}.temp.mkv"
-
+async def get_streams(file):
cmd = [
"ffprobe",
"-hide_banner",
@@ -25,26 +22,36 @@ async def change_metadata(file, key):
if process.returncode != 0:
LOGGER.error(f"Error getting stream info: {stderr.decode().strip()}")
- return
+ return None
try:
- streams = json.loads(stdout)["streams"]
+ return json.loads(stdout)["streams"]
except KeyError:
- LOGGER.error(
- f"No streams found in the ffprobe output: {stdout.decode().strip()}",
- )
+ LOGGER.error(f"No streams found in the ffprobe output: {stdout.decode().strip()}")
+ return None
+
+
+async def change_metadata(file, key):
+ LOGGER.info(f"Starting metadata modification for file: {file}")
+ temp_file = f"{file}.temp.mkv"
+
+ streams = await get_streams(file)
+ if not streams:
return
languages = {}
for stream in streams:
stream_index = stream["index"]
- stream_type = stream["codec_type"]
if "tags" in stream and "language" in stream["tags"]:
languages[stream_index] = stream["tags"]["language"]
cmd = [
"xtra",
- "-y",
+ "-hide_banner",
+ "-loglevel",
+ "error",
+ "-progress",
+ "pipe:1",
"-i",
file,
"-map_metadata",
@@ -175,4 +182,4 @@ async def add_attachment(file, attachment_path):
os.replace(temp_file, file)
LOGGER.info(f"Photo attachment added successfully to file: {file}")
- return
+ return
\ No newline at end of file
diff --git a/bot/helper/common.py b/bot/helper/common.py
index b415a0a6c..ec441853e 100644
--- a/bot/helper/common.py
+++ b/bot/helper/common.py
@@ -48,9 +48,13 @@
create_thumb,
get_document_type,
run_ffmpeg_cmd,
+ run_metadata_cmd,
split_file,
take_ss,
+ is_mkv,
)
+from bot.helper.aeon_utils.metadata_editor import change_metadata, get_streams
+
from .mirror_leech_utils.gdrive_utils.list import GoogleDriveList
from .mirror_leech_utils.rclone_utils.list import RcloneList
from .mirror_leech_utils.status_utils.ffmpeg_status import FFmpegStatus
@@ -1312,3 +1316,141 @@ async def proceed_ffmpeg(self, dl_path, gid):
if checked:
cpu_eater_lock.release()
return dl_path
+
+ async def proceed_metadata(self, up_dir, gid):
+ key = self.metadata
+ async with task_dict_lock:
+ task_dict[self.mid] = FFmpegStatus(self, gid, "Metadata")
+ checked = False
+
+ async def process_file(file_path, key):
+ """Processes a single file to update metadata."""
+ temp_file = f"{file_path}.temp.mkv"
+ streams = await get_streams(file_path)
+ if not streams:
+ return ""
+
+ languages = {
+ stream["index"]: stream["tags"]["language"]
+ for stream in streams
+ if "tags" in stream and "language" in stream["tags"]
+ }
+
+ cmd = [
+ "xtra",
+ "-hide_banner",
+ "-loglevel",
+ "error",
+ "-progress",
+ "pipe:1",
+ "-i",
+ file_path,
+ "-map_metadata",
+ "-1",
+ "-c",
+ "copy",
+ "-metadata:s:v:0",
+ f"title={key}",
+ "-metadata",
+ f"title={key}",
+ ]
+
+ audio_index = 0
+ subtitle_index = 0
+ first_video = False
+
+ for stream in streams:
+ stream_index = stream["index"]
+ stream_type = stream["codec_type"]
+
+ if stream_type == "video":
+ if not first_video:
+ cmd.extend(["-map", f"0:{stream_index}"])
+ first_video = True
+ cmd.extend([f"-metadata:s:v:{stream_index}", f"title={key}"])
+ if stream_index in languages:
+ cmd.extend(
+ [
+ f"-metadata:s:v:{stream_index}",
+ f"language={languages[stream_index]}",
+ ]
+ )
+ elif stream_type == "audio":
+ cmd.extend(
+ [
+ "-map",
+ f"0:{stream_index}",
+ f"-metadata:s:a:{audio_index}",
+ f"title={key}",
+ ]
+ )
+ if stream_index in languages:
+ cmd.extend(
+ [
+ f"-metadata:s:a:{audio_index}",
+ f"language={languages[stream_index]}",
+ ]
+ )
+ audio_index += 1
+ elif stream_type == "subtitle":
+ codec_name = stream.get("codec_name", "unknown")
+ if codec_name in ["webvtt", "unknown"]:
+ LOGGER.warning(
+ f"Skipping unsupported subtitle metadata modification: {codec_name} for stream {stream_index}"
+ )
+ else:
+ cmd.extend(
+ [
+ "-map",
+ f"0:{stream_index}",
+ f"-metadata:s:s:{subtitle_index}",
+ f"title={key}",
+ ]
+ )
+ if stream_index in languages:
+ cmd.extend(
+ [
+ f"-metadata:s:s:{subtitle_index}",
+ f"language={languages[stream_index]}",
+ ]
+ )
+ subtitle_index += 1
+ else:
+ cmd.extend(["-map", f"0:{stream_index}"])
+
+ cmd.append(temp_file)
+ return cmd, temp_file
+
+ async def process_directory(directory, key):
+ """Processes all MKV files in a directory."""
+ for dirpath, _, files in await sync_to_async(walk, directory, topdown=False):
+ for file_ in files:
+ file_path = ospath.join(dirpath, file_)
+ if is_mkv(file_path):
+ cmd, temp_file = await process_file(file_path, key)
+ if not cmd:
+ return ""
+ if not checked:
+ await cpu_eater_lock.acquire()
+ if self.isCancelled:
+ cpu_eater_lock.release()
+ return ""
+ await run_metadata_cmd(self, cmd)
+ os.replace(temp_file, file_path)
+
+
+ if self.is_file:
+ if is_mkv(up_dir):
+ cmd, temp_file = await process_file(up_dir, key)
+ if cmd:
+ checked = True
+ await cpu_eater_lock.acquire()
+ await run_metadata_cmd(self, cmd)
+ os.replace(temp_file, up_dir)
+ cpu_eater_lock.release()
+ else:
+ await process_directory(up_dir, key)
+
+ if checked:
+ cpu_eater_lock.release()
+ return up_dir
\ No newline at end of file
diff --git a/bot/helper/ext_utils/media_utils.py b/bot/helper/ext_utils/media_utils.py
index ba21c5bc7..dc3b4787b 100644
--- a/bot/helper/ext_utils/media_utils.py
+++ b/bot/helper/ext_utils/media_utils.py
@@ -766,10 +766,10 @@ async def create_sample_video(listener, video_file, sample_duration, part_durati
return None
-async def run_ffmpeg_cmd(listener, ffmpeg, path):
+async def run_ffmpeg_cmd(listener, cmd, path):
base_name, ext = ospath.splitext(path)
dir, base_name = base_name.rsplit("/", 1)
- output_file = ffmpeg[-1]
+ output_file = cmd[-1]
if output_file != "mltb" and output_file.startswith("mltb"):
oext = ospath.splitext(output_file)[-1]
if ext == oext:
@@ -779,12 +779,12 @@ async def run_ffmpeg_cmd(listener, ffmpeg, path):
else:
base_name = f"ffmpeg.{base_name}"
output = f"{dir}/{base_name}{ext}"
- ffmpeg[-1] = output
+ cmd[-1] = output
if listener.is_cancelled:
return False
async with listener.subprocess_lock:
listener.subproc = await create_subprocess_exec(
- *ffmpeg, stdout=PIPE, stderr=PIPE
+ *cmd, stdout=PIPE, stderr=PIPE
)
code = await listener.subproc.wait()
async with listener.subprocess_lock:
@@ -805,3 +805,47 @@ async def run_ffmpeg_cmd(listener, ffmpeg, path):
if await aiopath.exists(output):
await remove(output)
return False
+
+
+async def run_metadata_cmd(listener, cmd):
+ #base_name, ext = ospath.splitext(path)
+ #dir, base_name = base_name.rsplit("/", 1)
+ #output_file = cmd[-1]
+ #if output_file != "mltb" and output_file.startswith("mltb"):
+ # oext = ospath.splitext(output_file)[-1]
+ # if ext == oext:
+ # base_name = f"ffmpeg.{base_name}"
+ # else:
+ # ext = oext
+ #else:
+ # base_name = f"ffmpeg.{base_name}"
+ # output = f"{dir}/{base_name}{ext}"
+ # cmd[-1] = output
+ if listener.is_cancelled:
+ return False
+ async with listener.subprocess_lock:
+ listener.subproc = await create_subprocess_exec(
+ *cmd, stdout=PIPE, stderr=PIPE
+ )
+ code = await listener.subproc.wait()
+ async with listener.subprocess_lock:
+ if listener.is_cancelled:
+ return False
+ if code == 0:
+ return output
+ if code == -9:
+ listener.is_cancelled = True
+ return False
+ try:
+ stderr = (await listener.subproc.stderr.read()).decode().strip()
+ except Exception:
+ stderr = "Unable to decode the error!"
+ LOGGER.error(
+ f"{stderr}. Something went wrong while running metadata cmd, mostly file requires different/specific arguments.",
+ )
+ # if await aiopath.exists(output):
+ # await remove(output)
+ return False
+
+def is_mkv(file):
+ return file.lower().endswith(".mkv")
diff --git a/bot/helper/ext_utils/status_utils.py b/bot/helper/ext_utils/status_utils.py
index 2d5fa9dfc..1f13e5c33 100644
--- a/bot/helper/ext_utils/status_utils.py
+++ b/bot/helper/ext_utils/status_utils.py
@@ -29,6 +29,7 @@ class MirrorStatus:
STATUS_SAMVID = "SamVid"
STATUS_CONVERT = "Convert"
STATUS_FFMPEG = "FFmpeg"
+ STATUS_METADATA = "Metadata"
STATUSES = {
diff --git a/bot/helper/mirror_leech_utils/status_utils/ffmpeg_status.py b/bot/helper/mirror_leech_utils/status_utils/ffmpeg_status.py
index 4bbb972a2..d26b9a890 100644
--- a/bot/helper/mirror_leech_utils/status_utils/ffmpeg_status.py
+++ b/bot/helper/mirror_leech_utils/status_utils/ffmpeg_status.py
@@ -72,7 +72,7 @@ def eta(self):
self.listener.subsize - self._processed_bytes
) / self._speed_raw
return get_readable_time(seconds)
- except:
+ except Exception:
return "-"
def status(self):
@@ -82,6 +82,8 @@ def status(self):
return MirrorStatus.STATUS_SPLIT
if self.cstatus == "Sample Video":
return MirrorStatus.STATUS_SAMVID
+ if self.cstatus == "Metadata":
+ return MirrorStatus.STATUS_METADATA
return MirrorStatus.STATUS_FFMPEG
def task(self):
diff --git a/bot/helper/mirror_leech_utils/status_utils/sevenz_status.py b/bot/helper/mirror_leech_utils/status_utils/sevenz_status.py
index df3b0b812..41e6456ec 100644
--- a/bot/helper/mirror_leech_utils/status_utils/sevenz_status.py
+++ b/bot/helper/mirror_leech_utils/status_utils/sevenz_status.py
@@ -49,7 +49,7 @@ async def _sevenz_progress(self):
self._processed_bytes = (
int(self._progress_str.strip("%")) / 100
) * self.listener.subsize
- except:
+ except Exception:
self._processed_bytes = 0
self._progress_str = "0%"
s = b""
@@ -86,7 +86,7 @@ def eta(self):
self.listener.subsize - self._processed_bytes
) / self._speed_raw()
return get_readable_time(seconds)
- except:
+ except Exception:
return "-"
def status(self):
@@ -94,9 +94,6 @@ def status(self):
return MirrorStatus.STATUS_EXTRACT
return MirrorStatus.STATUS_ARCHIVE
- def processed_bytes(self):
- return get_readable_file_size(self._processed_bytes)
-
def task(self):
return self
diff --git a/bot/modules/users_settings.py b/bot/modules/users_settings.py
index 7506575c2..9e2deaee7 100644
--- a/bot/modules/users_settings.py
+++ b/bot/modules/users_settings.py
@@ -51,6 +51,7 @@ async def get_user_settings(from_user):
index = user_dict.get("index_url", "None")
upload_paths = "Added" if user_dict.get("upload_paths", False) else "None"
ex_ex = user_dict.get("excluded_extensions", extension_filter or "None")
+ meta_msg = user_dict.get("metadata", Config.METADATA_KEY or "None")
ns_msg = "Added" if user_dict.get("name_sub", False) else "None"
ytopt = user_dict.get("yt_opt", Config.YT_DLP_OPTIONS or "None")
ffc = user_dict.get("ffmpeg_cmds", Config.FFMPEG_CMDS or "None")
@@ -103,6 +104,7 @@ async def get_user_settings(from_user):
f"userset {user_id} user_tokens {user_tokens}",
)
buttons.data_button("Excluded Extensions", f"userset {user_id} ex_ex")
+ buttons.data_button("Metadata key", f"userset {user_id} metadata_key")
buttons.data_button("Name Subtitute", f"userset {user_id} name_substitute")
buttons.data_button("YT-DLP Options", f"userset {user_id} yto")
buttons.data_button("Ffmpeg Cmds", f"userset {user_id} ffc")
@@ -132,6 +134,7 @@ async def get_user_settings(from_user):
f"Default Package is {du}\n"
f"Upload using {tr} token/config\n"
f"Name substitution is {ns_msg}\n"
+ f"Metadata key is {meta_msg}\n"
f"Excluded Extensions is {ex_ex}
\n"
f"YT-DLP Options is {escape(ytopt)}
\n"
f"FFMPEG Commands is {ffc}
"
@@ -327,6 +330,7 @@ async def edit_user_settings(client, query):
"index_url",
"excluded_extensions",
"name_sub",
+ "metadata",
"thumb_layout",
"ffmpeg_cmds",
"session_string",
@@ -753,6 +757,20 @@ async def edit_user_settings(client, query):
)
pfunc = partial(set_option, pre_event=query, option="name_sub")
await event_handler(client, query, pfunc)
+ elif data[2] == "metadata_key":
+ await query.answer()
+ buttons = ButtonMaker()
+ if user_dict.get("metadata", False):
+ buttons.data_button("Remove Metadata key", f"userset {user_id} metadata")
+ buttons.data_button("Back", f"userset {user_id} back")
+ buttons.data_button("Close", f"userset {user_id} close")
+ emsg = "Metadata will change MKV video files including all audio, streams, and subtitle titles."
+ emsg += (
+ f"Your Current Value is {user_dict.get('metadata') or 'not added yet!'}"
+ )
+ await edit_message(message, emsg, buttons.build_menu(1), markdown=True)
+ pfunc = partial(set_option, pre_event=query, option="metadata")
+ await event_handler(client, query, pfunc)
elif data[2] in ["gd", "rc"]:
await query.answer()
du = "rc" if data[2] == "gd" else "gd"
diff --git a/config_sample.py b/config_sample.py
index 26039e14e..15e70a661 100644
--- a/config_sample.py
+++ b/config_sample.py
@@ -30,6 +30,7 @@
PAID_CHANNEL_ID = 0
PAID_CHANNEL_LINK = ""
SET_COMMANDS = True
+METADATA_KEY = ""
# GDrive Tools
GDRIVE_ID = ""