nca-toolkit / services /image_to_video.py
jananathbanuka
fix issues
4b12e15
# Copyright (c) 2025 Stephen G. Pope
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
import os
import subprocess
import logging
from services.file_management import download_file
from PIL import Image
STORAGE_PATH = "/tmp/"
logger = logging.getLogger(__name__)
def process_image_to_video(image_url, length, frame_rate, zoom_speed, job_id, webhook_url=None):
try:
# Download the image file
image_path = download_file(image_url, STORAGE_PATH)
logger.info(f"Downloaded image to {image_path}")
# Get image dimensions using Pillow
with Image.open(image_path) as img:
width, height = img.size
logger.info(f"Original image dimensions: {width}x{height}")
# Prepare the output path
output_path = os.path.join(STORAGE_PATH, f"{job_id}.mp4")
# Determine orientation and set appropriate dimensions
if width > height:
scale_dims = "7680:4320"
output_dims = "1920x1080"
else:
scale_dims = "4320:7680"
output_dims = "1080x1920"
# Calculate total frames and zoom factor
total_frames = float(length * frame_rate)
zoom_factor = 1 + (zoom_speed * length)
logger.info(f"Using scale dimensions: {scale_dims}, output dimensions: {output_dims}")
logger.info(f"Video length: {length}s, Frame rate: {frame_rate}fps, Total frames: {total_frames}")
logger.info(f"Zoom speed: {zoom_speed}/s, Final zoom factor: {zoom_factor}")
# Prepare FFmpeg command
cmd = [
'ffmpeg', '-framerate', str(frame_rate), '-loop', '1', '-i', image_path,
'-vf', f"scale={scale_dims},zoompan=z='min(1+({zoom_speed}*{length})*on/{total_frames}, {zoom_factor})':d={total_frames}:x='iw/2-(iw/zoom/2)':y='ih/2-(ih/zoom/2)':s={output_dims}",
'-c:v', 'libx264', '-t', str(length), '-pix_fmt', 'yuv420p', output_path
]
logger.info(f"Running FFmpeg command: {' '.join(cmd)}")
# Run FFmpeg command
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode != 0:
logger.error(f"FFmpeg command failed. Error: {result.stderr}")
raise subprocess.CalledProcessError(result.returncode, cmd, result.stdout, result.stderr)
logger.info(f"Video created successfully: {output_path}")
# Clean up input file
os.remove(image_path)
return output_path
except Exception as e:
logger.error(f"Error in process_image_to_video: {str(e)}", exc_info=True)
raise