diff --git a/scripts/generate_api.sh b/scripts/generate_api.sh index af00d8b..14cb127 100755 --- a/scripts/generate_api.sh +++ b/scripts/generate_api.sh @@ -16,7 +16,6 @@ elif [ -f "${ROOT_DIR}/openapi-generator-cli.jar" ]; then GENERATOR="java -jar ${ROOT_DIR}/openapi-generator-cli.jar" else echo "OpenAPI Generator not found. We use openapi-generator-cli 7.17.0. Install with version control:" - echo " - brew: brew install openapi-generator@7.17" echo " - npm: npm i -g @openapitools/openapi-generator-cli" exit 1 fi diff --git a/src/commands/upload/main.rs b/src/commands/upload/main.rs index 1512884..91e11de 100644 --- a/src/commands/upload/main.rs +++ b/src/commands/upload/main.rs @@ -12,7 +12,7 @@ use walkdir::WalkDir; use crate::auth; use crate::commands::api_config; use crate::media::ffmpeg::ensure_ffmpeg_available; -use crate::media::metadata::extract_media_metadata; +use crate::media::metadata::{extract_media_metadata, get_ffprobe_json}; use crate::media::media_file_type::is_audio_file; use crate::media::transcode::{ convert_to_mp3, create_rendition, has_video_streams, is_mxf_file, normalize_audio_to_mp3, @@ -224,7 +224,8 @@ fn run_recreate_filesystem(args: RecreateFilesystemArgs) -> Result<(), String> { .map_err(|e| format!("failed to start runtime: {}", e))?; for folder_path in in_app_paths { - let req = CreateFolderRequest::new(folder_path.clone()); + let mut req = CreateFolderRequest::new(); + req.path = Some(Some(folder_path.clone())); let response = rt.block_on(api::create_folder_asset_folder_post( &cfg, req, @@ -455,6 +456,14 @@ fn run_upload(args: UploadCmdArgs) -> Result<(), String> { source_info.umid = Some(Some(first_with_data.umid.clone())); } } + if is_mxf_file(&file_info.original_path) { + if let Ok(Some(probe)) = get_ffprobe_json(&file_info.original_path) { + if let serde_json::Value::Object(map) = probe { + source_info.original_ffprobe_metadata = + Some(Some(map.into_iter().collect())); + } + } + } related_umids_per_file.push( umid.map(|m| m.file_package_umids.iter().map(|u| u.umid.clone()).collect()) .unwrap_or_default(), @@ -791,9 +800,9 @@ fn run_two_queue_pipeline( ); preproc_req.generate_time_based_media_description = Some(!disable_description_generation); + // related_umid_for_master_clip removed in current API; use override_entity_ids if needed if !file_related_umids.is_empty() { - preproc_req.related_umid_for_master_clip = - Some(Some(file_related_umids)); + preproc_req.override_entity_ids = Some(Some(file_related_umids)); } let preproc_tasks = api::process_assets_users_assets_preprocess_post( &cfg, @@ -876,6 +885,14 @@ fn build_single_upload_request( source_info.umid = Some(Some(first_with_data.umid.clone())); } } + if is_mxf_file(&file_info.original_path) { + if let Ok(Some(probe)) = get_ffprobe_json(&file_info.original_path) { + if let serde_json::Value::Object(map) = probe { + source_info.original_ffprobe_metadata = + Some(Some(map.into_iter().collect())); + } + } + } let req = AssetUploadRequest::new( i32::try_from(content_length).unwrap_or(i32::MAX), upload_id.clone(), @@ -1050,8 +1067,7 @@ async fn upload_to_presigned_urls( preproc_req.generate_time_based_media_description = Some(!disable_description_generation); if !file_related_umids.is_empty() { - preproc_req.related_umid_for_master_clip = - Some(Some(file_related_umids)); + preproc_req.override_entity_ids = Some(Some(file_related_umids)); } if let Err(e) = api::process_assets_users_assets_preprocess_post( &cfg_clone, diff --git a/src/media/metadata.rs b/src/media/metadata.rs index 4970694..bc73815 100644 --- a/src/media/metadata.rs +++ b/src/media/metadata.rs @@ -27,6 +27,11 @@ pub fn extract_media_metadata(path: &PathBuf) -> Result { }) } +/// Returns raw ffprobe JSON (format + streams) for the given file, or None if ffprobe fails. +pub fn get_ffprobe_json(path: &PathBuf) -> Result, String> { + run_ffprobe_json(path) +} + #[derive(Debug, Default)] pub struct MxfUmids { pub material_package_umid: Option, @@ -37,11 +42,12 @@ fn run_ffprobe_json(path: &PathBuf) -> Result, String> { let output = Command::new("ffprobe") .args([ "-v", - "error", + "quiet", + "-count_frames", + "-print_format", + "json", "-show_format", "-show_streams", - "-of", - "json", &path.to_string_lossy(), ]) .output() diff --git a/src/tellers_api/openapi.tellers_public_api.yaml b/src/tellers_api/openapi.tellers_public_api.yaml index a588380..7e083fd 100644 --- a/src/tellers_api/openapi.tellers_public_api.yaml +++ b/src/tellers_api/openapi.tellers_public_api.yaml @@ -319,8 +319,12 @@ paths: Uses default group(s) unless group_id is set (user must have read access to that group). + after: only files with last_edit > after (newer). + before: only files with last_edit < before (older); use the smallest last_edit - from a previous page to get the next page.' + from a previous page to get the next page. + + filetype: only files with the given file type(s).' operationId: get_most_recent_files_asset_most_recent_get parameters: - name: path @@ -354,6 +358,15 @@ paths: format: date-time - type: 'null' title: Before + - name: after + in: query + required: false + schema: + anyOf: + - type: string + format: date-time + - type: 'null' + title: After - name: x-api-key in: header required: false @@ -370,6 +383,16 @@ paths: - type: string - type: 'null' title: Authorization + requestBody: + content: + application/json: + schema: + anyOf: + - type: array + items: + $ref: '#/components/schemas/FileType' + - type: 'null' + title: Filetype responses: '200': description: Successful Response @@ -391,8 +414,12 @@ paths: tags: - accepts-api-key summary: Create Folder - description: 'Create a folder at the given path. Ensures the folder and all - ancestor folders exist for the user''s default group. + description: 'Create a folder. Either at a path (body.path) or under a parent + folder (body.parent_asset_id + body.name). + + Path-based: ensures folder and ancestors exist for the user''s default group. + + Parent-based: parent must be a folder the user has WRITE access to. Callable with API key (X-Api-Key header) or Bearer token.' operationId: create_folder_asset_folder_post @@ -1185,19 +1212,35 @@ components: CreateFolderRequest: properties: path: - type: string + anyOf: + - type: string + - type: 'null' title: Path + parent_asset_id: + anyOf: + - type: string + - type: 'null' + title: Parent Asset Id + name: + anyOf: + - type: string + - type: 'null' + title: Name type: object - required: - - path title: CreateFolderRequest + description: Either provide path (create at path) or parent_asset_id + name + (create under parent). CreateFolderResponse: properties: + id: + type: string + title: Id path: type: string title: Path type: object required: + - id - path title: CreateFolderResponse FileReference: @@ -1243,6 +1286,7 @@ components: - image - placeholder - group_clip + - aaf - folder - project - entity @@ -1328,13 +1372,6 @@ components: type: array - type: 'null' title: Override Entity Ids - related_umid_for_master_clip: - anyOf: - - items: - type: string - type: array - - type: 'null' - title: Related Umid For Master Clip type: object required: - assets @@ -1443,6 +1480,17 @@ components: - type: string - type: 'null' title: Capture Device Umid + original_file_path: + anyOf: + - type: string + - type: 'null' + title: Original File Path + original_ffprobe_metadata: + anyOf: + - additionalProperties: true + type: object + - type: 'null' + title: Original Ffprobe Metadata type: object required: - sourceName