moviepy
moviepy copied to clipboard
How to transform a file bytes stream with moviepy
Hey Moviepy-Team :), I am struggeling with the issue of converting a file that is not physically present on the disk. I get that file from fastapi UploadFile and than want to scale it down in case it is a video so it will not take that much space in my database. But here is the catch (at least for me): VideoFileClip expects a str. In case I pass the file bytes directly it fails since it can not obtain the path suffix (as the error message indicates). My workaround is currently to create 2 tempfiles (one for the input and one for the output file) but that really not a nice solution since writing to disk can be considered as slow, especially for large video files.
Maybe it is my failure, but I was not able to find a proper solution in the internet, nor chat gpt or any llms gave me a working example. Can you tell me how to achieve it with io.BytesIO stream?
Thanks in advance :)
Here is some sample test code.
import os
from moviepy.editor import VideoFileClip
def upload_media(self, id: int, request: Request, upload_file: UploadFile = File(...)):
file_name = upload_file.filename
mime_type = upload_file.content_type
file = upload_file.file
output_file_bytes = None
if mime_type.startswith('video/'):
# filename = await upload_file.read_file()
temp_filepath = os.path.join(os.getcwd(), ".temp", file_name) # f"{os.getcwd()}/.temp/{file_name}"
with open(temp_filepath, "wb") as f:
f.write(file.read())
scaled_video_path = os.path.join(os.getcwd(), ".temp", file_name + "_scaled.mp4")
clip = VideoFileClip(temp_filepath)
# h265_clip = clip.encode_videofile("h265", fps=30, bitrate=4000000)
resized_clip = clip.resize(width=1920, height=1080)
resized_clip.write_videofile(
os.path.join(os.getcwd(), ".temp", file_name + "_scaled.mp4"),
codec="libx264",
audio_codec="aac",
fps=30, # clip.fps,
threads=4
)
with open(scaled_video_path, "r") as file:
output_file_bytes = file.read()
mime_type = "video/mp4"
os.remove(temp_filepath)
os.remove(scaled_video_path)
-->
No one has an idea? Is it not possible to use bytestreams directly instead of file paths?
Bro I Also Encounter the Same Problem
Bro Use the imageio.mimread
which output the list of the NumPy Array. You Can Use it With the ImageSequenceClip It May Work In Your Case. But in My Case It Do not Work.
But in Finaly I Need to use the Temp File Module tempfile.NamedTemporaryFile
Below Is the My Code I Can't delete it Because I Have an hierarchy of the Temp File But You Can delete Just Put Your Code in the with Block.
def vid_url2vid_clip(url: str) -> VideoFileClip:
# Send a GET request to the URL
response = requests.get(url, stream=True)
# Create a temporary file to store the video
with tempfile.NamedTemporaryFile(suffix='.mp4', delete=False) as f:
# Write the video content to the temporary file
for chunk in response.iter_content(chunk_size=1024):
if chunk:
f.write(chunk)
f.flush()
return VideoFileClip(f.name)
I Write the Code with The Temp file Module use it Because Your code sucks if the error occur then the file will not get deleted.
But Why Are you using the Instance method You should use the static method here You do not Modify the class instance.
def upload_media(self, id: int, request: Request, upload_file): file_name = upload_file.filename mime_type = upload_file.content_type file = upload_file.file
output_file_bytes = None
if mime_type.startswith('video/'):
with tempfile.NamedTemporaryFile("w+b", ) as tmp_file:
# filename = await upload_file.read_file()
tmp_file.write(file.read())
scaled_video_path = os.path.join(os.getcwd(), ".temp", file_name + "_scaled.mp4")
clip = VideoFileClip(tmp_file.name, target_resolution=(1980, 1080))
with tempfile.NamedTemporaryFile('w+b', suffix='_scaled.mp4') as scaled_tmp_file:
clip.write_videofile(
scaled_tmp_file.name,
codec="libx264",
audio_codec="aac",
fps=30, # clip.fps,
threads=4
)
with open(scaled_video_path, "rb") as file: # Use the 'rb' mode for reading the file in bytes you are reading it in 'r' mode which is for read text
output_file_bytes = file.read()
I Used the target_resolution parameter instead of the resize Because it is significantly faster. Chake Whether video resolution is already (1980, 1080) It Will also Speed up your code and You Do not Need for tempfile at that point
Please always include your specs like we ask for in our issue templates – MoviePy version, platform used etc. – to help pinpoint what causes your problem, thanks.