studio zipped shots - ffmpg + upload work

This commit is contained in:
Brandon Thomas committed 2024-08-25 12:26:22 -04:00
1 parent ba369dab95
commit 46447448fc
4 files changed
+137 -45

No files matched your search

@@ -2,7 +2,7 @@ use std::collections::{HashMap, HashSet};
use std::io::{BufReader, Cursor, Read};
use std::path::{Path, PathBuf};
use log::{error, info, warn};
use log::{debug, error, info, warn};
use once_cell::sync::Lazy;
use zip::ZipArchive;
@@ -48,10 +48,10 @@ pub fn extract_frames_from_zip<P: AsRef<Path>>(
let entries = get_relevant_zip_entries(&mut archive)?;
for entry in entries.iter() {
info!("Entry: {:?}", entry);
debug!("Entry: {:?}", entry);
if !entry.is_frame {
info!("Skipping entry (not a frame): {:?}", entry);
debug!("Skipping entry (not a frame): {:?}", entry);
continue;
}
@@ -74,6 +74,8 @@ pub fn extract_frames_from_zip<P: AsRef<Path>>(
let output_path = frame_temp_dir.as_ref().join(filesystem_name);
info!("Creating: {:?}", output_path);
let mut output_file = std::fs::File::create(&output_path)
.map_err(|err| {
error!("Problem creating file {:?} : {:?}", output_path, err);
@@ -103,7 +105,7 @@ fn get_relevant_zip_entries(archive: &mut ZipArchive<BufReader<Cursor<&[u8]>>>)
let mut entries = Vec::new();
for i in 0..(archive.len()) {
info!("Reading file {}...", i);
debug!("Reading file {}...", i);
let mut file = archive.by_index(i)
.map_err(|err| {
@@ -114,8 +116,8 @@ fn get_relevant_zip_entries(archive: &mut ZipArchive<BufReader<Cursor<&[u8]>>>)
let filename = file.name();
let filename_lowercase = filename.to_lowercase();
info!("File {} is {:?} - is file = {}", i, filename, file.is_file());
info!("Enclosed name: {:?}", file.enclosed_name());
debug!("File {} is {:?} - is file = {}", i, filename, file.is_file());
debug!("Enclosed name: {:?}", file.enclosed_name());
if file.is_dir() {
continue;
@@ -147,7 +149,7 @@ fn get_relevant_zip_entries(archive: &mut ZipArchive<BufReader<Cursor<&[u8]>>>)
}
for entry in entries.iter() {
info!("Entry: {:?}", entry);
debug!("Entry: {:?}", entry);
}
Ok(entries)
@@ -0,0 +1,66 @@
use std::io::Write;
use std::path::{Path, PathBuf};
use std::process::Command;
use anyhow::bail;
use log::{error, info};
use errors::AnyhowResult;
use filesys::path_to_string::path_to_string;
use videos::ffprobe_get_info::{ffprobe_get_info, VideoDimensions, VideoDuration};
#[derive(Copy,Clone,Debug)]
pub enum FrameType {
Png,
Jpg,
}
pub struct OutputFile {
pub path: PathBuf,
pub dimensions: Option<VideoDimensions>,
pub duration: Option<VideoDuration>,
}
pub fn ffmpeg_frames_to_mp4(frame_input_directory: &Path, frame_type: FrameType, frame_rate: u16) -> AnyhowResult<OutputFile> {
let glob_pattern = match frame_type {
FrameType::Png => format!("{}/*.png", path_to_string(frame_input_directory)),
FrameType::Jpg => format!("{}/*.jpg", path_to_string(frame_input_directory)),
};
let output_file_path = frame_input_directory.join("output.mp4");
info!("Calling ffmpeg to convert frames to mp4...");
// ffmpeg -framerate 30 -pattern_type glob -i '*.png' \
// -c:v libx264 -pix_fmt yuv420p out.mp4
let output = Command::new("ffmpeg")
.arg("-framerate")
.arg(format!("{}", frame_rate))
.arg("-pattern_type")
.arg("glob")
.arg("-i")
.arg(glob_pattern)
.arg("-c:v")
.arg("libx264")
.arg("-pix_fmt")
.arg("yuv420p")
.arg(&output_file_path)
.output()?;
if !output.status.success() {
error!("bad exit status: {}", output.status);
let _r = std::io::stdout().write_all(&output.stdout);
let _r = std::io::stderr().write_all(&output.stderr);
bail!("ffmpeg failed: {:?}", output.status.to_string());
}
let info = ffprobe_get_info(&output_file_path)?;
Ok(OutputFile {
path: output_file_path,
duration: info.duration,
dimensions: info.dimensions,
})
}
@@ -1,2 +1,3 @@
pub mod extract_frames_from_zip;
pub mod upload_studio_shot_media_file_handler;
pub mod upload_studio_shot_media_file_handler;
mod ffmpeg_frames_to_mp4;
@@ -20,6 +20,7 @@ use enums::by_table::media_files::media_file_engine_category::MediaFileEngineCat
use enums::by_table::media_files::media_file_type::MediaFileType;
use enums::common::visibility::Visibility;
use hashing::sha256::sha256_hash_bytes::sha256_hash_bytes;
use hashing::sha256::sha256_hash_file::sha256_hash_file;
use http_server_common::request::get_request_ip::get_request_ip;
use mimetypes::mimetype_for_bytes::get_mimetype_for_bytes;
use mimetypes::mimetype_to_extension::mimetype_to_extension;
@@ -34,6 +35,7 @@ use crate::http_server::endpoints::media_files::upload::upload_error::MediaFileU
use crate::http_server::endpoints::media_files::upload::upload_new_scene_media_file_handler::UploadNewSceneMediaFileForm;
use crate::http_server::endpoints::media_files::upload::upload_pmx::extract_and_upload_pmx_files::{extract_and_upload_pmx_files, PmxError};
use crate::http_server::endpoints::media_files::upload::upload_studio_shot::extract_frames_from_zip::{extract_frames_from_zip, ExtractFramesError};
use crate::http_server::endpoints::media_files::upload::upload_studio_shot::ffmpeg_frames_to_mp4::{ffmpeg_frames_to_mp4, FrameType};
use crate::http_server::web_utils::user_session::require_moderator::{require_moderator, RequireModeratorError, UseDatabase};
use crate::state::server_state::ServerState;
use crate::http_server::validations::validate_idempotency_token_format::validate_idempotency_token_format;
@@ -216,7 +218,7 @@ pub async fn upload_studio_shot_media_file_handler(
MediaFileUploadError::ServerError
})?;
extract_frames_from_zip(&file_bytes, frame_temp_dir)
extract_frames_from_zip(&file_bytes, frame_temp_dir.path())
.map_err(|err| {
warn!("Extract frames error: {:?}", err);
match err {
@@ -232,46 +234,67 @@ pub async fn upload_studio_shot_media_file_handler(
// ==================== FFMPEG ==================== //
let video_file_details = ffmpeg_frames_to_mp4(frame_temp_dir.path(), FrameType::Png, 30)
.map_err(|err| {
warn!("FFMPEG error: {:?}", err);
MediaFileUploadError::ServerError
})?;
let hash = sha256_hash_file(&video_file_details.path)
.map_err(|io_error| {
error!("Problem hashing bytes: {:?}", io_error);
MediaFileUploadError::ServerError
})?;
// ==================== UPLOAD AND SAVE ==================== //
// ==================== UPLOAD ==================== //
// TODO DELETE ME
let token = MediaFileToken::new("test_token".to_string());
const PREFIX : Option<&str> = Some("upload_");
const SUFFIX : Option<&str> = Some(".mp4");
// const PREFIX : Option<&str> = Some("upload_");
// const SUFFIX : Option<&str> = Some(".pmx");
//
// // TODO(bt, 2024-02-22): This should be a transaction.
// let (token, record_id) = insert_media_file_from_file_upload(InsertMediaFileFromUploadArgs {
// maybe_media_class: Some(MediaFileClass::Dimensional),
// media_file_type: MediaFileType::Pmx,
// maybe_creator_user_token: Some(&user_session.get_strongly_typed_user_token()),
// maybe_creator_anonymous_visitor_token: maybe_avt_token.as_ref(),
// creator_ip_address: &ip_address,
// creator_set_visibility,
// upload_type: UploadType::Filesystem,
// maybe_engine_category: Some(engine_category),
// maybe_animation_type,
// maybe_mime_type: Some("application/octet-stream"),
// file_size_bytes: pmx_details.file_size_bytes,
// maybe_duration_millis,
// sha256_checksum: &pmx_details.sha256_checksum,
// maybe_scene_source_media_file_token: None,
// is_intermediate_system_file: false,
// maybe_title: maybe_title.as_deref(),
// public_bucket_directory_hash: pmx_details.pmx_public_upload_path.get_object_hash(),
// maybe_public_bucket_prefix: PREFIX,
// maybe_public_bucket_extension: SUFFIX,
// pool: &server_state.mysql_pool,
// })
// .await
// .map_err(|err| {
// warn!("New file creation DB error: {:?}", err);
// MediaFileUploadError::ServerError
// })?;
//
// info!("new media file id: {} token: {:?}", record_id, &token);
let bucket_path = MediaFileBucketPath::generate_new(PREFIX, SUFFIX);
server_state.public_bucket_client.upload_filename_with_content_type(
bucket_path.get_full_object_path_str(),
&video_file_details.path,
"video/mp4")
.await
.map_err(|e| {
error!("Upload video to bucket error: {:?}", e);
MediaFileUploadError::ServerError
})?;
// ==================== SAVE RECORD ==================== //
// TODO(bt, 2024-02-22): This should be a transaction.
let (token, record_id) = insert_media_file_from_file_upload(InsertMediaFileFromUploadArgs {
maybe_media_class: Some(MediaFileClass::Video),
media_file_type: MediaFileType::Mp4,
maybe_creator_user_token: Some(&user_session.user_token_typed),
maybe_creator_anonymous_visitor_token: maybe_avt_token.as_ref(),
creator_ip_address: &ip_address,
creator_set_visibility,
upload_type: UploadType::StorytellerEngine,
maybe_engine_category: None,
maybe_animation_type: None,
maybe_mime_type: Some("video/mp4"),
file_size_bytes: 0, // TODO
maybe_duration_millis: video_file_details.duration.map(|duration| duration.millis as u64),
sha256_checksum: &hash,
maybe_scene_source_media_file_token: None,
is_intermediate_system_file: false, // TODO
maybe_title: maybe_title.as_deref(),
public_bucket_directory_hash: bucket_path.get_object_hash(),
maybe_public_bucket_prefix: PREFIX,
maybe_public_bucket_extension: SUFFIX,
pool: &server_state.mysql_pool,
})
.await
.map_err(|err| {
warn!("New file creation DB error: {:?}", err);
MediaFileUploadError::ServerError
})?;
info!("new media file id: {} token: {:?}", record_id, &token);
Ok(Json(UploadStudioShotSuccessResponse {
success: true,