Я создал API с использованием FastAPI для перекодирования видеофайлов, например с адаптивным битрейтом YouTube. В настоящее время я пытаюсь установить значения разрешения для каждого файла. Но всякий раз, когда я передаю несколько разрешений в виде списка данных формы через автодокументацию пользовательского интерфейса Swagger, я получаю ошибку 422 необрабатываемого объекта.
Вот мой код:
async def transcode_video(input_path, output_folder, res, unique_id, total_files, pbar):
# Use asyncio for command execution with progress bar
output_path = os.path.join(output_folder, f"{res}p.m3u8")
# Calculate the target size
target_size = calculate_target_size(input_path)
cmd_duration = f"ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 {input_path}"
total_duration = float(subprocess.check_output(cmd_duration, shell=True, text=True).strip())
cmd = (
f"ffmpeg -i {input_path} -vf scale=-2:{res} -c:a aac -b:a 128k "
f"-g 50 -hls_time 1 -hls_list_size 0 "
f"-crf 23 -b:v 100k -fs {target_size} "
f"-hls_segment_filename \"{output_path.replace('.m3u8', '_%03d.ts')}\" "
f"{output_path}"
)
process = await asyncio.create_subprocess_shell(
cmd,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE
)
while True:
line = await process.stderr.readline()
if not line:
break
line = line.decode().strip()
if "time=" in line:
# Extracting the time progress from FFmpeg output
time_str = line.split("time=")[1].split()[0]
current_time = sum(x * float(t) for x, t in zip([3600, 60, 1], time_str.split(":")))
progress = (current_time / total_duration) * 100
pbar.update(progress - pbar.n)
# Wait for the transcoding process to complete
await process.wait()
if process.returncode != 0:
raise HTTPException(status_code=500, detail="Video transcoding failed.")
pbar.close()
# Increment the total number of transcoded files
total_files[0] += 1
@app.post("/transcode/")
async def transcode_video_endpoint(files: List[UploadFile] = File(...), resolutions: List[int] = None):
# Counters for transcoded videos
total_files = [0]
# Iterate over each file
for file in files:
# Check if the file is a valid video file based on its extension
valid_video_extensions = {".mp4", ".avi", ".mkv", ".mov", ".wmv", ".flv"}
if not any(file.filename.lower().endswith(ext) for ext in valid_video_extensions):
print(f"Skipping non-video file: {file.filename}")
continue
# Assign a unique ID for each file
unique_id = str(uuid.uuid4())
# Log the filename and unique ID
print(f"Processing file: {file.filename} with unique ID: {unique_id}")
# Create a folder for the unique ID
unique_id_folder = os.path.join(OUTPUT_FOLDER, unique_id)
Path(unique_id_folder).mkdir(parents=True, exist_ok=True)
# Save the uploaded file
input_path = os.path.join(UPLOAD_FOLDER, file.filename)
with open(input_path, "wb") as video_file:
video_file.write(file.file.read())
# Check if the file is a valid video file using ffprobe
try:
subprocess.run(
["ffprobe", "-v", "error", "-select_streams", "v:0", "-show_entries", "stream=codec_type", "-of", "csv=p=0", input_path],
check=True, capture_output=True
)
except subprocess.CalledProcessError:
print(f"Skipping non-video file: {file.filename}")
continue
# Determine the resolutions to transcode based on the provided or default resolution
resolutions_to_transcode = [res for res in [240, 360, 480, 720] if resolutions is None or res in resolutions]
# If resolutions is not exactly 360, 480, 720, or 1080, transcode to the nearest lower resolution
if resolutions is not None:
resolutions_to_transcode = [get_closest_lower_resolution(res) for res in resolutions]
# Transcode the video into the specified resolutions
for res in resolutions_to_transcode:
output_folder = os.path.join(unique_id_folder, f"{res}p")
Path(output_folder).mkdir(parents=True, exist_ok=True)
# Call the transcode_video function with tqdm progress bar
with tqdm(total=100, desc=f"Transcoding {res}p", position=0, leave=True) as pbar:
await transcode_video(input_path, output_folder, res, unique_id, total_files, pbar)
# Create index.m3u8 file after transcoding all resolutions
create_index_m3u8(unique_id, resolutions_to_transcode)
return JSONResponse(content={"message": f"{total_files[0]} videos transcoded."})
Если я укажу только одно значение разрешения, оно будет работать нормально. Но если я предоставлю список разрешений для всех загруженных видео, я получу ошибку 422, как показано ниже:
Code Details
422
Error: Unprocessable Entity
Response body
Download
{
"detail": [
{
"loc": [
"body",
"resolutions",
0
],
"msg": "value is not a valid integer",
"type": "type_error.integer"
}
]
}
Я использую Python 3.8.18. Не могли бы вы мне помочь, пожалуйста? Я пробовал использовать list, объект, но всегда получал ошибку. Мой формат ввода показан ниже:
Я создал API с использованием FastAPI для перекодирования видеофайлов, например с адаптивным битрейтом YouTube. В настоящее время я пытаюсь установить значения разрешения для каждого файла. Но всякий раз, когда я передаю несколько разрешений в виде списка данных формы через автодокументацию пользовательского интерфейса Swagger, я получаю ошибку 422 необрабатываемого объекта. Вот мой код: [code]async def transcode_video(input_path, output_folder, res, unique_id, total_files, pbar): # Use asyncio for command execution with progress bar output_path = os.path.join(output_folder, f"{res}p.m3u8")
# Calculate the target size target_size = calculate_target_size(input_path)
process = await asyncio.create_subprocess_shell( cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE )
while True: line = await process.stderr.readline() if not line: break line = line.decode().strip() if "time=" in line: # Extracting the time progress from FFmpeg output time_str = line.split("time=")[1].split()[0] current_time = sum(x * float(t) for x, t in zip([3600, 60, 1], time_str.split(":"))) progress = (current_time / total_duration) * 100 pbar.update(progress - pbar.n)
# Wait for the transcoding process to complete await process.wait()
if process.returncode != 0: raise HTTPException(status_code=500, detail="Video transcoding failed.") pbar.close()
# Increment the total number of transcoded files total_files[0] += 1
# Iterate over each file for file in files: # Check if the file is a valid video file based on its extension valid_video_extensions = {".mp4", ".avi", ".mkv", ".mov", ".wmv", ".flv"} if not any(file.filename.lower().endswith(ext) for ext in valid_video_extensions): print(f"Skipping non-video file: {file.filename}") continue
# Assign a unique ID for each file unique_id = str(uuid.uuid4())
# Log the filename and unique ID print(f"Processing file: {file.filename} with unique ID: {unique_id}")
# Create a folder for the unique ID unique_id_folder = os.path.join(OUTPUT_FOLDER, unique_id) Path(unique_id_folder).mkdir(parents=True, exist_ok=True)
# Save the uploaded file input_path = os.path.join(UPLOAD_FOLDER, file.filename) with open(input_path, "wb") as video_file: video_file.write(file.file.read())
# Check if the file is a valid video file using ffprobe try: subprocess.run( ["ffprobe", "-v", "error", "-select_streams", "v:0", "-show_entries", "stream=codec_type", "-of", "csv=p=0", input_path], check=True, capture_output=True ) except subprocess.CalledProcessError: print(f"Skipping non-video file: {file.filename}") continue
# Determine the resolutions to transcode based on the provided or default resolution resolutions_to_transcode = [res for res in [240, 360, 480, 720] if resolutions is None or res in resolutions]
# If resolutions is not exactly 360, 480, 720, or 1080, transcode to the nearest lower resolution if resolutions is not None: resolutions_to_transcode = [get_closest_lower_resolution(res) for res in resolutions]
# Transcode the video into the specified resolutions for res in resolutions_to_transcode: output_folder = os.path.join(unique_id_folder, f"{res}p") Path(output_folder).mkdir(parents=True, exist_ok=True)
# Call the transcode_video function with tqdm progress bar with tqdm(total=100, desc=f"Transcoding {res}p", position=0, leave=True) as pbar: await transcode_video(input_path, output_folder, res, unique_id, total_files, pbar)
# Create index.m3u8 file after transcoding all resolutions create_index_m3u8(unique_id, resolutions_to_transcode)
return JSONResponse(content={"message": f"{total_files[0]} videos transcoded."}) [/code] Если я укажу только одно значение разрешения, оно будет работать нормально. Но если я предоставлю список разрешений для всех загруженных видео, я получу ошибку 422, как показано ниже: [code]Code Details 422 Error: Unprocessable Entity
Response body Download { "detail": [ { "loc": [ "body", "resolutions", 0 ], "msg": "value is not a valid integer", "type": "type_error.integer" } ] } [/code] Я использую Python 3.8.18. Не могли бы вы мне помочь, пожалуйста? Я пробовал использовать list, объект, но всегда получал ошибку. Мой формат ввода показан ниже: [img]https://i.sstatic.net/eQeqh.png[/img]
Я создал API, используя FastAPI, для транскодирования видеофайлов, таких как адаптивный битрейт YouTube. В настоящее время я пытаюсь установить значения разрешения для каждого файла. Но всякий раз, когда я передаю несколько разрешений в качестве...
Я постоянно получаю сообщение об ошибке 422 Unprocessable Entity со следующими отсутствующими полями:
{ detail :[{ type : missing , loc : , msg : Field required , input :null},{ type : missing , loc : , msg : Field required , input :null}]}
Я...
Я постоянно получаю сообщение об ошибке 422 Unprocessable Entity со следующими отсутствующими полями:
{ detail :[{ type : missing , loc : , msg : Field required , input :null},{ type : missing , loc : , msg : Field required , input :null}]}
Я использую FastAPI v0.111.0, pydantic v2.0.0
У меня есть следующий класс
class Index(File):
indexes: list | None = Field(default_factory=list, description= List of indexes )
class File:
id: str = Field( , description= poc id )
Я создаю простой API для тестирования базы данных. Когда я использую запрос GET, все работает нормально, но если я перейду на POST, я получаю ошибку 422 Unprocessable Entity.
Вот код FastAPI:
из импорта FastAPI FastAPI приложение = ФастAPI()...