
Recherche avancée
Autres articles (40)
-
Publier sur MédiaSpip
13 juin 2013Puis-je poster des contenus à partir d’une tablette Ipad ?
Oui, si votre Médiaspip installé est à la version 0.2 ou supérieure. Contacter au besoin l’administrateur de votre MédiaSpip pour le savoir -
Ajouter notes et légendes aux images
7 février 2011, parPour pouvoir ajouter notes et légendes aux images, la première étape est d’installer le plugin "Légendes".
Une fois le plugin activé, vous pouvez le configurer dans l’espace de configuration afin de modifier les droits de création / modification et de suppression des notes. Par défaut seuls les administrateurs du site peuvent ajouter des notes aux images.
Modification lors de l’ajout d’un média
Lors de l’ajout d’un média de type "image" un nouveau bouton apparait au dessus de la prévisualisation (...) -
Participer à sa traduction
10 avril 2011Vous pouvez nous aider à améliorer les locutions utilisées dans le logiciel ou à traduire celui-ci dans n’importe qu’elle nouvelle langue permettant sa diffusion à de nouvelles communautés linguistiques.
Pour ce faire, on utilise l’interface de traduction de SPIP où l’ensemble des modules de langue de MediaSPIP sont à disposition. ll vous suffit de vous inscrire sur la liste de discussion des traducteurs pour demander plus d’informations.
Actuellement MediaSPIP n’est disponible qu’en français et (...)
Sur d’autres sites (7039)
-
FFMPEG My stitched frames colors looks very different from my original video, causing my video to not be able to stitch it back properly
26 mai 2024, par Wer WerI am trying to extract some frames off my video to do some form of steganography. I accidentally used a 120fps video, causing the files to be too big when i extract every single frame. To fix this, I decided to calculate how many frames is needed to hide the bits (LSB replacement for every 8 bit) and then extract only certain amount of frames. This means


- 

- if i only need 1 frame, ill extract frame0.png
- ill remove frame0 from the original video
- encode my data into frame0.png
- stitch frame0 back into ffv1 video
- concatenate frame0 video to the rest of the video, frame0 video in front.












I can do extraction and remove frame0 from the video. However, when looking at frame0.mkv and the original.mkv, i realised the colors seemed to be different.
Frame0.mkv
original.mkv


This causes a glitch during the stitching of videos together, where the end of the video has some corrupted pixels. Not only that, it stops the video at where frame0 ends. I think those corrupted pixels were supposed to be original.mkv pixels, but they did not concatenate properly.
results.mkv


I use an ffmpeg sub command to extract frames and stitch them


def split_into_frames(self, ffv1_video, hidden_text_length):
 if not ffv1_video.endswith(".mkv"):
 ffv1_video += ".mkv"

 ffv1_video_path = os.path.join(self.here, ffv1_video)
 ffv1_video = cv2.VideoCapture(ffv1_video_path)

 currentframe = 0
 total_frame_bits = 0
 frames_to_remove = []

 while True:
 ret, frame = ffv1_video.read()
 if ret:
 name = os.path.join(self.here, "data", f"frame{currentframe}.png")
 print("Creating..." + name)
 cv2.imwrite(name, frame)

 current_frame_path = os.path.join(
 self.here, "data", f"frame{currentframe}.png"
 )

 if os.path.exists(current_frame_path):
 binary_data = self.read_frame_binary(current_frame_path)

 if (total_frame_bits // 8) >= hidden_text_length:
 print("Complete")
 break
 total_frame_bits += len(binary_data)
 frames_to_remove.append(currentframe)
 currentframe += 1
 else:
 print("Complete")
 break

 ffv1_video.release()

 # Remove the extracted frames from the original video
 self.remove_frames_from_video(ffv1_video_path, frames_to_remove)




This code splits the video into the required number of frames. It checks if the total amount of frame bits is enough to encode the hidden text


def remove_frames_from_video(self, input_video, frames_to_remove):
 if not input_video.endswith(".mkv"):
 input_video += ".mkv"

 input_video_path = os.path.join(self.here, input_video)

 # Create a filter string to exclude specific frames
 filter_str = (
 "select='not("
 + "+".join([f"eq(n\,{frame})" for frame in frames_to_remove])
 + ")',setpts=N/FRAME_RATE/TB"
 )

 # Temporary output video path
 output_video_path = os.path.join(self.here, "temp_output.mkv")

 command = [
 "ffmpeg",
 "-y",
 "-i",
 input_video_path,
 "-vf",
 filter_str,
 "-c:v",
 "ffv1",
 "-level",
 "3",
 "-coder",
 "1",
 "-context",
 "1",
 "-g",
 "1",
 "-slices",
 "4",
 "-slicecrc",
 "1",
 "-an", # Remove audio
 output_video_path,
 ]

 try:
 subprocess.run(command, check=True)
 print(f"Frames removed. Temporary video created at {output_video_path}")

 # Replace the original video with the new video
 os.replace(output_video_path, input_video_path)
 print(f"Original video replaced with updated video at {input_video_path}")

 # Re-add the trimmed audio to the new video
 self.trim_audio_and_add_to_video(input_video_path, frames_to_remove)
 except subprocess.CalledProcessError as e:
 print(f"An error occurred: {e}")
 if os.path.exists(output_video_path):
 os.remove(output_video_path)

def trim_audio_and_add_to_video(self, video_path, frames_to_remove):
 # Calculate the new duration based on the remaining frames
 fps = 60 # Assuming the framerate is 60 fps
 total_frames_removed = len(frames_to_remove)
 original_duration = self.get_video_duration(video_path)
 new_duration = original_duration - (total_frames_removed / fps)

 # Extract and trim the audio
 audio_path = os.path.join(self.here, "trimmed_audio.aac")
 command_extract_trim = [
 "ffmpeg",
 "-y",
 "-i",
 video_path,
 "-t",
 str(new_duration),
 "-q:a",
 "0",
 "-map",
 "a",
 audio_path,
 ]
 try:
 subprocess.run(command_extract_trim, check=True)
 print(f"Audio successfully trimmed and extracted to {audio_path}")

 # Add the trimmed audio back to the video
 final_video_path = video_path.replace(".mkv", "_final.mkv")
 command_add_audio = [
 "ffmpeg",
 "-y",
 "-i",
 video_path,
 "-i",
 audio_path,
 "-c:v",
 "copy",
 "-c:a",
 "aac",
 "-strict",
 "experimental",
 final_video_path,
 ]
 subprocess.run(command_add_audio, check=True)
 print(f"Final video with trimmed audio created at {final_video_path}")

 # Replace the original video with the final video
 os.replace(final_video_path, video_path)
 print(f"Original video replaced with final video at {video_path}")
 except subprocess.CalledProcessError as e:
 print(f"An error occurred: {e}")

def get_video_duration(self, video_path):
 command = [
 "ffprobe",
 "-v",
 "error",
 "-show_entries",
 "format=duration",
 "-of",
 "default=noprint_wrappers=1:nokey=1",
 video_path,
 ]
 try:
 result = subprocess.run(
 command, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE
 )
 duration = float(result.stdout.decode().strip())
 return duration
 except subprocess.CalledProcessError as e:
 print(f"An error occurred while getting video duration: {e}")
 return 0.0



here ill remove all the frames that has been extracted from the video


def stitch_frames_to_video(self, ffv1_video, framerate=60):
 # this command is another ffmpeg subcommand.
 # it takes every single frame from data1 directory and stitch it back into a ffv1 video
 if not ffv1_video.endswith(".mkv"):
 ffv1_video += ".mkv"

 output_video_path = os.path.join(self.here, ffv1_video)

 command = [
 "ffmpeg",
 "-y",
 "-framerate",
 str(framerate),
 "-i",
 os.path.join(self.frames_directory, "frame%d.png"),
 "-c:v",
 "ffv1",
 "-level",
 "3",
 "-coder",
 "1",
 "-context",
 "1",
 "-g",
 "1",
 "-slices",
 "4",
 "-slicecrc",
 "1",
 output_video_path,
 ]

 try:
 subprocess.run(command, check=True)
 print(f"Video successfully created at {output_video_path}")
 except subprocess.CalledProcessError as e:
 print(f"An error occurred: {e}")



after encoding the frames, ill try to stitch the frames back into ffv1 video


def concatenate_videos(self, video1_path, video2_path, output_path):
 if not video1_path.endswith(".mkv"):
 video1_path += ".mkv"
 if not video2_path.endswith(".mkv"):
 video2_path += ".mkv"
 if not output_path.endswith(".mkv"):
 output_path += ".mkv"

 video1_path = os.path.join(self.here, video1_path)
 video2_path = os.path.join(self.here, video2_path)
 output_video_path = os.path.join(self.here, output_path)

 # Create a text file with the paths of the videos to concatenate
 concat_list_path = os.path.join(self.here, "concat_list.txt")
 with open(concat_list_path, "w") as f:
 f.write(f"file '{video1_path}'\n")
 f.write(f"file '{video2_path}'\n")

 command = [
 "ffmpeg",
 "-y",
 "-f",
 "concat",
 "-safe",
 "0",
 "-i",
 concat_list_path,
 "-c",
 "copy",
 output_video_path,
 ]

 try:
 subprocess.run(command, check=True)
 print(f"Videos successfully concatenated into {output_video_path}")
 os.remove(concat_list_path) # Clean up the temporary file
 except subprocess.CalledProcessError as e:
 print(f"An error occurred: {e}")



now i try to concatenate the frames video with the original video, but it is corrupting as the colors are different.


this code does the other processing by removing all the extracted frames from the video, as well as trimming the audio (but i think ill be removing the audio trimming as i realised it is not needed at all)


I think its because .png frames will lose colors when they get extracted out. The only work around I know is to extract every single frame. But this causes the program to run too long as for a 12 second video, I will extract 700++ frames. Is there a way to fix this ?


my full code


import json
import os
import shutil
import magic
import ffmpeg
import cv2
import numpy as np
import subprocess
from PIL import Image
import glob


import json
import os
import shutil
import magic
import ffmpeg
import cv2
import numpy as np
import subprocess
from PIL import Image
import glob


class FFV1Steganography:
 def __init__(self):
 self.here = os.path.dirname(os.path.abspath(__file__))

 # Create a folder to save the frames
 self.frames_directory = os.path.join(self.here, "data")
 try:
 if not os.path.exists(self.frames_directory):
 os.makedirs(self.frames_directory)
 except OSError:
 print("Error: Creating directory of data")

 def read_hidden_text(self, filename):
 file_path_txt = os.path.join(self.here, filename)
 # Read the content of the file in binary mode
 with open(file_path_txt, "rb") as f:
 hidden_text_content = f.read()
 return hidden_text_content

 def calculate_length_of_hidden_text(self, filename):
 hidden_text_content = self.read_hidden_text(filename)
 # Convert each byte to its binary representation and join them
 return len("".join(format(byte, "08b") for byte in hidden_text_content))

 def find_raw_video_file(self, filename):
 file_extensions = [".mp4", ".mkv", ".avi"]
 for ext in file_extensions:
 file_path = os.path.join(self.here, filename + ext)
 if os.path.isfile(file_path):
 return file_path
 return None

 def convert_video(self, input_file, ffv1_video):
 # this function is the same as running this command line
 # ffmpeg -i video.mp4 -t 12 -c:v ffv1 -level 3 -coder 1 -context 1 -g 1 -slices 4 -slicecrc 1 -c:a copy output.mkv

 # in order to run any ffmpeg subprocess, you have to have ffmpeg installed into the computer.
 # https://ffmpeg.org/download.html

 # WARNING:
 # the ffmpeg you should download is not the same as the ffmpeg library for python.
 # you need to download the exe from the link above, then add ffmpeg bin directory to system variables
 output_file = os.path.join(self.here, ffv1_video)

 if not output_file.endswith(".mkv"):
 output_file += ".mkv"

 command = [
 "ffmpeg",
 "-y",
 "-i",
 input_file,
 "-t",
 "12",
 "-c:v",
 "ffv1",
 "-level",
 "3",
 "-coder",
 "1",
 "-context",
 "1",
 "-g",
 "1",
 "-slices",
 "4",
 "-slicecrc",
 "1",
 "-c:a",
 "copy",
 output_file,
 ]

 try:
 subprocess.run(command, check=True)
 print(f"Conversion successful: {output_file}")
 return output_file
 except subprocess.CalledProcessError as e:
 print(f"Error during conversion: {e}")

 def extract_audio(self, ffv1_video, audio_path):
 # Ensure the audio output file has the correct extension
 if not audio_path.endswith(".aac"):
 audio_path += ".aac"

 # Full path to the extracted audio file
 extracted_audio = os.path.join(self.here, audio_path)

 if not ffv1_video.endswith(".mkv"):
 ffv1_video += ".mkv"

 command = [
 "ffmpeg",
 "-i",
 ffv1_video,
 "-q:a",
 "0",
 "-map",
 "a",
 extracted_audio,
 ]
 try:
 result = subprocess.run(
 command, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE
 )
 print(f"Audio successfully extracted to {extracted_audio}")
 print(result.stdout.decode())
 print(result.stderr.decode())
 except subprocess.CalledProcessError as e:
 print(f"An error occurred: {e}")
 print(e.stdout.decode())
 print(e.stderr.decode())

 def read_frame_binary(self, frame_path):
 # Open the image and convert to binary
 with open(frame_path, "rb") as f:
 binary_content = f.read()
 binary_string = "".join(format(byte, "08b") for byte in binary_content)
 return binary_string

 def remove_frames_from_video(self, input_video, frames_to_remove):
 if not input_video.endswith(".mkv"):
 input_video += ".mkv"

 input_video_path = os.path.join(self.here, input_video)

 # Create a filter string to exclude specific frames
 filter_str = (
 "select='not("
 + "+".join([f"eq(n\,{frame})" for frame in frames_to_remove])
 + ")',setpts=N/FRAME_RATE/TB"
 )

 # Temporary output video path
 output_video_path = os.path.join(self.here, "temp_output.mkv")

 command = [
 "ffmpeg",
 "-y",
 "-i",
 input_video_path,
 "-vf",
 filter_str,
 "-c:v",
 "ffv1",
 "-level",
 "3",
 "-coder",
 "1",
 "-context",
 "1",
 "-g",
 "1",
 "-slices",
 "4",
 "-slicecrc",
 "1",
 "-an", # Remove audio
 output_video_path,
 ]

 try:
 subprocess.run(command, check=True)
 print(f"Frames removed. Temporary video created at {output_video_path}")

 # Replace the original video with the new video
 os.replace(output_video_path, input_video_path)
 print(f"Original video replaced with updated video at {input_video_path}")

 # Re-add the trimmed audio to the new video
 self.trim_audio_and_add_to_video(input_video_path, frames_to_remove)
 except subprocess.CalledProcessError as e:
 print(f"An error occurred: {e}")
 if os.path.exists(output_video_path):
 os.remove(output_video_path)

 def trim_audio_and_add_to_video(self, video_path, frames_to_remove):
 # Calculate the new duration based on the remaining frames
 fps = 60 # Assuming the framerate is 60 fps
 total_frames_removed = len(frames_to_remove)
 original_duration = self.get_video_duration(video_path)
 new_duration = original_duration - (total_frames_removed / fps)

 # Extract and trim the audio
 audio_path = os.path.join(self.here, "trimmed_audio.aac")
 command_extract_trim = [
 "ffmpeg",
 "-y",
 "-i",
 video_path,
 "-t",
 str(new_duration),
 "-q:a",
 "0",
 "-map",
 "a",
 audio_path,
 ]
 try:
 subprocess.run(command_extract_trim, check=True)
 print(f"Audio successfully trimmed and extracted to {audio_path}")

 # Add the trimmed audio back to the video
 final_video_path = video_path.replace(".mkv", "_final.mkv")
 command_add_audio = [
 "ffmpeg",
 "-y",
 "-i",
 video_path,
 "-i",
 audio_path,
 "-c:v",
 "copy",
 "-c:a",
 "aac",
 "-strict",
 "experimental",
 final_video_path,
 ]
 subprocess.run(command_add_audio, check=True)
 print(f"Final video with trimmed audio created at {final_video_path}")

 # Replace the original video with the final video
 os.replace(final_video_path, video_path)
 print(f"Original video replaced with final video at {video_path}")
 except subprocess.CalledProcessError as e:
 print(f"An error occurred: {e}")

 def get_video_duration(self, video_path):
 command = [
 "ffprobe",
 "-v",
 "error",
 "-show_entries",
 "format=duration",
 "-of",
 "default=noprint_wrappers=1:nokey=1",
 video_path,
 ]
 try:
 result = subprocess.run(
 command, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE
 )
 duration = float(result.stdout.decode().strip())
 return duration
 except subprocess.CalledProcessError as e:
 print(f"An error occurred while getting video duration: {e}")
 return 0.0

 def split_into_frames(self, ffv1_video, hidden_text_length):
 if not ffv1_video.endswith(".mkv"):
 ffv1_video += ".mkv"

 ffv1_video_path = os.path.join(self.here, ffv1_video)
 ffv1_video = cv2.VideoCapture(ffv1_video_path)

 currentframe = 0
 total_frame_bits = 0
 frames_to_remove = []

 while True:
 ret, frame = ffv1_video.read()
 if ret:
 name = os.path.join(self.here, "data", f"frame{currentframe}.png")
 print("Creating..." + name)
 cv2.imwrite(name, frame)

 current_frame_path = os.path.join(
 self.here, "data", f"frame{currentframe}.png"
 )

 if os.path.exists(current_frame_path):
 binary_data = self.read_frame_binary(current_frame_path)

 if (total_frame_bits // 8) >= hidden_text_length:
 print("Complete")
 break
 total_frame_bits += len(binary_data)
 frames_to_remove.append(currentframe)
 currentframe += 1
 else:
 print("Complete")
 break

 ffv1_video.release()

 # Remove the extracted frames from the original video
 self.remove_frames_from_video(ffv1_video_path, frames_to_remove)

 def stitch_frames_to_video(self, ffv1_video, framerate=60):
 # this command is another ffmpeg subcommand.
 # it takes every single frame from data1 directory and stitch it back into a ffv1 video
 if not ffv1_video.endswith(".mkv"):
 ffv1_video += ".mkv"

 output_video_path = os.path.join(self.here, ffv1_video)

 command = [
 "ffmpeg",
 "-y",
 "-framerate",
 str(framerate),
 "-i",
 os.path.join(self.frames_directory, "frame%d.png"),
 "-c:v",
 "ffv1",
 "-level",
 "3",
 "-coder",
 "1",
 "-context",
 "1",
 "-g",
 "1",
 "-slices",
 "4",
 "-slicecrc",
 "1",
 output_video_path,
 ]

 try:
 subprocess.run(command, check=True)
 print(f"Video successfully created at {output_video_path}")
 except subprocess.CalledProcessError as e:
 print(f"An error occurred: {e}")

 def add_audio_to_video(self, encoded_video, audio_path, final_video):
 # the audio will be lost during splitting and restitching.
 # that is why previously we separated the audio from video and saved it as aac.
 # now, we can put the audio back into the video, again using ffmpeg subcommand.

 if not encoded_video.endswith(".mkv"):
 encoded_video += ".mkv"

 if not final_video.endswith(".mkv"):
 final_video += ".mkv"

 if not audio_path.endswith(".aac"):
 audio_path += ".aac"

 final_output_path = os.path.join(self.here, final_video)

 command = [
 "ffmpeg",
 "-y",
 "-i",
 os.path.join(self.here, encoded_video),
 "-i",
 os.path.join(self.here, audio_path),
 "-c:v",
 "copy",
 "-c:a",
 "aac",
 "-strict",
 "experimental",
 final_output_path,
 ]
 try:
 subprocess.run(command, check=True)
 print(f"Final video with audio created at {final_output_path}")
 except subprocess.CalledProcessError as e:
 print(f"An error occurred: {e}")

 def concatenate_videos(self, video1_path, video2_path, output_path):
 if not video1_path.endswith(".mkv"):
 video1_path += ".mkv"
 if not video2_path.endswith(".mkv"):
 video2_path += ".mkv"
 if not output_path.endswith(".mkv"):
 output_path += ".mkv"

 video1_path = os.path.join(self.here, video1_path)
 video2_path = os.path.join(self.here, video2_path)
 output_video_path = os.path.join(self.here, output_path)

 # Create a text file with the paths of the videos to concatenate
 concat_list_path = os.path.join(self.here, "concat_list.txt")
 with open(concat_list_path, "w") as f:
 f.write(f"file '{video1_path}'\n")
 f.write(f"file '{video2_path}'\n")

 command = [
 "ffmpeg",
 "-y",
 "-f",
 "concat",
 "-safe",
 "0",
 "-i",
 concat_list_path,
 "-c",
 "copy",
 output_video_path,
 ]

 try:
 subprocess.run(command, check=True)
 print(f"Videos successfully concatenated into {output_video_path}")
 os.remove(concat_list_path) # Clean up the temporary file
 except subprocess.CalledProcessError as e:
 print(f"An error occurred: {e}")

 def cleanup(self, files_to_delete):
 # Delete specified files
 for file in files_to_delete:
 file_path = os.path.join(self.here, file)
 if os.path.exists(file_path):
 os.remove(file_path)
 print(f"Deleted file: {file_path}")
 else:
 print(f"File not found: {file_path}")

 # Delete the frames directory and its contents
 if os.path.exists(self.frames_directory):
 shutil.rmtree(self.frames_directory)
 print(f"Deleted directory and its contents: {self.frames_directory}")
 else:
 print(f"Directory not found: {self.frames_directory}")


if __name__ == "__main__":
 stego = FFV1Steganography()

 # original video (mp4,mkv,avi)
 original_video = "video"
 # converted ffv1 video
 ffv1_video = "output"
 # extracted audio
 extracted_audio = "audio"
 # encoded video without sound
 encoded_video = "encoded"
 # final result video, encoded, with sound
 final_video = "result"

 # region --hidden text processing --
 hidden_text = stego.read_hidden_text("hiddentext.txt")
 hidden_text_length = stego.calculate_length_of_hidden_text("hiddentext.txt")
 # endregion

 # region -- raw video locating --
 raw_video_file = stego.find_raw_video_file(original_video)
 if raw_video_file:
 print(f"Found video file: {raw_video_file}")
 else:
 print("video.mp4 not found.")
 # endregion

 # region -- video processing INPUT--
 # converted_video_file = stego.convert_video(raw_video_file, ffv1_video)
 # if converted_video_file and os.path.exists(converted_video_file):
 # stego.extract_audio(converted_video_file, extracted_audio)
 # else:
 # print(f"Conversion failed: {converted_video_file} not found.")

 # stego.split_into_frames(ffv1_video, hidden_text_length * 50000)
 # endregion

 # region -- video processing RESULT --
 # stego.stitch_frames_to_video(encoded_video)
 stego.concatenate_videos(encoded_video, ffv1_video, final_video)
 # stego.add_audio_to_video(final_video, extracted_audio, final_video)
 # endregion

 # region -- cleanup --
 files_to_delete = [
 extracted_audio + ".aac",
 encoded_video + ".mkv",
 ffv1_video + ".mkv",
 ]

 stego.cleanup(files_to_delete)
 # endregion








-
FFmpeg fails to draw text
6 avril 2024, par Edoardo BalducciI've rarely used ffmpeg before, so, sorry If the question is too dumb.
I have a problem adding a text layer to a video frame using ffmpeg.


This is my current code :


import subprocess
from PyQt5.QtGui import QPixmap, QImage
from PyQt5.QtWidgets import QLabel

class VideoThumbnailLabel(QLabel):
 def __init__(self, file_path, *args, **kwargs):
 super().__init__(*args, **kwargs)
 self.video = video
 video_duration = self.get_video_duration(file_path)
 thumbnail_path = self.get_thumbnail(file_path, video_duration)
 if thumbnail_path:
 self.setPixmap(QPixmap(thumbnail_path).scaled(160, 90, Qt.KeepAspectRatio))
 self.setToolTip(f"{video.title}\n{video.description}")

 def get_video_duration(self, video_path):
 """Returns the duration of the video in seconds."""
 command = [
 'ffprobe', '-v', 'error', '-show_entries',
 'format=duration', '-of',
 'default=noprint_wrappers=1:nokey=1', video_path
 ]
 try:
 result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
 if result.returncode != 0:
 print(f"ffprobe error: {result.stderr}")
 return 0
 duration = float(result.stdout)
 return int(duration) # Returning duration as an integer for simplicity
 except Exception as e:
 print(f"Error getting video duration: {e}")
 return 0

 def get_thumbnail(self, video_path, duration):
 """Generates a thumbnail with the video duration overlaid."""
 output_path = "thumbnail.jpg" # Temporary thumbnail file
 duration_str = f"{duration // 3600:02d}:{(duration % 3600) // 60:02d}:{duration % 60:02d}"
 command = [
 'ffmpeg', '-i', video_path,
 '-ss', '00:00:01', # Time to take the screenshot
 '-frames:v', '1', # Number of frames to capture
 '-vf', f"drawtext=text='Duration: {duration_str}':x=10:y=10:fontsize=24:fontcolor=white",
 '-q:v', '2', # Output quality
 '-y', # Overwrite output files without asking
 output_path
 ]
 try:
 result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
 if result.returncode != 0:
 print(f"ffmpeg error: {result.stderr}")
 return None
 return output_path
 except Exception as e:
 print(f"Error generating thumbnail with duration: {e}")
 return None



and it is used like this :


for i, video in enumerate(self.videos):
 video_widget = VideoThumbnailLabel(video.file)
 video_widget.mousePressEvent = lambda event, v=video: self.onThumbnailClick(
 v
 )
 self.layout.addWidget(video_widget, i // 3, i % 3)



I'm facing a problem where I am not able to get the thumbnail if I try to add the duration (I've tested it without the draw filter and worked fine)


I get this error (from the
result.returncode
) that I'm not able to comprehend :

ffmpeg error: b"ffmpeg version 6.1.1 Copyright (c) 2000-2023 the FFmpeg developers\n built with Apple clang version 15.0.0 (clang-1500.1.0.2.5)\n configuration: --prefix=/opt/homebrew/Cellar/ffmpeg/6.1.1_4 --enable-shared --enable-pthreads --enable-version3 --cc=clang --host-cflags= --host-ldflags='-Wl,-ld_classic' --enable-ffplay --enable-gnutls --enable-gpl --enable-libaom --enable-libaribb24 --enable-libbluray --enable-libdav1d --enable-libharfbuzz --enable-libjxl --enable-libmp3lame --enable-libopus --enable-librav1e --enable-librist --enable-librubberband --enable-libsnappy --enable-libsrt --enable-libssh --enable-libsvtav1 --enable-libtesseract --enable-libtheora --enable-libvidstab --enable-libvmaf --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libxvid --enable-lzma --enable-libfontconfig --enable-libfreetype --enable-frei0r --enable-libass --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libopenvino --enable-libspeex --enable-libsoxr --enable-libzmq --enable-libzimg --disable-libjack --disable-indev=jack --enable-videotoolbox --enable-audiotoolbox --enable-neon\n libavutil 58. 29.100 / 58. 29.100\n libavcodec 60. 31.102 / 60. 31.102\n libavformat 60. 16.100 / 60. 16.100\n libavdevice 60. 3.100 / 60. 3.100\n libavfilter 9. 12.100 / 9. 12.100\n libswscale 7. 5.100 / 7. 5.100\n libswresample 4. 12.100 / 4. 12.100\n libpostproc 57. 3.100 / 57. 3.100\nInput #0, mov,mp4,m4a,3gp,3g2,mj2, from '/Users/edoardo/Projects/work/test/BigBuckBunny.mp4':\n Metadata:\n major_brand : mp42\n minor_version : 0\n compatible_brands: isomavc1mp42\n creation_time : 2010-01-10T08:29:06.000000Z\n Duration: 00:09:56.47, start: 0.000000, bitrate: 2119 kb/s\n Stream #0:0[0x1](und): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 125 kb/s (default)\n Metadata:\n creation_time : 2010-01-10T08:29:06.000000Z\n handler_name : (C) 2007 Google Inc. v08.13.2007.\n vendor_id : [0][0][0][0]\n Stream #0:1[0x2](und): Video: h264 (High) (avc1 / 0x31637661), yuv420p(progressive), 1280x720 [SAR 1:1 DAR 16:9], 1991 kb/s, 24 fps, 24 tbr, 24k tbn (default)\n Metadata:\n creation_time : 2010-01-10T08:29:06.000000Z\n handler_name : (C) 2007 Google Inc. v08.13.2007.\n vendor_id : [0][0][0][0]\n[Parsed_drawtext_0 @ 0x60000331cd10] Both text and text file provided. Please provide only one\n[AVFilterGraph @ 0x600002018000] Error initializing filters\n[vost#0:0/mjpeg @ 0x13ce0c7e0] Error initializing a simple filtergraph\nError opening output file thumbnail.jpg.\nError opening output files: Invalid argument\n"



I've installed both
ffmpeg
andffmprobe
in my machine :

┌(edoardomacbook-air)-[~/Projects/work/tests-scripts] 
└─ $ ffmpeg -version && ffprobe -version 2 ⚙ 
ffmpeg version 6.1.1 Copyright (c) 2000-2023 the FFmpeg developers
built with Apple clang version 15.0.0 (clang-1500.1.0.2.5)
configuration: --prefix=/opt/homebrew/Cellar/ffmpeg/6.1.1_4 --enable-shared --enable-pthreads --enable-version3 --cc=clang --host-cflags= --host-ldflags='-Wl,-ld_classic' --enable-ffplay --enable-gnutls --enable-gpl --enable-libaom --enable-libaribb24 --enable-libbluray --enable-libdav1d --enable-libharfbuzz --enable-libjxl --enable-libmp3lame --enable-libopus --enable-librav1e --enable-librist --enable-librubberband --enable-libsnappy --enable-libsrt --enable-libssh --enable-libsvtav1 --enable-libtesseract --enable-libtheora --enable-libvidstab --enable-libvmaf --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libxvid --enable-lzma --enable-libfontconfig --enable-libfreetype --enable-frei0r --enable-libass --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libopenvino --enable-libspeex --enable-libsoxr --enable-libzmq --enable-libzimg --disable-libjack --disable-indev=jack --enable-videotoolbox --enable-audiotoolbox --enable-neon
libavutil 58. 29.100 / 58. 29.100
libavcodec 60. 31.102 / 60. 31.102
libavformat 60. 16.100 / 60. 16.100
libavdevice 60. 3.100 / 60. 3.100
libavfilter 9. 12.100 / 9. 12.100
libswscale 7. 5.100 / 7. 5.100
libswresample 4. 12.100 / 4. 12.100
libpostproc 57. 3.100 / 57. 3.100
ffprobe version 6.1.1 Copyright (c) 2007-2023 the FFmpeg developers
built with Apple clang version 15.0.0 (clang-1500.1.0.2.5)
configuration: --prefix=/opt/homebrew/Cellar/ffmpeg/6.1.1_4 --enable-shared --enable-pthreads --enable-version3 --cc=clang --host-cflags= --host-ldflags='-Wl,-ld_classic' --enable-ffplay --enable-gnutls --enable-gpl --enable-libaom --enable-libaribb24 --enable-libbluray --enable-libdav1d --enable-libharfbuzz --enable-libjxl --enable-libmp3lame --enable-libopus --enable-librav1e --enable-librist --enable-librubberband --enable-libsnappy --enable-libsrt --enable-libssh --enable-libsvtav1 --enable-libtesseract --enable-libtheora --enable-libvidstab --enable-libvmaf --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libxvid --enable-lzma --enable-libfontconfig --enable-libfreetype --enable-frei0r --enable-libass --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libopenvino --enable-libspeex --enable-libsoxr --enable-libzmq --enable-libzimg --disable-libjack --disable-indev=jack --enable-videotoolbox --enable-audiotoolbox --enable-neon
libavutil 58. 29.100 / 58. 29.100
libavcodec 60. 31.102 / 60. 31.102
libavformat 60. 16.100 / 60. 16.100
libavdevice 60. 3.100 / 60. 3.100
libavfilter 9. 12.100 / 9. 12.100
libswscale 7. 5.100 / 7. 5.100
libswresample 4. 12.100 / 4. 12.100
libpostproc 57. 3.100 / 57. 3.100



Does anyone see the problem ?



P.S. : I know that I havent provided a minimal reproducible example, but since I don't know where the problem lies I didn't want to exclude anything


-
Ffmpeg encoding too slow
8 mars 2024, par Marc CuadrasI'm using a Python script to encode mp4 videos in various qualities, in my case 360 +720 +1080.
Ffmpeg encoding videos very slow, 1 video taking 2 hours or more, I'm using good dedicated server from hetzner (ryzen 5 3600 + 64gm ram), any suggestion to improve speed will be really appreciated


import os
import glob
from datetime import datetime
import subprocess
from rich import print
import time
import sys
import pymysql as sql
import move

MYSQL_HOST = "127.0.0.1"
MYSQL_USER = ""
MYSQL_PASSWORD = ""
MYSQL_DB = ""

BASE_PATH = ''
UPLOAD_PATH = os.path.join(BASE_PATH, 'upload')
UPLOAD_PATH2 = os.path.join(BASE_PATH, 'upload2')
VIDEO_PATH = os.path.join(BASE_PATH, 'video')
LOGO_PATH = os.path.join(BASE_PATH, 'logo.png')
ERROR_PATH = os.path.join(BASE_PATH, 'error')
RCLONE_PATH = os.path.join(BASE_PATH, 'rclone')
WAIT = 60
VIDEO_LENGTH = 10
MOVE_CMD = "screen -dmS move python3 move.py --folder {}"

global current_video_id

db = sql.connect(
 host=MYSQL_HOST,
 user=MYSQL_USER,
 password=MYSQL_PASSWORD,
 database=MYSQL_DB
 )

def executedb(query):
 cursor = db.cursor()
 cursor.execute(query)
 db.commit()

def time_now():
 now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
 return now

def write_log(msg):
 log_msg = f"{time_now()}: {msg}"
 with open('log.txt','a') as f:
 f.write(log_msg)
 f.write('\n')

def change_extension(files):
 for file in files:
 os.rename(file, file.split('.')[0] + '.mkv')

def change_all_videos_extension():
 # changing all the vidoe's extension to mkv format
 MKV_files = glob.glob('*.MKV')
 AVI_files = glob.glob('*.AVI')
 avi_files = glob.glob('*.avi')
 MP4_files = glob.glob('*.MK4')
 mp4_files = glob.glob('*.mp4')
 webm_files = glob.glob('*.webm')
 ts_files = glob.glob('*.ts')

 if len(avi_files) > 0:
 print(f'[{time_now()}] Converting avi videos to mkv format')
 change_extension(avi_files)

 if len(MKV_files) > 0:
 print(f'[{time_now()}] Converting MKV videos to mkv format')
 change_extension(MKV_files)

 if len(AVI_files) > 0:
 print(f'[{time_now()}] Converting AVI videos to mkv format')
 change_extension(AVI_files)

 if len(MP4_files) > 0:
 print(f'[{time_now()}] Converting MP4 videos to mkv format')
 change_extension(MP4_files)

 if len(mp4_files) > 0:
 print(f'[{time_now()}] Converting mp4 videos to mkv format')
 change_extension(mp4_files)

 if len(webm_files) > 0:
 print(f'[{time_now()}] Converting webm videos to mkv format')
 change_extension(webm_files)

 if len(ts_files) > 0:
 print(f'[{time_now()}] Converting ts videos to mkv format')
 change_extension(ts_files)
 
def encode_480(filename):
 FILENAME_PATH = filename
 newname = filename.split('.')[0]
 newname_path = os.path.join(VIDEO_PATH, newname)

 poster_cmd = f'ffmpeg -y -ss 00:00:10 -i {FILENAME_PATH} -vframes 1 -q:v 2 poster.jpg'
 os.system(poster_cmd)
 
 if not os.path.exists(newname_path):
 os.mkdir(newname_path)

 os.replace('poster.jpg', os.path.join(newname_path, 'poster.jpg'))
 

 ffmpeg480_cmd = f'ffmpeg -hide_banner -y -i {FILENAME_PATH} -sn -c:a aac -ac 2 -c:v libx264 -crf 23 -preset fast -sc_threshold 0 -g 48 -keyint_min 48 -hls_time 4 -hls_playlist_type vod -maxrate 1024k -bufsize 1536k -b:a 128k -pix_fmt yuv420p -hls_segment_filename 4835JRK9%03d.ts 480p.m3u8'
 os.system(ffmpeg480_cmd)
 
 # os.remove(FILENAME_PATH)
 
 ts_files = glob.glob('*.ts')
 for ts_file in ts_files:
 os.replace(ts_file, os.path.join(newname_path, ts_file))
 
 master_text = '''#EXTM3U
#EXT-X-STREAM-INF:BANDWIDTH=1500000,RESOLUTION=854x480
480p.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=840000,RESOLUTION=640x360
360p.m3u8
'''
 with open('master.m3u8', 'w') as f:
 f.write(master_text)
 
 m3u8_files = glob.glob('*.m3u8')
 for m3u8_file in m3u8_files:
 os.replace(m3u8_file, os.path.join(newname_path, m3u8_file))


def encode_360(filename):
 FILENAME_PATH = filename
 newname = filename.split('.')[0]
 newname_path = os.path.join(VIDEO_PATH,newname)
 poster_cmd = f'ffmpeg -y -ss 00:00:10 -i {FILENAME_PATH} -vframes 1 -q:v 2 poster.jpg'
 os.system(poster_cmd)
 if not os.path.exists(newname_path):
 os.mkdir(newname_path)
 os.replace('poster.jpg',os.path.join(newname_path, 'poster.jpg'))

 
 ffmpeg360_cmd = f'ffmpeg -hide_banner -y -i {FILENAME_PATH} -sn -c:a aac -ac 2 -c:v libx264 -crf 23 -preset fast -sc_threshold 0 -g 48 -keyint_min 48 -hls_time 4 -hls_playlist_type vod -maxrate 512k -bufsize 768k -b:a 128k -pix_fmt yuv420p -hls_segment_filename 365RL6TJ%03d.ts 360p.m3u8'
 os.system(ffmpeg360_cmd)
 # return
 
 
 ts_files = glob.glob('*.ts')
 for ts_file in ts_files:
 os.replace(ts_file, os.path.join(newname_path,ts_file))
 master_text = '''#EXTM3U
#EXT-X-STREAM-INF:BANDWIDTH=840000,RESOLUTION=640x360
360p.m3u8
 '''
 with open('master.m3u8', 'w') as f:
 f.write(master_text)
 m3u8_files = glob.glob('*.m3u8')
 for m3u8_file in m3u8_files:
 os.replace(m3u8_file, os.path.join(newname_path, m3u8_file))


def encode_720(filename):
 FILENAME_PATH = filename
 newname = filename.split('.')[0]
 newname_path = os.path.join(VIDEO_PATH,newname)
 poster_cmd = f'ffmpeg -y -ss 00:00:10 -i {FILENAME_PATH} -vframes 1 -q:v 2 poster.jpg'
 os.system(poster_cmd)

 if not os.path.exists(newname_path):
 os.mkdir(newname_path)

 os.replace('poster.jpg',os.path.join(newname_path, 'poster.jpg'))

 ffmpeg720_cmd = f'ffmpeg -hide_banner -y -i {FILENAME_PATH} -sn -c:a aac -ac 2 -c:v libx264 -crf 23 -preset fast -sc_threshold 0 -g 48 -keyint_min 48 -hls_time 4 -hls_playlist_type vod -maxrate 2048k -bufsize 3072k -b:a 160k -pix_fmt yuv420p -hls_segment_filename 7269TKL0%03d.ts 720p.m3u8'
 os.system(ffmpeg720_cmd)

 # os.remove(FILENAME_PATH)

 ts_files = glob.glob('*.ts')
 for ts_file in ts_files:
 os.replace(ts_file, os.path.join(newname_path,ts_file))
 
 m3u8_text = '''#EXTM3U
#EXT-X-STREAM-INF:BANDWIDTH=2800000,RESOLUTION=1280x720
720p.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=840000,RESOLUTION=640x360
360p.m3u8 
 '''

 with open('master.m3u8','w') as f:
 f.write(m3u8_text)
 
 m3u8_files = glob.glob('*.m3u8')
 for m3u8_file in m3u8_files:
 os.replace(m3u8_file, os.path.join(newname_path,m3u8_file)) 


def encode_1080(filename):
 FILENAME_PATH = filename
 newname = filename.split('.')[0]
 newname_path = os.path.join(VIDEO_PATH, newname)
 poster_cmd = f'ffmpeg -y -ss 00:00:10 -i {FILENAME_PATH} -vframes 1 -q:v 2 poster.jpg'
 os.system(poster_cmd)

 if not os.path.exists(newname_path):
 os.mkdir(newname_path)

 os.replace('poster.jpg', os.path.join(newname_path, 'poster.jpg'))

 ffmpeg1080_cmd = f'ffmpeg -hide_banner -y -i {FILENAME_PATH} -sn -c:a aac -ac 2 -c:v libx264 -crf 23 -preset fast -sc_threshold 0 -g 48 -keyint_min 48 -hls_time 4 -hls_playlist_type vod -maxrate 4000k -bufsize 6000k -b:a 192k -pix_fmt yuv420p -hls_segment_filename 108YUT8T%03d.ts 1080p.m3u8'
 os.system(ffmpeg1080_cmd)

 # os.remove(FILENAME_PATH)

 ts_files = glob.glob('*.ts')
 for ts_file in ts_files:
 os.replace(ts_file, os.path.join(newname_path, ts_file))

 m3u8_text = '''#EXTM3U
#EXT-X-STREAM-INF:BANDWIDTH=4000000,RESOLUTION=1920x1080
1080p.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=2800000,RESOLUTION=1280x720
720p.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=840000,RESOLUTION=640x360
360p.m3u8
'''

 with open('master.m3u8', 'w') as f:
 f.write(m3u8_text)

 m3u8_files = glob.glob('*.m3u8')
 for m3u8_file in m3u8_files:
 os.replace(m3u8_file, os.path.join(newname_path, m3u8_file))



def vod(filename, resolution):
 os.replace(filename,filename.replace(' ','_'))
 filename = filename.replace(' ','_')
 FILENAME_PATH = filename
 width_cmd = f'ffprobe -v error -select_streams v:0 -show_entries stream=width -of default=nw=1:nk=1 {FILENAME_PATH}'
 height_cmd= f'ffprobe -v error -select_streams v:0 -show_entries stream=height -of default=nw=1:nk=1 {FILENAME_PATH}'

 width_result = subprocess.run(width_cmd.split(), stdout=subprocess.PIPE)
 width = width_result.stdout.strip().decode('utf-8')

 height_result = subprocess.run(height_cmd.split(), stdout=subprocess.PIPE)
 height = height_result.stdout.strip().decode('utf-8')

 if not os.path.exists(VIDEO_PATH):
 os.mkdir(VIDEO_PATH)

 if resolution == 360: 
 write_log(f'Encoding {filename} in 360p') 
 encode_360(filename)

 if resolution == 720 :
 if int(height) >=400 :
 if int(height) <= 700:
 query = f"""UPDATE videos SET encoding_status = 'encoding480' WHERE file_uid LIKE '%{filename.replace(".mkv","")}'"""
 executedb(query)
 write_log(f'Encoding {filename} in 480p')
 encode_480(filename) 
 query = f"""UPDATE videos SET encoding_status = 'done480' WHERE file_uid LIKE '%{filename.replace(".mkv","")}'"""
 executedb(query)
 query = f"""UPDATE videos SET quality = 2 WHERE file_uid LIKE '%{filename.replace(".mkv","")}'"""
 executedb(query)
 else:
 if int(height) >= 800:
 query = f"""UPDATE videos SET encoding_status = 'encoding720' WHERE file_uid LIKE '%{filename.replace(".mkv","")}'"""
 executedb(query)
 write_log(f'Encoding {filename} in 720p')
 encode_720(filename) 
 query = f"""UPDATE videos SET encoding_status = 'done720' WHERE file_uid LIKE '%{filename.replace(".mkv","")}'"""
 executedb(query)
 query = f"""UPDATE videos SET quality = 3 WHERE file_uid LIKE '%{filename.replace(".mkv","")}'"""
 executedb(query)
 query = f"""UPDATE videos SET encoding_status = 'encoding1080' WHERE file_uid LIKE '%{filename.replace(".mkv","")}'"""
 executedb(query)
 write_log(f'Encoding {filename} in 720p')
 encode_1080(filename) 
 query = f"""UPDATE videos SET encoding_status = 'done1080' WHERE file_uid LIKE '%{filename.replace(".mkv","")}'"""
 executedb(query)
 query = f"""UPDATE videos SET quality = 4 WHERE file_uid LIKE '%{filename.replace(".mkv","")}'"""
 executedb(query)
 else:
 query = f"""UPDATE videos SET encoding_status = 'encoding720' WHERE file_uid LIKE '%{filename.replace(".mkv","")}'"""
 executedb(query)
 write_log(f'Encoding {filename} in 720p')
 encode_720(filename) 
 query = f"""UPDATE videos SET encoding_status = 'done720' WHERE file_uid LIKE '%{filename.replace(".mkv","")}'"""
 executedb(query)
 query = f"""UPDATE videos SET quality = 3 WHERE file_uid LIKE '%{filename.replace(".mkv","")}'"""
 executedb(query)
 
 
 
 os.remove(filename)


def move_to_rclone():
 if not os.path.exists(RCLONE_PATH):
 os.mkdir(RCLONE_PATH)
 folders = os.listdir(VIDEO_PATH)
 if len(folders) > 0:
 for folder in folders:
 folder_in_rclone = os.path.join(RCLONE_PATH, folder)
 if not os.path.exists(folder_in_rclone):
 os.mkdir(folder_in_rclone)
 files = os.listdir(os.path.join(VIDEO_PATH, folder))
 for file in files:
 os.replace(os.path.join(VIDEO_PATH, folder, file), os.path.join(folder_in_rclone, file))
 os.system(MOVE_CMD.format(folder_in_rclone))
 if len(os.listdir(os.path.join(VIDEO_PATH, folder))) == 0:
 os.rmdir(os.path.join(VIDEO_PATH, folder))
 # rclone_folders = os.listdir(RCLONE_PATH)
 # if len(rclone_folders)> 0:
 # for rclone_folder in rclone_folders:
 # os.system(MOVE_CMD.format(rclone_folder))

def get_length(input_video):
 result = subprocess.run(['ffprobe', '-v', 'error', '-show_entries', 'format=duration', '-of', 'default=noprint_wrappers=1:nokey=1', input_video], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
 try:
 output = float(result.stdout)
 except:
 output = 0
 return output

def move_to_error(file):
 if not os.path.exists(ERROR_PATH):
 os.mkdir(ERROR_PATH)
 print(f'[red][-][/red] Moving {file} to the error folder')
 os.replace(file, os.path.join(ERROR_PATH, file.split('/')[-1]))

def main():
 db = sql.connect(
 host=MYSQL_HOST,
 user=MYSQL_USER,
 password=MYSQL_PASSWORD,
 database=MYSQL_DB
 )

 def get_video_data(file_uid):
 cursor = db.cursor()
 query = f"""SELECT id, post_title, file_uid, group_id FROM videos WHERE file_uid LIKE %s"""
 cursor.execute(query, (f'%{file_uid}%',))
 data = cursor.fetchone()
 return data if data else None
 
 if os.path.exists(UPLOAD_PATH):
 upload_files = os.listdir(UPLOAD_PATH)
 upload_files = [os.path.join(UPLOAD_PATH, x) for x in upload_files]
 upload_files.sort(key=lambda x: os.path.getmtime(x))
 else:
 upload_files = []

 if len(upload_files) > 0:
 for upload_file in upload_files:
 if get_length(upload_file) < VIDEO_LENGTH:
 move_to_error(upload_file)
 write_log(f'Length of {upload_file} is less than 60 sec. Moving to error folder')
 continue
 try:
 os.replace(upload_file, upload_file.split('/')[-1])
 query = f"""UPDATE videos SET encoding_status = 'uploaded' WHERE file_uid LIKE '%{upload_file.replace(".mkv","").replace(".mp4","")}'"""
 executedb(query)
 except:
 print(e)
 continue
 change_all_videos_extension()

 mkv_files = glob.glob('*.mkv')
 if len(mkv_files) > 0:
 for mkv_file in mkv_files:
 try:
 query = f"""UPDATE videos SET encoding_status = 'encoding360' WHERE file_uid LIKE '%{mkv_file.replace(".mkv","")}'"""
 executedb(query)
 vod(mkv_file, 360)
 query = f"""UPDATE videos SET encoding_status = 'done360' WHERE file_uid LIKE '%{mkv_file.replace(".mkv","")}'"""
 executedb(query)
 query = f"""UPDATE videos SET quality = 1 WHERE file_uid LIKE '%{mkv_file.replace(".mkv","")}'"""
 executedb(query)

 
 vod(mkv_file, 720)
 
 except Exception as e:
 query = f"""UPDATE videos SET encoding_status = 'error' WHERE file_uid LIKE '%{mkv_file.replace(".mkv","")}'"""
 executedb(query)
 video_data = get_video_data(
 mkv_file.replace(".mkv", ""))
 if video_data:
 cursor = db.cursor()
 video_id, video_title, video_uid, group_id = video_data
 error_log_query = f"""INSERT INTO error_logs (video_id, video_title, video_uid, group_id, log, created_at, updated_at) 
 VALUES (%s, %s, %s, %s, %s, NOW(), NOW())"""
 cursor.execute(
 error_log_query, (video_id, video_title, video_uid, group_id, str(e)))
 db.commit()
 write_log(f'Error: {e}')
 move_to_error(mkv_file)
 move_to_rclone()
 else:
 print(f'[{time_now()}] No new video found on upload folder')

 if os.path.exists(UPLOAD_PATH2):
 upload_files2 = os.listdir(UPLOAD_PATH2)
 upload_files2 = [os.path.join(UPLOAD_PATH2, x) for x in upload_files2]
 upload_files2.sort(key=lambda x: os.path.getmtime(x))
 else:
 upload_files2 = []

 if len(upload_files2) > 0:
 for upload_file2 in upload_files2:
 if get_length(upload_file2) < VIDEO_LENGTH:
 move_to_error(upload_file2)
 continue
 if len(os.listdir(UPLOAD_PATH)) != 0:
 main()
 try:
 os.replace(upload_file2, upload_file2.split('/')[-1])
 query = f"""UPDATE videos SET encoding_status = 'uploaded' WHERE file_uid LIKE '%{upload_file2.replace(".mkv","").replace(".mp4","")}'"""
 executedb(query)
 except:
 continue

 change_all_videos_extension()

 mkv_files = glob.glob('*.mkv')
 if len(mkv_files) > 0:
 for mkv_file in mkv_files:
 try:
 query = f"""UPDATE videos SET encoding_status = 'encoding360' WHERE file_uid LIKE '%{mkv_file.replace(".mkv","")}'"""
 executedb(query)
 vod(mkv_file, 360)
 query = f"""UPDATE videos SET encoding_status = 'done360' WHERE file_uid LIKE '%{mkv_file.replace(".mkv","")}'"""
 executedb(query)
 query = f"""UPDATE videos SET quality = 1 WHERE file_uid LIKE '%{mkv_file.replace(".mkv","")}'"""
 executedb(query)


 vod(mkv_file, 720)

 except Exception as e:
 query = f"""UPDATE videos SET encoding_status = 'error' WHERE file_uid LIKE '%{mkv_file.replace(".mkv","")}'"""
 executedb(query)
 video_data = get_video_data(
 mkv_file.replace(".mkv", ""))
 if video_data:
 cursor = db.cursor()
 video_id, video_title, video_uid, group_id = video_data
 error_log_query = f"""INSERT INTO error_logs (video_id, video_title, video_uid, group_id, log, created_at, updated_at) 
 VALUES (%s, %s, %s, %s, %s, NOW(), NOW())"""
 cursor.execute(
 error_log_query, (video_id, video_title, video_uid, group_id, str(e)))
 db.commit()
 write_log(f'Error: {e}')
 move_to_error(mkv_file)
 move_to_rclone()
 else:
 print(f'[{time_now()}] No new video found on upload2 folder.')
 move_to_rclone()
 db.close()

if __name__=="__main__":
 while True:
 try:
 main()
 except Exception as e:
 print(f'Error: {e}')
 for i in range(WAIT):
 print(f'[green][+][/green] Waiting for {WAIT-i} seconds ', end="\r")
 time.sleep(1)





looking for suggestion to improve encoding speed