diff --git a/assets/draft_content.json b/assets/draft_content.json new file mode 100644 index 0000000..16c5710 --- /dev/null +++ b/assets/draft_content.json @@ -0,0 +1,3336 @@ +{ + "canvas_config": { + "height": 1920, + "ratio": "9:16", + "width": 1080 + }, + "color_space": 0, + "config": { + "adjust_max_index": 1, + "attachment_info": [], + "combination_max_index": 1, + "export_range": null, + "extract_audio_last_index": 1, + "lyrics_recognition_id": "", + "lyrics_sync": true, + "lyrics_taskinfo": [], + "maintrack_adsorb": true, + "material_save_mode": 0, + "multi_language_current": "none", + "multi_language_list": [], + "multi_language_main": "none", + "multi_language_mode": "none", + "original_sound_last_index": 1, + "record_audio_last_index": 1, + "sticker_max_index": 1, + "subtitle_keywords_config": null, + "subtitle_recognition_id": "", + "subtitle_sync": true, + "subtitle_taskinfo": [], + "system_font_list": [], + "video_mute": false, + "zoom_info_params": null + }, + "cover": null, + "create_time": 0, + "duration": 10766666, + "extra_info": null, + "fps": 30.0, + "free_render_index_mode_on": false, + "group_container": null, + "id": "76F297DF-6A6C-4619-9510-7852DA0DD8F7", + "keyframe_graph_list": [], + "keyframes": { + "adjusts": [], + "audios": [], + "effects": [], + "filters": [], + "handwrites": [], + "stickers": [], + "texts": [], + "videos": [] + }, + "last_modified_platform": { + "app_id": 3704, + "app_source": "lv", + "app_version": "5.9.0", + "device_id": "0594836068dad896e25a104fc9dbabab", + "hard_disk_id": "92ff8fc0225cc7379b7488c983cc022b", + "mac_address": "32d6cbfd9256fd8884fac27c2658c25c,ec42e1bff9b87e6e58088ef68d13a818,f2ee8c3f35364c316ce08bf3c505b58b,35b263c32e7453165aabde96ca99b250", + "os": "windows", + "os_version": "10.0.26100" + }, + "materials": { + "ai_translates": [], + "audio_balances": [], + "audio_effects": [], + "audio_fades": [ + { + "fade_in_duration": 0, + "fade_out_duration": 733333, + "fade_type": 0, + "id": "5862328C-A939-4e9c-ADA8-D9BE5DBB39D9", + "type": "audio_fade" + } + ], + "audio_track_indexes": [], + "audios": [ + { + "app_id": 0, + "category_id": "", + "category_name": "", + "check_flag": 1, + "copyright_limit_type": "none", + "duration": 11800000, + "effect_id": "", + "formula_id": "", + "id": "7B9ED69A-3E48-4291-91E0-8480464BC337", + "intensifies_path": "", + "is_ai_clone_tone": false, + "is_text_edit_overdub": false, + "is_ugc": false, + "local_material_id": "", + "music_id": "", + "name": "飞书20250710-111410.mp4", + "path": "C:/Users/imeep/Desktop/v2/飞书20250710-111410.mp4", + "query": "", + "request_id": "", + "resource_id": "", + "search_id": "", + "source_from": "", + "source_platform": 0, + "team_id": "", + "text_id": "", + "tone_category_id": "", + "tone_category_name": "", + "tone_effect_id": "", + "tone_effect_name": "", + "tone_platform": "", + "tone_second_category_id": "", + "tone_second_category_name": "", + "tone_speaker": "", + "tone_type": "", + "type": "video_original_sound", + "video_id": "", + "wave_points": [] + } + ], + "beats": [ + { + "ai_beats": { + "beat_speed_infos": [], + "beats_path": "C:/Users/ivan/AppData/Local/JianyingPro/User Data/Presets/Combination/Resources/5d4b0af0b8d76734bb95823b93adfd41.beat", + "beats_url": "", + "melody_path": "", + "melody_percents": [ + 0.6, + 0.6 + ], + "melody_url": "" + }, + "enable_ai_beats": true, + "gear": 0, + "gear_count": 2, + "id": "E1C93883-23BB-4605-80BB-A7FDA94F162B", + "mode": 1, + "type": "beats", + "user_beats": [], + "user_delete_ai_beats": { + "beat_0": [], + "beat_1": [], + "beat_2": [], + "beat_3": [], + "beat_4": [], + "melody_0": [] + } + } + ], + "canvases": [ + { + "album_image": "", + "blur": 0.0, + "color": "", + "id": "BC56C74E-98B2-4403-A136-4007965D5EB5", + "image": "", + "image_id": "", + "image_name": "", + "source_platform": 0, + "team_id": "", + "type": "canvas_color" + }, + { + "album_image": "", + "blur": 0.0, + "color": "", + "id": "699EF67C-B9C3-4445-B3C0-683A783455E0", + "image": "", + "image_id": "", + "image_name": "", + "source_platform": 0, + "team_id": "", + "type": "canvas_color" + }, + { + "album_image": "", + "blur": 0.0, + "color": "", + "id": "527F4ED7-30A7-4852-B8D0-407EEEDC16BC", + "image": "", + "image_id": "", + "image_name": "", + "source_platform": 0, + "team_id": "", + "type": "canvas_color" + }, + { + "album_image": "", + "blur": 0.0, + "color": "", + "id": "24D05FAA-CED8-482e-9815-AC47AC80CC39", + "image": "", + "image_id": "", + "image_name": "", + "source_platform": 0, + "team_id": "", + "type": "canvas_color" + }, + { + "album_image": "", + "blur": 0.0, + "color": "", + "id": "9D9A702A-1919-43b8-AB75-B11AAB966128", + "image": "", + "image_id": "", + "image_name": "", + "source_platform": 0, + "team_id": "", + "type": "canvas_color" + }, + { + "album_image": "", + "blur": 0.0, + "color": "", + "id": "91D2C504-84EA-4997-A55B-C932B597AB53", + "image": "", + "image_id": "", + "image_name": "", + "source_platform": 0, + "team_id": "", + "type": "canvas_color" + }, + { + "album_image": "", + "blur": 0.0, + "color": "", + "id": "872D6D55-0C73-452c-BD1C-5F6FCDBEDCF6", + "image": "", + "image_id": "", + "image_name": "", + "source_platform": 0, + "team_id": "", + "type": "canvas_color" + }, + { + "album_image": "", + "blur": 0.0, + "color": "", + "id": "CACD8957-3030-410a-94CD-CDF488A394FD", + "image": "", + "image_id": "", + "image_name": "", + "source_platform": 0, + "team_id": "", + "type": "canvas_color" + }, + { + "album_image": "", + "blur": 0.0, + "color": "", + "id": "A15E73E3-DB98-4cf5-8B81-CCADAF7DB80C", + "image": "", + "image_id": "", + "image_name": "", + "source_platform": 0, + "team_id": "", + "type": "canvas_color" + }, + { + "album_image": "", + "blur": 0.0, + "color": "", + "id": "43FAC945-26E7-42a0-85B1-0DB387A8CDE1", + "image": "", + "image_id": "", + "image_name": "", + "source_platform": 0, + "team_id": "", + "type": "canvas_color" + } + ], + "chromas": [], + "color_curves": [], + "digital_humans": [], + "drafts": [], + "effects": [], + "flowers": [], + "green_screens": [], + "handwrites": [], + "hsl": [], + "images": [], + "log_color_wheels": [], + "loudnesses": [ + { + "enable": false, + "file_id": "", + "id": "AAB0AB41-D66B-4785-8932-657DBA782A43", + "loudness_param": null, + "target_loudness": 0.0, + "time_range": null + }, + { + "enable": false, + "file_id": "", + "id": "2F8B9440-64FC-42d9-9215-1A640900B515", + "loudness_param": null, + "target_loudness": 0.0, + "time_range": null + }, + { + "enable": false, + "file_id": "", + "id": "028F6C5E-49B2-4884-AD4D-9712D5D28B57", + "loudness_param": null, + "target_loudness": 0.0, + "time_range": null + }, + { + "enable": false, + "file_id": "", + "id": "F98842B2-1518-473e-B11C-D73BEC7E75A1", + "loudness_param": null, + "target_loudness": 0.0, + "time_range": null + }, + { + "enable": false, + "file_id": "", + "id": "995EC006-DE65-42c1-AE7D-D4FA0BCD5466", + "loudness_param": null, + "target_loudness": 0.0, + "time_range": null + }, + { + "enable": false, + "file_id": "", + "id": "528FBE3B-F2DA-4083-B9B6-6E6EC83E6D8B", + "loudness_param": null, + "target_loudness": 0.0, + "time_range": null + }, + { + "enable": false, + "file_id": "", + "id": "F9E4AC34-E06F-495d-8D84-686F00E30EB6", + "loudness_param": null, + "target_loudness": 0.0, + "time_range": null + }, + { + "enable": false, + "file_id": "", + "id": "DD1AD60F-06EE-45dd-AF94-DC36222FC206", + "loudness_param": null, + "target_loudness": 0.0, + "time_range": null + }, + { + "enable": false, + "file_id": "", + "id": "FDBCBC6D-9BE4-4c81-A887-1253DAB8CEF8", + "loudness_param": null, + "target_loudness": 0.0, + "time_range": null + }, + { + "enable": false, + "file_id": "", + "id": "07AF2049-2A24-467a-BE22-0B4FDAB1E139", + "loudness_param": null, + "target_loudness": 0.0, + "time_range": null + }, + { + "enable": false, + "file_id": "", + "id": "56186FA4-1459-45bb-B686-2A638620DB69", + "loudness_param": null, + "target_loudness": 0.0, + "time_range": null + } + ], + "manual_deformations": [], + "masks": [], + "material_animations": [ + { + "animations": [], + "id": "9EFE6717-84A6-4bd7-A573-6660D53163B7", + "multi_language_current": "none", + "type": "sticker_animation" + }, + { + "animations": [], + "id": "44BA9ECA-DEC5-4f82-9292-B4C386714B53", + "multi_language_current": "none", + "type": "sticker_animation" + }, + { + "animations": [], + "id": "CDFB999E-4155-42d3-8EFF-064CE026CC9E", + "multi_language_current": "none", + "type": "sticker_animation" + }, + { + "animations": [], + "id": "4BA9D16C-B8C1-43f0-BBE7-DDE6816CF88B", + "multi_language_current": "none", + "type": "sticker_animation" + }, + { + "animations": [], + "id": "2DFCF3C1-8EB1-4833-B1E3-A880747C3E7C", + "multi_language_current": "none", + "type": "sticker_animation" + }, + { + "animations": [], + "id": "835C4A28-FE80-4397-A307-8B0FE1DDEA02", + "multi_language_current": "none", + "type": "sticker_animation" + }, + { + "animations": [], + "id": "0FFDB33E-A55F-49d4-A938-253127CEA7D0", + "multi_language_current": "none", + "type": "sticker_animation" + }, + { + "animations": [], + "id": "B5692776-FDF0-4a7e-9B66-5C646CF4ED43", + "multi_language_current": "none", + "type": "sticker_animation" + }, + { + "animations": [], + "id": "3E72214A-A2BC-4f0e-8CEE-97D0669725D0", + "multi_language_current": "none", + "type": "sticker_animation" + }, + { + "animations": [ + { + "anim_adjust_params": null, + "category_id": "in", + "category_name": "入场", + "duration": 500000, + "id": "12088589", + "material_type": "video", + "name": "展开", + "panel": "video", + "path": "C:/Users/imeep/AppData/Local/JianyingPro/User Data/Cache/effect/12088589/553acdb325d76533d6ecbd6d621d9b9e", + "platform": "all", + "request_id": "2025063013174286796D76A3C6356147F5", + "resource_id": "7221413342257091133", + "start": 0, + "type": "in" + } + ], + "id": "1D94666F-2F21-4ced-AA13-D65280870DEF", + "multi_language_current": "none", + "type": "sticker_animation" + }, + { + "animations": [], + "id": "63CF0C77-3A2B-4ea9-8827-F79504CEF085", + "multi_language_current": "none", + "type": "sticker_animation" + } + ], + "material_colors": [], + "multi_language_refs": [], + "placeholders": [], + "plugin_effects": [], + "primary_color_wheels": [], + "realtime_denoises": [], + "shapes": [], + "smart_crops": [], + "smart_relights": [], + "sound_channel_mappings": [ + { + "audio_channel_mapping": 0, + "id": "AB68626B-F4EE-4d49-A6B8-C82B0ED5A843", + "is_config_open": false, + "type": "none" + }, + { + "audio_channel_mapping": 0, + "id": "6B22DA24-E4A7-4d04-B459-4328A3245EDF", + "is_config_open": false, + "type": "none" + }, + { + "audio_channel_mapping": 0, + "id": "3E5BCEF2-A2EB-4bc5-B641-B541F975266C", + "is_config_open": false, + "type": "none" + }, + { + "audio_channel_mapping": 0, + "id": "F472C5C9-B908-4835-936F-741F29D61F34", + "is_config_open": false, + "type": "none" + }, + { + "audio_channel_mapping": 0, + "id": "1F5F19A1-CE79-4041-A31B-C08BEA9F4F6A", + "is_config_open": false, + "type": "none" + }, + { + "audio_channel_mapping": 0, + "id": "A8404C7D-B0D5-449b-BDD9-CDF47ACE2DA0", + "is_config_open": false, + "type": "none" + }, + { + "audio_channel_mapping": 0, + "id": "280D2EC5-AD9C-46b6-BFF2-1EE9C0DB4C91", + "is_config_open": false, + "type": "none" + }, + { + "audio_channel_mapping": 0, + "id": "57A4AF29-448A-4b39-AE73-E292C3BF6B0A", + "is_config_open": false, + "type": "none" + }, + { + "audio_channel_mapping": 0, + "id": "37D1BED1-BB0C-418a-9B2B-BBC6F762AC08", + "is_config_open": false, + "type": "none" + }, + { + "audio_channel_mapping": 0, + "id": "2D89584C-D6ED-4ba8-BC33-4EEC959E0EBC", + "is_config_open": false, + "type": "none" + }, + { + "audio_channel_mapping": 0, + "id": "B0D09A80-2B29-473f-8EAB-AE311670CF0F", + "is_config_open": false, + "type": "none" + } + ], + "speeds": [ + { + "curve_speed": null, + "id": "2B7B204C-F5C5-4890-8D6C-5A3A9C371BAD", + "mode": 0, + "speed": 1.28306, + "type": "speed" + }, + { + "curve_speed": null, + "id": "8D5AB600-BB9B-4740-8D9A-08C3CE1C7113", + "mode": 0, + "speed": 0.7560985714285714, + "type": "speed" + }, + { + "curve_speed": null, + "id": "E8E5E5C7-2527-4f76-9232-B8F8BCDE74B7", + "mode": 0, + "speed": 1.0, + "type": "speed" + }, + { + "curve_speed": null, + "id": "BA003FAB-0ECB-4772-B429-162F60D58BC6", + "mode": 0, + "speed": 1.4729732578522932, + "type": "speed" + }, + { + "curve_speed": null, + "id": "B94FD630-5C98-4be8-8B55-04A68F7A60D7", + "mode": 0, + "speed": 1.2058821193770943, + "type": "speed" + }, + { + "curve_speed": null, + "id": "4D7263D8-81CC-4530-9479-2DF318504DF9", + "mode": 0, + "speed": 1.0, + "type": "speed" + }, + { + "curve_speed": null, + "id": "9C590E1D-32DD-4335-BC82-0CBDEC9579D6", + "mode": 0, + "speed": 0.9505058333333334, + "type": "speed" + }, + { + "curve_speed": null, + "id": "4E6B4A3C-7263-4f38-8D60-F347F62BE50D", + "mode": 0, + "speed": 1.0, + "type": "speed" + }, + { + "curve_speed": null, + "id": "67810649-FFF6-4b8a-AA59-D11A88DE94DD", + "mode": 0, + "speed": 1.0, + "type": "speed" + }, + { + "curve_speed": null, + "id": "F75BC4E5-12FF-4653-A7A5-6A14BABC14ED", + "mode": 0, + "speed": 1.0, + "type": "speed" + }, + { + "curve_speed": null, + "id": "ABA5CA59-584A-4c2c-AC7A-60C6BE9B177C", + "mode": 0, + "speed": 1.0, + "type": "speed" + } + ], + "stickers": [ + { + "aigc_type": "none", + "background_alpha": 1.0, + "background_color": "", + "border_color": "", + "border_line_style": 0, + "border_width": 0.0, + "category_id": "10515", + "category_name": "热门", + "check_flag": 1, + "combo_info": { + "text_templates": [] + }, + "cycle_setting": true, + "formula_id": "", + "global_alpha": 1.0, + "has_shadow": false, + "icon_url": "https://p3-heycan-jy-sign.byteimg.com/tos-cn-i-3jr8j4ixpe/baf6a084af284707b51a4f5c38a23e8c~tplv-3jr8j4ixpe-resize:200:200.png?x-expires=1783057711&x-signature=RUIg3IMV3judNjgZb3HVlf0AwIE%3D", + "id": "92160682-A5E8-4211-A503-35CFCB489346", + "multi_language_current": "none", + "name": "爱心", + "original_size": [], + "path": "C:/Users/imeep/AppData/Local/JianyingPro/User Data/Cache/artistEffect/7392568549761486099/f5f190e989bf9105bce969446216b7f7", + "platform": "all", + "preview_cover_url": "https://p3-heycan-jy-sign.byteimg.com/tos-cn-i-3jr8j4ixpe/baf6a084af284707b51a4f5c38a23e8c~tplv-3jr8j4ixpe-resize:200:200.png?x-expires=1783057711&x-signature=RUIg3IMV3judNjgZb3HVlf0AwIE%3D", + "radius": { + "bottom_left": 0.0, + "bottom_right": 0.0, + "top_left": 0.0, + "top_right": 0.0 + }, + "request_id": "20250703134831C05433BE774045E7B5CD", + "resource_id": "7392568549761486099", + "sequence_type": false, + "shadow_alpha": 0.8, + "shadow_angle": 0.0, + "shadow_color": "", + "shadow_distance": 0.0, + "shadow_point": { + "x": 0.0, + "y": 0.0 + }, + "shadow_smoothing": 0.0, + "shape_param": { + "custom_points": [], + "roundness": [], + "shape_size": [], + "shape_type": 0 + }, + "source_platform": 1, + "sticker_id": "7392568549761486099", + "sub_type": 0, + "team_id": "", + "type": "sticker", + "unicode": "" + } + ], + "tail_leaders": [], + "text_templates": [], + "texts": [ + { + "add_type": 0, + "alignment": 1, + "background_alpha": 1.0, + "background_color": "", + "background_height": 0.14, + "background_horizontal_offset": 0.0, + "background_round_radius": 0.0, + "background_style": 0, + "background_vertical_offset": 0.0, + "background_width": 0.14, + "base_content": "", + "bold_width": 0.0, + "border_alpha": 1.0, + "border_color": "#000000", + "border_width": 0.08, + "caption_template_info": { + "category_id": "", + "category_name": "", + "effect_id": "", + "is_new": false, + "path": "", + "request_id": "", + "resource_id": "", + "resource_name": "", + "source_platform": 0 + }, + "check_flag": 15, + "combo_info": { + "text_templates": [] + }, + "content": "{\"text\":\"finding the perfect everyday jorts\",\"styles\":[{\"fill\":{\"content\":{\"solid\":{\"color\":[1,1,1]}}},\"font\":{\"path\":\"C:/Users/imeep/AppData/Local/JianyingPro/Apps/5.9.0.11632/Resources/Font/SystemFont/zh-hans.ttf\",\"id\":\"\"},\"strokes\":[{\"content\":{\"solid\":{\"color\":[0,0,0]}},\"width\":0.079999998211860657}],\"size\":15,\"useLetterColor\":true,\"range\":[0,34]}]}", + "fixed_height": -1.0, + "fixed_width": -1.0, + "font_category_id": "", + "font_category_name": "", + "font_id": "", + "font_name": "", + "font_path": "C:/Users/imeep/AppData/Local/JianyingPro/Apps/5.9.0.11632/Resources/Font/SystemFont/zh-hans.ttf", + "font_resource_id": "", + "font_size": 15.0, + "font_source_platform": 0, + "font_team_id": "", + "font_title": "none", + "font_url": "", + "fonts": [], + "force_apply_line_max_width": false, + "global_alpha": 1.0, + "group_id": "", + "has_shadow": false, + "id": "B3537E2F-8CF5-400e-966E-50A87EE46374", + "initial_scale": 1.0, + "inner_padding": -1.0, + "is_rich_text": false, + "italic_degree": 0, + "ktv_color": "", + "language": "", + "layer_weight": 1, + "letter_spacing": 0.0, + "line_feed": 1, + "line_max_width": 0.82, + "line_spacing": 0.02, + "multi_language_current": "none", + "name": "", + "original_size": [], + "preset_category": "", + "preset_category_id": "", + "preset_has_set_alignment": false, + "preset_id": "", + "preset_index": 0, + "preset_name": "", + "recognize_task_id": "", + "recognize_type": 0, + "relevance_segment": [], + "shadow_alpha": 0.9, + "shadow_angle": -45.0, + "shadow_color": "", + "shadow_distance": 5.0, + "shadow_point": { + "x": 0.6363961030678928, + "y": -0.6363961030678928 + }, + "shadow_smoothing": 0.45, + "shape_clip_x": false, + "shape_clip_y": false, + "source_from": "", + "style_name": "", + "sub_type": 0, + "subtitle_keywords": null, + "subtitle_template_original_fontsize": 0.0, + "text_alpha": 1.0, + "text_color": "#ffffff", + "text_curve": null, + "text_preset_resource_id": "", + "text_size": 30, + "text_to_audio_ids": [], + "tts_auto_update": false, + "type": "text", + "typesetting": 0, + "underline": false, + "underline_offset": 0.22, + "underline_width": 0.05, + "use_effect_default_color": true, + "words": { + "end_time": [], + "start_time": [], + "text": [] + } + } + ], + "time_marks": [ + { + "id": "D46D7DF1-D84A-4e5f-9A0B-E31C93A45659", + "mark_items": [ + { + "color": "#00c1cd", + "id": "D7CBCAAE-AA73-41db-9FBA-414BC0F30188", + "time_range": { + "duration": 0, + "start": 11433333 + }, + "title": "标记 07" + }, + { + "color": "#00c1cd", + "id": "B5E7A41F-B169-4a32-B8A3-CF4592828E70", + "time_range": { + "duration": 0, + "start": 9566666 + }, + "title": "标记 06" + }, + { + "color": "#00c1cd", + "id": "75C34E47-19AC-4d84-8CD6-69611F7CA914", + "time_range": { + "duration": 0, + "start": 7733333 + }, + "title": "标记 05" + }, + { + "color": "#00c1cd", + "id": "00245570-2143-4f3a-876A-76B8220916BF", + "time_range": { + "duration": 0, + "start": 5900000 + }, + "title": "标记 04" + }, + { + "color": "#00c1cd", + "id": "5022EAD4-A8C8-4945-91FE-D1EB785EEE26", + "time_range": { + "duration": 0, + "start": 4033333 + }, + "title": "标记 03" + }, + { + "color": "#00c1cd", + "id": "AA72E912-AA72-4471-BDC9-5A0E9D230741", + "time_range": { + "duration": 0, + "start": 2200000 + }, + "title": "标记 02" + }, + { + "color": "#00c1cd", + "id": "BA3727F6-491C-4439-8B77-6224922253DE", + "time_range": { + "duration": 0, + "start": 333333 + }, + "title": "标记 01" + } + ] + } + ], + "transitions": [ + { + "category_id": "39862", + "category_name": "叠化", + "duration": 400000, + "effect_id": "2912470", + "id": "70BCB27D-330C-4142-A23D-717F906B0D2C", + "is_overlap": true, + "name": "云朵 II", + "path": "C:/Users/imeep/AppData/Local/JianyingPro/User Data/Cache/effect/2912470/5a8914a3658f88265bbeda060a7c79aa", + "platform": "all", + "request_id": "20250701154254E85A0954F29DEA8C6D87", + "resource_id": "6955760408737092132", + "type": "transition" + }, + { + "category_id": "39663", + "category_name": "热门", + "duration": 333333, + "effect_id": "86769062", + "id": "8635E323-D35C-41b4-BDB4-4ECAC22DCFA0", + "is_overlap": true, + "name": "大波浪", + "path": "C:/Users/imeep/AppData/Local/JianyingPro/User Data/Cache/effect/86769062/e63143bff5e6394a018f2b802e1d832e", + "platform": "all", + "request_id": "20250701154254E85A0954F29DEA8C6D87", + "resource_id": "7426688369653977638", + "type": "transition" + }, + { + "category_id": "39663", + "category_name": "热门", + "duration": 400000, + "effect_id": "19272888", + "id": "35D4F27D-2DA7-46f6-A756-2A5084855021", + "is_overlap": true, + "name": "抖动放大", + "path": "C:/Users/imeep/AppData/Local/JianyingPro/User Data/Cache/effect/19272888/e4cafc076ecab223a39a26fe6f05b6db", + "platform": "all", + "request_id": "20250701154254E85A0954F29DEA8C6D87", + "resource_id": "7260415521852494397", + "type": "transition" + } + ], + "video_effects": [ + { + "adjust_params": [ + { + "default_value": 0.8, + "name": "effects_adjust_intensity", + "value": 0.35 + }, + { + "default_value": 0.8, + "name": "effects_adjust_luminance", + "value": 0.0 + }, + { + "default_value": 1.0, + "name": "effects_adjust_blur", + "value": 0.0 + }, + { + "default_value": 0.35, + "name": "effects_adjust_speed", + "value": 0.35 + }, + { + "default_value": 0.8, + "name": "effects_adjust_size", + "value": 0.8 + } + ], + "algorithm_artifact_path": "", + "apply_target_type": 0, + "apply_time_range": null, + "category_id": "39654", + "category_name": "热门", + "common_keyframes": [], + "disable_effect_faces": [], + "effect_id": "14730265", + "formula_id": "", + "id": "A940EDCB-64C4-40e5-BE36-841149A9F89E", + "name": "摇晃运镜", + "path": "C:/Users/imeep/AppData/Local/JianyingPro/User Data/Cache/effect/14730265/bbc5f812f50b8ea1aadb3fd6ac2dd6f0", + "platform": "all", + "render_index": 11000, + "request_id": "20250701160043025D1153CBE0BA9DB503", + "resource_id": "7241063267357954619", + "source_platform": 0, + "time_range": null, + "track_render_index": 0, + "type": "video_effect", + "value": 1.0, + "version": "" + }, + { + "adjust_params": [ + { + "default_value": 0.8, + "name": "effects_adjust_size", + "value": 0.73 + }, + { + "default_value": 0.8, + "name": "effects_adjust_intensity", + "value": 0.35 + }, + { + "default_value": 0.8, + "name": "effects_adjust_luminance", + "value": 0.0 + }, + { + "default_value": 1.0, + "name": "effects_adjust_blur", + "value": 0.0 + }, + { + "default_value": 0.35, + "name": "effects_adjust_speed", + "value": 0.35 + } + ], + "algorithm_artifact_path": "", + "apply_target_type": 0, + "apply_time_range": null, + "category_id": "39654", + "category_name": "热门", + "common_keyframes": [], + "disable_effect_faces": [], + "effect_id": "14730265", + "formula_id": "", + "id": "4BECF71F-BD2F-417e-A505-B27B33924E49", + "name": "摇晃运镜", + "path": "C:/Users/imeep/AppData/Local/JianyingPro/User Data/Cache/effect/14730265/bbc5f812f50b8ea1aadb3fd6ac2dd6f0", + "platform": "all", + "render_index": 11000, + "request_id": "20250701160043025D1153CBE0BA9DB503", + "resource_id": "7241063267357954619", + "source_platform": 0, + "time_range": null, + "track_render_index": 0, + "type": "video_effect", + "value": 1.0, + "version": "" + }, + { + "adjust_params": [ + { + "default_value": 0.3, + "name": "effects_adjust_speed", + "value": 0.3 + }, + { + "default_value": 0.7, + "name": "effects_adjust_intensity", + "value": 0.7 + }, + { + "default_value": 0.5, + "name": "effects_adjust_luminance", + "value": 0.5 + }, + { + "default_value": 0.7, + "name": "effects_adjust_range", + "value": 0.7 + } + ], + "algorithm_artifact_path": "", + "apply_target_type": 2, + "apply_time_range": null, + "category_id": "39654", + "category_name": "热门", + "common_keyframes": [], + "disable_effect_faces": [], + "effect_id": "6733311", + "formula_id": "", + "id": "D767E308-A05A-4516-AAD3-CB63864FB4A4", + "name": "震动屏闪", + "path": "C:/Users/imeep/AppData/Local/JianyingPro/User Data/Cache/effect/6733311/c83c072fdbd9921701f12498c0c00cfc", + "platform": "all", + "render_index": 0, + "request_id": "20250701160043025D1153CBE0BA9DB503", + "resource_id": "7171697545154925069", + "source_platform": 0, + "time_range": null, + "track_render_index": 0, + "type": "video_effect", + "value": 1.0, + "version": "" + } + ], + "video_trackings": [], + "videos": [ + { + "aigc_type": "none", + "audio_fade": null, + "cartoon_path": "", + "category_id": "", + "category_name": "local", + "check_flag": 63487, + "crop": { + "lower_left_x": 0.0, + "lower_left_y": 1.0, + "lower_right_x": 1.0, + "lower_right_y": 1.0, + "upper_left_x": 0.0, + "upper_left_y": 0.0, + "upper_right_x": 1.0, + "upper_right_y": 0.0 + }, + "crop_ratio": "free", + "crop_scale": 1.0, + "duration": 10066666, + "extra_type_option": 0, + "formula_id": "", + "freeze": null, + "has_audio": false, + "height": 1248, + "id": "F40AA450-A3A0-4025-97DE-4DB7EF226275", + "intensifies_audio_path": "", + "intensifies_path": "", + "is_ai_generate_content": false, + "is_copyright": true, + "is_text_edit_overdub": false, + "is_unified_beauty_mode": false, + "local_id": "", + "local_material_id": "0f59adb8-cc10-46e8-8eb1-9ad688b6ee6c", + "material_id": "", + "material_name": "1751960798614.mp4", + "material_url": "", + "matting": { + "flag": 0, + "has_use_quick_brush": false, + "has_use_quick_eraser": false, + "interactiveTime": [], + "path": "", + "strokes": [] + }, + "media_path": "", + "object_locked": null, + "origin_material_id": "", + "path": "D:/AI视频/7.6/换装4/3/1751960798614.mp4", + "picture_from": "none", + "picture_set_category_id": "", + "picture_set_category_name": "", + "request_id": "", + "reverse_intensifies_path": "", + "reverse_path": "", + "smart_motion": null, + "source": 0, + "source_platform": 0, + "stable": { + "matrix_path": "", + "stable_level": 0, + "time_range": { + "duration": 0, + "start": 0 + } + }, + "team_id": "", + "type": "video", + "video_algorithm": { + "algorithms": [], + "complement_frame_config": null, + "deflicker": null, + "gameplay_configs": [], + "motion_blur_config": null, + "noise_reduction": null, + "path": "", + "quality_enhance": null, + "time_range": null + }, + "width": 704 + }, + { + "aigc_type": "none", + "audio_fade": null, + "cartoon_path": "", + "category_id": "", + "category_name": "local", + "check_flag": 63487, + "crop": { + "lower_left_x": 0.0, + "lower_left_y": 1.0, + "lower_right_x": 1.0, + "lower_right_y": 1.0, + "upper_left_x": 0.0, + "upper_left_y": 0.0, + "upper_right_x": 1.0, + "upper_right_y": 0.0 + }, + "crop_ratio": "free", + "crop_scale": 1.0, + "duration": 10066666, + "extra_type_option": 0, + "formula_id": "", + "freeze": null, + "has_audio": false, + "height": 1248, + "id": "2B639AFE-2332-44f6-853D-AB9F8E9CB717", + "intensifies_audio_path": "", + "intensifies_path": "", + "is_ai_generate_content": false, + "is_copyright": true, + "is_text_edit_overdub": false, + "is_unified_beauty_mode": false, + "local_id": "", + "local_material_id": "6ad0813a-3fa0-40bd-88d6-aad75b29b89f", + "material_id": "", + "material_name": "1751960755049.mp4", + "material_url": "", + "matting": { + "flag": 0, + "has_use_quick_brush": false, + "has_use_quick_eraser": false, + "interactiveTime": [], + "path": "", + "strokes": [] + }, + "media_path": "", + "object_locked": null, + "origin_material_id": "", + "path": "D:/AI视频/7.6/换装4/3/1751960755049.mp4", + "picture_from": "none", + "picture_set_category_id": "", + "picture_set_category_name": "", + "request_id": "", + "reverse_intensifies_path": "", + "reverse_path": "", + "smart_motion": null, + "source": 0, + "source_platform": 0, + "stable": { + "matrix_path": "", + "stable_level": 0, + "time_range": { + "duration": 0, + "start": 0 + } + }, + "team_id": "", + "type": "video", + "video_algorithm": { + "algorithms": [ + { + "algorithm_id": "", + "type": "ComplementFrame" + } + ], + "complement_frame_config": { + "mode": "optical_flow" + }, + "deflicker": null, + "gameplay_configs": [], + "motion_blur_config": null, + "noise_reduction": null, + "path": "##_draftpath_placeholder_0E685133-18CE-45ED-8CB8-2904A212EC80_##/Resources/videoAlg/bd722e40f900536ea7294a9fa46ff146_31707_5595124.mp4", + "quality_enhance": null, + "time_range": { + "duration": 5595124, + "start": 31707 + } + }, + "width": 704 + }, + { + "aigc_type": "none", + "audio_fade": null, + "cartoon_path": "", + "category_id": "", + "category_name": "local", + "check_flag": 63487, + "crop": { + "lower_left_x": 0.0, + "lower_left_y": 1.0, + "lower_right_x": 1.0, + "lower_right_y": 1.0, + "upper_left_x": 0.0, + "upper_left_y": 0.0, + "upper_right_x": 1.0, + "upper_right_y": 0.0 + }, + "crop_ratio": "free", + "crop_scale": 1.0, + "duration": 10066666, + "extra_type_option": 0, + "formula_id": "", + "freeze": null, + "has_audio": false, + "height": 1248, + "id": "0831DEC4-B257-4f10-AA7E-845C67C886F8", + "intensifies_audio_path": "", + "intensifies_path": "", + "is_ai_generate_content": false, + "is_copyright": true, + "is_text_edit_overdub": false, + "is_unified_beauty_mode": false, + "local_id": "", + "local_material_id": "0f59adb8-cc10-46e8-8eb1-9ad688b6ee6c", + "material_id": "", + "material_name": "1751960798614.mp4", + "material_url": "", + "matting": { + "flag": 0, + "has_use_quick_brush": false, + "has_use_quick_eraser": false, + "interactiveTime": [], + "path": "", + "strokes": [] + }, + "media_path": "", + "object_locked": null, + "origin_material_id": "", + "path": "D:/AI视频/7.6/换装4/3/1751960798614.mp4", + "picture_from": "none", + "picture_set_category_id": "", + "picture_set_category_name": "", + "request_id": "", + "reverse_intensifies_path": "", + "reverse_path": "", + "smart_motion": null, + "source": 0, + "source_platform": 0, + "stable": { + "matrix_path": "", + "stable_level": 0, + "time_range": { + "duration": 0, + "start": 0 + } + }, + "team_id": "", + "type": "video", + "video_algorithm": { + "algorithms": [], + "complement_frame_config": null, + "deflicker": null, + "gameplay_configs": [], + "motion_blur_config": null, + "noise_reduction": null, + "path": "", + "quality_enhance": null, + "time_range": null + }, + "width": 704 + }, + { + "aigc_type": "none", + "audio_fade": null, + "cartoon_path": "", + "category_id": "", + "category_name": "local", + "check_flag": 63487, + "crop": { + "lower_left_x": 0.0, + "lower_left_y": 1.0, + "lower_right_x": 1.0, + "lower_right_y": 1.0, + "upper_left_x": 0.0, + "upper_left_y": 0.0, + "upper_right_x": 1.0, + "upper_right_y": 0.0 + }, + "crop_ratio": "free", + "crop_scale": 1.0, + "duration": 10066666, + "extra_type_option": 0, + "formula_id": "", + "freeze": null, + "has_audio": false, + "height": 1248, + "id": "3E4EDBFE-4A9C-4b81-8B9A-2EDA2E26845D", + "intensifies_audio_path": "", + "intensifies_path": "", + "is_ai_generate_content": false, + "is_copyright": true, + "is_text_edit_overdub": false, + "is_unified_beauty_mode": false, + "local_id": "", + "local_material_id": "78b1fac1-0255-4911-a217-239f857268e8", + "material_id": "", + "material_name": "1751960938780.mp4", + "material_url": "", + "matting": { + "flag": 0, + "has_use_quick_brush": false, + "has_use_quick_eraser": false, + "interactiveTime": [], + "path": "", + "strokes": [] + }, + "media_path": "", + "object_locked": null, + "origin_material_id": "", + "path": "D:/AI视频/7.6/换装4/3/1751960938780.mp4", + "picture_from": "none", + "picture_set_category_id": "", + "picture_set_category_name": "", + "request_id": "", + "reverse_intensifies_path": "", + "reverse_path": "", + "smart_motion": null, + "source": 0, + "source_platform": 0, + "stable": { + "matrix_path": "", + "stable_level": 0, + "time_range": { + "duration": 0, + "start": 0 + } + }, + "team_id": "", + "type": "video", + "video_algorithm": { + "algorithms": [], + "complement_frame_config": null, + "deflicker": null, + "gameplay_configs": [], + "motion_blur_config": null, + "noise_reduction": null, + "path": "", + "quality_enhance": null, + "time_range": null + }, + "width": 704 + }, + { + "aigc_type": "none", + "audio_fade": null, + "cartoon_path": "", + "category_id": "", + "category_name": "local", + "check_flag": 63487, + "crop": { + "lower_left_x": 0.0, + "lower_left_y": 1.0, + "lower_right_x": 1.0, + "lower_right_y": 1.0, + "upper_left_x": 0.0, + "upper_left_y": 0.0, + "upper_right_x": 1.0, + "upper_right_y": 0.0 + }, + "crop_ratio": "free", + "crop_scale": 1.0, + "duration": 10066666, + "extra_type_option": 0, + "formula_id": "", + "freeze": null, + "has_audio": false, + "height": 1248, + "id": "23A99095-A6A7-4fe3-91C2-289E693D457D", + "intensifies_audio_path": "", + "intensifies_path": "", + "is_ai_generate_content": false, + "is_copyright": true, + "is_text_edit_overdub": false, + "is_unified_beauty_mode": false, + "local_id": "", + "local_material_id": "78b1fac1-0255-4911-a217-239f857268e8", + "material_id": "", + "material_name": "1751960938780.mp4", + "material_url": "", + "matting": { + "flag": 0, + "has_use_quick_brush": false, + "has_use_quick_eraser": false, + "interactiveTime": [], + "path": "", + "strokes": [] + }, + "media_path": "", + "object_locked": null, + "origin_material_id": "", + "path": "D:/AI视频/7.6/换装4/3/1751960938780.mp4", + "picture_from": "none", + "picture_set_category_id": "", + "picture_set_category_name": "", + "request_id": "", + "reverse_intensifies_path": "", + "reverse_path": "", + "smart_motion": null, + "source": 0, + "source_platform": 0, + "stable": { + "matrix_path": "", + "stable_level": 0, + "time_range": { + "duration": 0, + "start": 0 + } + }, + "team_id": "", + "type": "video", + "video_algorithm": { + "algorithms": [], + "complement_frame_config": null, + "deflicker": null, + "gameplay_configs": [], + "motion_blur_config": null, + "noise_reduction": null, + "path": "", + "quality_enhance": null, + "time_range": null + }, + "width": 704 + }, + { + "aigc_type": "none", + "audio_fade": null, + "cartoon_path": "", + "category_id": "", + "category_name": "local", + "check_flag": 63487, + "crop": { + "lower_left_x": 0.0, + "lower_left_y": 1.0, + "lower_right_x": 1.0, + "lower_right_y": 1.0, + "upper_left_x": 0.0, + "upper_left_y": 0.0, + "upper_right_x": 1.0, + "upper_right_y": 0.0 + }, + "crop_ratio": "free", + "crop_scale": 1.0, + "duration": 10066666, + "extra_type_option": 0, + "formula_id": "", + "freeze": null, + "has_audio": false, + "height": 1248, + "id": "44A8FD1E-D5C7-4b45-B75B-CF7828863A64", + "intensifies_audio_path": "", + "intensifies_path": "", + "is_ai_generate_content": false, + "is_copyright": true, + "is_text_edit_overdub": false, + "is_unified_beauty_mode": false, + "local_id": "", + "local_material_id": "78b1fac1-0255-4911-a217-239f857268e8", + "material_id": "", + "material_name": "1751960938780.mp4", + "material_url": "", + "matting": { + "flag": 0, + "has_use_quick_brush": false, + "has_use_quick_eraser": false, + "interactiveTime": [], + "path": "", + "strokes": [] + }, + "media_path": "", + "object_locked": null, + "origin_material_id": "", + "path": "D:/AI视频/7.6/换装4/3/1751960938780.mp4", + "picture_from": "none", + "picture_set_category_id": "", + "picture_set_category_name": "", + "request_id": "", + "reverse_intensifies_path": "", + "reverse_path": "", + "smart_motion": null, + "source": 0, + "source_platform": 0, + "stable": { + "matrix_path": "", + "stable_level": 0, + "time_range": { + "duration": 0, + "start": 0 + } + }, + "team_id": "", + "type": "video", + "video_algorithm": { + "algorithms": [], + "complement_frame_config": null, + "deflicker": null, + "gameplay_configs": [], + "motion_blur_config": null, + "noise_reduction": null, + "path": "", + "quality_enhance": null, + "time_range": null + }, + "width": 704 + }, + { + "aigc_type": "none", + "audio_fade": null, + "cartoon_path": "", + "category_id": "", + "category_name": "local", + "check_flag": 63487, + "crop": { + "lower_left_x": 0.0, + "lower_left_y": 1.0, + "lower_right_x": 1.0, + "lower_right_y": 1.0, + "upper_left_x": 0.0, + "upper_left_y": 0.0, + "upper_right_x": 1.0, + "upper_right_y": 0.0 + }, + "crop_ratio": "free", + "crop_scale": 1.0, + "duration": 10066666, + "extra_type_option": 0, + "formula_id": "", + "freeze": null, + "has_audio": false, + "height": 1248, + "id": "B3462906-E721-4a43-9DF5-E09EB0694C12", + "intensifies_audio_path": "", + "intensifies_path": "", + "is_ai_generate_content": false, + "is_copyright": true, + "is_text_edit_overdub": false, + "is_unified_beauty_mode": false, + "local_id": "", + "local_material_id": "abd4ea53-13ff-4cf1-9160-64af1d76f84c", + "material_id": "", + "material_name": "1751960673226.mp4", + "material_url": "", + "matting": { + "flag": 0, + "has_use_quick_brush": false, + "has_use_quick_eraser": false, + "interactiveTime": [], + "path": "", + "strokes": [] + }, + "media_path": "", + "object_locked": null, + "origin_material_id": "", + "path": "D:/AI视频/7.6/换装4/3/1751960673226.mp4", + "picture_from": "none", + "picture_set_category_id": "", + "picture_set_category_name": "", + "request_id": "", + "reverse_intensifies_path": "", + "reverse_path": "", + "smart_motion": null, + "source": 0, + "source_platform": 0, + "stable": { + "matrix_path": "", + "stable_level": 0, + "time_range": { + "duration": 0, + "start": 0 + } + }, + "team_id": "", + "type": "video", + "video_algorithm": { + "algorithms": [], + "complement_frame_config": null, + "deflicker": null, + "gameplay_configs": [], + "motion_blur_config": null, + "noise_reduction": null, + "path": "", + "quality_enhance": null, + "time_range": null + }, + "width": 704 + }, + { + "aigc_type": "none", + "audio_fade": null, + "cartoon_path": "", + "category_id": "", + "category_name": "local", + "check_flag": 63487, + "crop": { + "lower_left_x": 0.0, + "lower_left_y": 1.0, + "lower_right_x": 1.0, + "lower_right_y": 1.0, + "upper_left_x": 0.0, + "upper_left_y": 0.0, + "upper_right_x": 1.0, + "upper_right_y": 0.0 + }, + "crop_ratio": "free", + "crop_scale": 1.0, + "duration": 10066666, + "extra_type_option": 0, + "formula_id": "", + "freeze": null, + "has_audio": false, + "height": 1248, + "id": "EE337662-05D3-4efc-9723-F0A0EE93E723", + "intensifies_audio_path": "", + "intensifies_path": "", + "is_ai_generate_content": false, + "is_copyright": true, + "is_text_edit_overdub": false, + "is_unified_beauty_mode": false, + "local_id": "", + "local_material_id": "6ad0813a-3fa0-40bd-88d6-aad75b29b89f", + "material_id": "", + "material_name": "1751960755049.mp4", + "material_url": "", + "matting": { + "flag": 0, + "has_use_quick_brush": false, + "has_use_quick_eraser": false, + "interactiveTime": [], + "path": "", + "strokes": [] + }, + "media_path": "", + "object_locked": null, + "origin_material_id": "", + "path": "D:/AI视频/7.6/换装4/3/1751960755049.mp4", + "picture_from": "none", + "picture_set_category_id": "", + "picture_set_category_name": "", + "request_id": "", + "reverse_intensifies_path": "", + "reverse_path": "", + "smart_motion": null, + "source": 0, + "source_platform": 0, + "stable": { + "matrix_path": "", + "stable_level": 0, + "time_range": { + "duration": 0, + "start": 0 + } + }, + "team_id": "", + "type": "video", + "video_algorithm": { + "algorithms": [], + "complement_frame_config": null, + "deflicker": null, + "gameplay_configs": [], + "motion_blur_config": null, + "noise_reduction": null, + "path": "", + "quality_enhance": null, + "time_range": null + }, + "width": 704 + }, + { + "aigc_type": "none", + "audio_fade": null, + "cartoon_path": "", + "category_id": "", + "category_name": "local", + "check_flag": 63487, + "crop": { + "lower_left_x": 0.0, + "lower_left_y": 1.0, + "lower_right_x": 1.0, + "lower_right_y": 1.0, + "upper_left_x": 0.0, + "upper_left_y": 0.0, + "upper_right_x": 1.0, + "upper_right_y": 0.0 + }, + "crop_ratio": "free", + "crop_scale": 1.0, + "duration": 10066666, + "extra_type_option": 0, + "formula_id": "", + "freeze": null, + "has_audio": false, + "height": 1248, + "id": "C60E636D-802A-4595-A3E6-2CAC82F631AF", + "intensifies_audio_path": "", + "intensifies_path": "", + "is_ai_generate_content": false, + "is_copyright": true, + "is_text_edit_overdub": false, + "is_unified_beauty_mode": false, + "local_id": "", + "local_material_id": "78b1fac1-0255-4911-a217-239f857268e8", + "material_id": "", + "material_name": "1751960938780.mp4", + "material_url": "", + "matting": { + "flag": 0, + "has_use_quick_brush": false, + "has_use_quick_eraser": false, + "interactiveTime": [], + "path": "", + "strokes": [] + }, + "media_path": "", + "object_locked": null, + "origin_material_id": "", + "path": "D:/AI视频/7.6/换装4/3/1751960938780.mp4", + "picture_from": "none", + "picture_set_category_id": "", + "picture_set_category_name": "", + "request_id": "", + "reverse_intensifies_path": "", + "reverse_path": "", + "smart_motion": null, + "source": 0, + "source_platform": 0, + "stable": { + "matrix_path": "", + "stable_level": 0, + "time_range": { + "duration": 0, + "start": 0 + } + }, + "team_id": "", + "type": "video", + "video_algorithm": { + "algorithms": [], + "complement_frame_config": null, + "deflicker": null, + "gameplay_configs": [], + "motion_blur_config": null, + "noise_reduction": null, + "path": "", + "quality_enhance": null, + "time_range": null + }, + "width": 704 + }, + { + "aigc_type": "none", + "audio_fade": null, + "cartoon_path": "", + "category_id": "", + "category_name": "", + "check_flag": 63487, + "crop": { + "lower_left_x": 0.0, + "lower_left_y": 1.0, + "lower_right_x": 1.0, + "lower_right_y": 1.0, + "upper_left_x": 0.0, + "upper_left_y": 0.0, + "upper_right_x": 1.0, + "upper_right_y": 0.0 + }, + "crop_ratio": "free", + "crop_scale": 1.0, + "duration": 10800000000, + "extra_type_option": 0, + "formula_id": "", + "freeze": null, + "has_audio": false, + "height": 968, + "id": "88754D98-04E1-4ec6-B7BE-52DDE21ECF22", + "intensifies_audio_path": "", + "intensifies_path": "", + "is_ai_generate_content": false, + "is_copyright": true, + "is_text_edit_overdub": false, + "is_unified_beauty_mode": false, + "local_id": "", + "local_material_id": "", + "material_id": "", + "material_name": "", + "material_url": "", + "matting": { + "flag": 0, + "has_use_quick_brush": false, + "has_use_quick_eraser": false, + "interactiveTime": [], + "path": "", + "strokes": [] + }, + "media_path": "", + "object_locked": null, + "origin_material_id": "", + "path": "C:/Users/imeep/Desktop/v2/模版/贴图素材/白底-logo.png", + "picture_from": "none", + "picture_set_category_id": "", + "picture_set_category_name": "", + "request_id": "", + "reverse_intensifies_path": "", + "reverse_path": "", + "smart_motion": null, + "source": 0, + "source_platform": 0, + "stable": { + "matrix_path": "", + "stable_level": 0, + "time_range": { + "duration": 0, + "start": 0 + } + }, + "team_id": "", + "type": "photo", + "video_algorithm": { + "algorithms": [], + "complement_frame_config": null, + "deflicker": null, + "gameplay_configs": [], + "motion_blur_config": null, + "noise_reduction": null, + "path": "", + "quality_enhance": null, + "time_range": null + }, + "width": 5958 + } + ], + "vocal_beautifys": [], + "vocal_separations": [ + { + "choice": 0, + "id": "279F6DF1-45F1-4f29-B708-033136021BFE", + "production_path": "", + "time_range": null, + "type": "vocal_separation" + }, + { + "choice": 0, + "id": "54BC403C-9707-421b-B36D-982629D97592", + "production_path": "", + "time_range": null, + "type": "vocal_separation" + }, + { + "choice": 0, + "id": "640FB495-5B0D-4222-B367-9CED46C1C6D1", + "production_path": "", + "time_range": null, + "type": "vocal_separation" + }, + { + "choice": 0, + "id": "A4B4A9CA-2711-4331-B30F-11EBBF78B533", + "production_path": "", + "time_range": null, + "type": "vocal_separation" + }, + { + "choice": 0, + "id": "01782D85-8AD8-42a7-8A89-1D46E2AE659C", + "production_path": "", + "time_range": null, + "type": "vocal_separation" + }, + { + "choice": 0, + "id": "8B73AB84-2247-4b8d-86BB-AD602B8EDC3A", + "production_path": "", + "time_range": null, + "type": "vocal_separation" + }, + { + "choice": 0, + "id": "09D08E6C-EB2D-411f-ACB7-1B3FFE8E090C", + "production_path": "", + "time_range": null, + "type": "vocal_separation" + }, + { + "choice": 0, + "id": "167C8DE5-B592-4985-8CEB-438AA2585012", + "production_path": "", + "time_range": null, + "type": "vocal_separation" + }, + { + "choice": 0, + "id": "D2E387A2-92EE-4457-BF20-F90527B9DCBC", + "production_path": "", + "time_range": null, + "type": "vocal_separation" + }, + { + "choice": 0, + "id": "D4C9DAFC-40F5-4db9-8BD8-09B03A11AF78", + "production_path": "", + "time_range": null, + "type": "vocal_separation" + }, + { + "choice": 0, + "id": "C6BF7350-5D25-4fb1-A57F-0FCEF8C597D6", + "production_path": "", + "time_range": null, + "type": "vocal_separation" + } + ] + }, + "mutable_config": null, + "name": "", + "new_version": "110.0.0", + "platform": { + "app_id": 3704, + "app_source": "lv", + "app_version": "5.9.0", + "device_id": "043352ffb3f7f3a6166f1c560ccb0c22", + "hard_disk_id": "a653efd287e9c5c0ac1c865af1f6c50f", + "mac_address": "06134291d546eaa504ca39269b164540,266ae377f75863a9afde5068a15b2a7e", + "os": "windows", + "os_version": "10.0.26100" + }, + "relationships": [], + "render_index_track_mode_on": true, + "retouch_cover": null, + "source": "default", + "static_cover_image_path": "", + "time_marks": null, + "tracks": [ + { + "attribute": 0, + "flag": 0, + "id": "EA40A977-4E1B-4ca2-B47B-99689CE1BF4C", + "is_default_name": true, + "name": "", + "segments": [ + { + "caption_info": null, + "cartoon": false, + "clip": { + "alpha": 1.0, + "flip": { + "horizontal": false, + "vertical": false + }, + "rotation": 0.0, + "scale": { + "x": 1.0, + "y": 1.0 + }, + "transform": { + "x": 0.0, + "y": 0.0 + } + }, + "common_keyframes": [], + "enable_adjust": true, + "enable_color_correct_adjust": false, + "enable_color_curves": true, + "enable_color_match_adjust": false, + "enable_color_wheels": true, + "enable_lut": true, + "enable_smart_color_adjust": false, + "extra_material_refs": [ + "2B7B204C-F5C5-4890-8D6C-5A3A9C371BAD", + "70BCB27D-330C-4142-A23D-717F906B0D2C", + "BC56C74E-98B2-4403-A136-4007965D5EB5", + "9EFE6717-84A6-4bd7-A573-6660D53163B7", + "AB68626B-F4EE-4d49-A6B8-C82B0ED5A843", + "AAB0AB41-D66B-4785-8932-657DBA782A43", + "279F6DF1-45F1-4f29-B708-033136021BFE" + ], + "group_id": "", + "hdr_settings": { + "intensity": 1.0, + "mode": 1, + "nits": 1000 + }, + "id": "A060208B-35D5-42d9-B849-5489BE63838E", + "intensifies_audio": false, + "is_placeholder": false, + "is_tone_modify": false, + "keyframe_refs": [], + "last_nonzero_volume": 1.0, + "material_id": "F40AA450-A3A0-4025-97DE-4DB7EF226275", + "render_index": 0, + "responsive_layout": { + "enable": false, + "horizontal_pos_layout": 0, + "size_layout": 0, + "target_follow": "", + "vertical_pos_layout": 0 + }, + "reverse": false, + "source_timerange": { + "duration": 1026448, + "start": 733333 + }, + "speed": 1.28306, + "target_timerange": { + "duration": 800000, + "start": 0 + }, + "template_id": "", + "template_scene": "default", + "track_attribute": 0, + "track_render_index": 0, + "uniform_scale": { + "on": true, + "value": 1.0 + }, + "visible": true, + "volume": 1.0 + }, + { + "caption_info": null, + "cartoon": false, + "clip": { + "alpha": 1.0, + "flip": { + "horizontal": false, + "vertical": false + }, + "rotation": 0.0, + "scale": { + "x": 1.0, + "y": 1.0 + }, + "transform": { + "x": 0.0, + "y": 0.0 + } + }, + "common_keyframes": [], + "enable_adjust": true, + "enable_color_correct_adjust": false, + "enable_color_curves": true, + "enable_color_match_adjust": false, + "enable_color_wheels": true, + "enable_lut": true, + "enable_smart_color_adjust": false, + "extra_material_refs": [ + "8D5AB600-BB9B-4740-8D9A-08C3CE1C7113", + "8635E323-D35C-41b4-BDB4-4ECAC22DCFA0", + "699EF67C-B9C3-4445-B3C0-683A783455E0", + "44BA9ECA-DEC5-4f82-9292-B4C386714B53", + "6B22DA24-E4A7-4d04-B459-4328A3245EDF", + "2F8B9440-64FC-42d9-9215-1A640900B515", + "54BC403C-9707-421b-B36D-982629D97592" + ], + "group_id": "", + "hdr_settings": { + "intensity": 1.0, + "mode": 1, + "nits": 1000 + }, + "id": "4273EB31-BD59-4bd4-854A-8780DCBB92F8", + "intensifies_audio": false, + "is_placeholder": false, + "is_tone_modify": false, + "keyframe_refs": [], + "last_nonzero_volume": 1.0, + "material_id": "2B639AFE-2332-44f6-853D-AB9F8E9CB717", + "render_index": 0, + "responsive_layout": { + "enable": false, + "horizontal_pos_layout": 0, + "size_layout": 0, + "target_follow": "", + "vertical_pos_layout": 0 + }, + "reverse": false, + "source_timerange": { + "duration": 1058538, + "start": 2300000 + }, + "speed": 0.7560985714285714, + "target_timerange": { + "duration": 1400000, + "start": 800000 + }, + "template_id": "", + "template_scene": "default", + "track_attribute": 0, + "track_render_index": 0, + "uniform_scale": { + "on": true, + "value": 1.0 + }, + "visible": true, + "volume": 1.0 + }, + { + "caption_info": null, + "cartoon": false, + "clip": { + "alpha": 1.0, + "flip": { + "horizontal": false, + "vertical": false + }, + "rotation": 0.0, + "scale": { + "x": 1.1099999999999999, + "y": 1.1099999999999999 + }, + "transform": { + "x": 0.0, + "y": 0.07661691542288562 + } + }, + "common_keyframes": [ + { + "id": "B751840A-C4C2-4f7c-A114-3D11BCA039F9", + "keyframe_list": [ + { + "curveType": "Line", + "graphID": "", + "id": "D0EDB3C1-9861-43ce-B872-C067FC87297F", + "left_control": { + "x": 0.0, + "y": 0.0 + }, + "right_control": { + "x": 0.0, + "y": 0.0 + }, + "time_offset": 4100000, + "values": [ + 0.0 + ] + }, + { + "curveType": "Line", + "graphID": "", + "id": "1BECE669-15F4-4737-B534-A60C06215FE9", + "left_control": { + "x": 0.0, + "y": 0.0 + }, + "right_control": { + "x": 0.0, + "y": 0.0 + }, + "time_offset": 5466666, + "values": [ + 0.0 + ] + } + ], + "material_id": "", + "property_type": "KFTypePositionX" + }, + { + "id": "09EE94F9-25E4-4c78-A6CD-EEAB9FE858F7", + "keyframe_list": [ + { + "curveType": "Line", + "graphID": "", + "id": "1DBDD85A-C4AD-4005-92ED-A0A1148BE85E", + "left_control": { + "x": 0.0, + "y": 0.0 + }, + "right_control": { + "x": 0.0, + "y": 0.0 + }, + "time_offset": 4100000, + "values": [ + 0.0 + ] + }, + { + "curveType": "Line", + "graphID": "", + "id": "AE31A230-DAF1-4d4e-A306-7B4BFAA58556", + "left_control": { + "x": 0.0, + "y": 0.0 + }, + "right_control": { + "x": 0.0, + "y": 0.0 + }, + "time_offset": 5466666, + "values": [ + 0.07661691542288562 + ] + } + ], + "material_id": "", + "property_type": "KFTypePositionY" + }, + { + "id": "30E2248B-61E3-4d74-A0B3-A14E2CAFB9D5", + "keyframe_list": [ + { + "curveType": "Line", + "graphID": "", + "id": "B9CCAE2F-92F3-47f0-B81C-D30A50E2C666", + "left_control": { + "x": 0.0, + "y": 0.0 + }, + "right_control": { + "x": 0.0, + "y": 0.0 + }, + "time_offset": 4100000, + "values": [ + 1.0 + ] + }, + { + "curveType": "Line", + "graphID": "", + "id": "E79844D6-A7A7-4ed1-BFA6-4065C8403C0A", + "left_control": { + "x": 0.0, + "y": 0.0 + }, + "right_control": { + "x": 0.0, + "y": 0.0 + }, + "time_offset": 5466666, + "values": [ + 1.1099999999999999 + ] + } + ], + "material_id": "", + "property_type": "KFTypeScaleX" + }, + { + "id": "D6F0CBD0-453D-45c6-971A-CF3C020C1F59", + "keyframe_list": [ + { + "curveType": "Line", + "graphID": "", + "id": "0170B13F-362D-411f-A24C-7FFF9DBFA96C", + "left_control": { + "x": 0.0, + "y": 0.0 + }, + "right_control": { + "x": 0.0, + "y": 0.0 + }, + "time_offset": 4100000, + "values": [ + 0.0 + ] + } + ], + "material_id": "", + "property_type": "KFTypeRotation" + } + ], + "enable_adjust": true, + "enable_color_correct_adjust": false, + "enable_color_curves": true, + "enable_color_match_adjust": false, + "enable_color_wheels": true, + "enable_lut": true, + "enable_smart_color_adjust": false, + "extra_material_refs": [ + "E8E5E5C7-2527-4f76-9232-B8F8BCDE74B7", + "35D4F27D-2DA7-46f6-A756-2A5084855021", + "527F4ED7-30A7-4852-B8D0-407EEEDC16BC", + "CDFB999E-4155-42d3-8EFF-064CE026CC9E", + "3E5BCEF2-A2EB-4bc5-B641-B541F975266C", + "028F6C5E-49B2-4884-AD4D-9712D5D28B57", + "640FB495-5B0D-4222-B367-9CED46C1C6D1" + ], + "group_id": "", + "hdr_settings": { + "intensity": 1.0, + "mode": 1, + "nits": 1000 + }, + "id": "ACE5945D-91E6-434b-B6A8-01AC21434884", + "intensifies_audio": false, + "is_placeholder": false, + "is_tone_modify": false, + "keyframe_refs": [], + "last_nonzero_volume": 1.0, + "material_id": "0831DEC4-B257-4f10-AA7E-845C67C886F8", + "render_index": 0, + "responsive_layout": { + "enable": false, + "horizontal_pos_layout": 0, + "size_layout": 0, + "target_follow": "", + "vertical_pos_layout": 0 + }, + "reverse": false, + "source_timerange": { + "duration": 1566666, + "start": 4100000 + }, + "speed": 1.0, + "target_timerange": { + "duration": 1566666, + "start": 2200000 + }, + "template_id": "", + "template_scene": "default", + "track_attribute": 0, + "track_render_index": 0, + "uniform_scale": { + "on": true, + "value": 1.0 + }, + "visible": true, + "volume": 1.0 + }, + { + "caption_info": null, + "cartoon": false, + "clip": { + "alpha": 1.0, + "flip": { + "horizontal": false, + "vertical": false + }, + "rotation": 0.0, + "scale": { + "x": 1.0, + "y": 1.0 + }, + "transform": { + "x": 0.0, + "y": 0.0 + } + }, + "common_keyframes": [], + "enable_adjust": true, + "enable_color_correct_adjust": false, + "enable_color_curves": true, + "enable_color_match_adjust": false, + "enable_color_wheels": true, + "enable_lut": true, + "enable_smart_color_adjust": false, + "extra_material_refs": [ + "BA003FAB-0ECB-4772-B429-162F60D58BC6", + "A940EDCB-64C4-40e5-BE36-841149A9F89E", + "24D05FAA-CED8-482e-9815-AC47AC80CC39", + "4BA9D16C-B8C1-43f0-BBE7-DDE6816CF88B", + "F472C5C9-B908-4835-936F-741F29D61F34", + "F98842B2-1518-473e-B11C-D73BEC7E75A1", + "A4B4A9CA-2711-4331-B30F-11EBBF78B533" + ], + "group_id": "", + "hdr_settings": { + "intensity": 1.0, + "mode": 1, + "nits": 1000 + }, + "id": "E2E36E11-DEEB-43b1-94E0-F3C4FA1F04D4", + "intensifies_audio": false, + "is_placeholder": false, + "is_tone_modify": false, + "keyframe_refs": [], + "last_nonzero_volume": 1.0, + "material_id": "3E4EDBFE-4A9C-4b81-8B9A-2EDA2E26845D", + "render_index": 0, + "responsive_layout": { + "enable": false, + "horizontal_pos_layout": 0, + "size_layout": 0, + "target_follow": "", + "vertical_pos_layout": 0 + }, + "reverse": false, + "source_timerange": { + "duration": 1816668, + "start": 2133333 + }, + "speed": 1.4729732578522932, + "target_timerange": { + "duration": 1233334, + "start": 3766666 + }, + "template_id": "", + "template_scene": "default", + "track_attribute": 0, + "track_render_index": 0, + "uniform_scale": { + "on": true, + "value": 1.0 + }, + "visible": true, + "volume": 1.0 + }, + { + "caption_info": null, + "cartoon": false, + "clip": { + "alpha": 1.0, + "flip": { + "horizontal": false, + "vertical": false + }, + "rotation": 0.0, + "scale": { + "x": 1.0, + "y": 1.0 + }, + "transform": { + "x": 0.0, + "y": 0.0 + } + }, + "common_keyframes": [], + "enable_adjust": true, + "enable_color_correct_adjust": false, + "enable_color_curves": true, + "enable_color_match_adjust": false, + "enable_color_wheels": true, + "enable_lut": true, + "enable_smart_color_adjust": false, + "extra_material_refs": [ + "B94FD630-5C98-4be8-8B55-04A68F7A60D7", + "4BECF71F-BD2F-417e-A505-B27B33924E49", + "9D9A702A-1919-43b8-AB75-B11AAB966128", + "2DFCF3C1-8EB1-4833-B1E3-A880747C3E7C", + "1F5F19A1-CE79-4041-A31B-C08BEA9F4F6A", + "995EC006-DE65-42c1-AE7D-D4FA0BCD5466", + "01782D85-8AD8-42a7-8A89-1D46E2AE659C" + ], + "group_id": "", + "hdr_settings": { + "intensity": 1.0, + "mode": 1, + "nits": 1000 + }, + "id": "C41050B2-8CD4-447a-9FB6-088DBE11AF0F", + "intensifies_audio": false, + "is_placeholder": false, + "is_tone_modify": false, + "keyframe_refs": [], + "last_nonzero_volume": 1.0, + "material_id": "23A99095-A6A7-4fe3-91C2-289E693D457D", + "render_index": 0, + "responsive_layout": { + "enable": false, + "horizontal_pos_layout": 0, + "size_layout": 0, + "target_follow": "", + "vertical_pos_layout": 0 + }, + "reverse": false, + "source_timerange": { + "duration": 1085294, + "start": 6166666 + }, + "speed": 1.2058821193770943, + "target_timerange": { + "duration": 900000, + "start": 5000000 + }, + "template_id": "", + "template_scene": "default", + "track_attribute": 0, + "track_render_index": 0, + "uniform_scale": { + "on": true, + "value": 1.0 + }, + "visible": true, + "volume": 1.0 + }, + { + "caption_info": null, + "cartoon": false, + "clip": { + "alpha": 1.0, + "flip": { + "horizontal": false, + "vertical": false + }, + "rotation": 0.0, + "scale": { + "x": 1.16, + "y": 1.16 + }, + "transform": { + "x": 0.0, + "y": -0.05074626865671639 + } + }, + "common_keyframes": [ + { + "id": "083F38E6-70A4-4b61-ADFA-90A30FEBDE82", + "keyframe_list": [ + { + "curveType": "Line", + "graphID": "", + "id": "A3F66E25-1109-41e8-997F-A35AF580897E", + "left_control": { + "x": 0.0, + "y": 0.0 + }, + "right_control": { + "x": 0.0, + "y": 0.0 + }, + "time_offset": 4033333, + "values": [ + 0.0 + ] + }, + { + "curveType": "Line", + "graphID": "", + "id": "9C430899-7984-4c18-B7C8-6A860902F467", + "left_control": { + "x": 0.0, + "y": 0.0 + }, + "right_control": { + "x": 0.0, + "y": 0.0 + }, + "time_offset": 5333333, + "values": [ + 0.0 + ] + } + ], + "material_id": "", + "property_type": "KFTypePositionX" + }, + { + "id": "F2245B9E-4B76-4874-B8BC-BC6E45E33A83", + "keyframe_list": [ + { + "curveType": "Line", + "graphID": "", + "id": "9B464437-9AB8-4425-9398-4B10A581BFC8", + "left_control": { + "x": 0.0, + "y": 0.0 + }, + "right_control": { + "x": 0.0, + "y": 0.0 + }, + "time_offset": 4033333, + "values": [ + 0.0 + ] + }, + { + "curveType": "Line", + "graphID": "", + "id": "2F1B617A-F50A-4cee-9C3E-F46530CC653A", + "left_control": { + "x": 0.0, + "y": 0.0 + }, + "right_control": { + "x": 0.0, + "y": 0.0 + }, + "time_offset": 5333333, + "values": [ + -0.05074626865671639 + ] + } + ], + "material_id": "", + "property_type": "KFTypePositionY" + }, + { + "id": "2E7F81D2-E4A4-45ac-AD1C-1AAF3D247663", + "keyframe_list": [ + { + "curveType": "Line", + "graphID": "", + "id": "EA3B42FE-2CD4-4b85-88AF-C3FB4F32A1AB", + "left_control": { + "x": 0.0, + "y": 0.0 + }, + "right_control": { + "x": 0.0, + "y": 0.0 + }, + "time_offset": 4033333, + "values": [ + 1.0 + ] + }, + { + "curveType": "Line", + "graphID": "", + "id": "22D6D13C-DC24-4d3d-AA31-3BE433F0801B", + "left_control": { + "x": 0.0, + "y": 0.0 + }, + "right_control": { + "x": 0.0, + "y": 0.0 + }, + "time_offset": 5333333, + "values": [ + 1.16 + ] + } + ], + "material_id": "", + "property_type": "KFTypeScaleX" + }, + { + "id": "63111DE6-2EE7-457a-8204-DB031A7B4EAD", + "keyframe_list": [ + { + "curveType": "Line", + "graphID": "", + "id": "43CF62A3-64B0-48e8-BE96-044EC540BC1E", + "left_control": { + "x": 0.0, + "y": 0.0 + }, + "right_control": { + "x": 0.0, + "y": 0.0 + }, + "time_offset": 4033333, + "values": [ + 0.0 + ] + } + ], + "material_id": "", + "property_type": "KFTypeRotation" + } + ], + "enable_adjust": true, + "enable_color_correct_adjust": false, + "enable_color_curves": true, + "enable_color_match_adjust": false, + "enable_color_wheels": true, + "enable_lut": true, + "enable_smart_color_adjust": false, + "extra_material_refs": [ + "4D7263D8-81CC-4530-9479-2DF318504DF9", + "91D2C504-84EA-4997-A55B-C932B597AB53", + "835C4A28-FE80-4397-A307-8B0FE1DDEA02", + "A8404C7D-B0D5-449b-BDD9-CDF47ACE2DA0", + "528FBE3B-F2DA-4083-B9B6-6E6EC83E6D8B", + "8B73AB84-2247-4b8d-86BB-AD602B8EDC3A" + ], + "group_id": "", + "hdr_settings": { + "intensity": 1.0, + "mode": 1, + "nits": 1000 + }, + "id": "4702D642-DEC5-4453-89D7-F60AC715D939", + "intensifies_audio": false, + "is_placeholder": false, + "is_tone_modify": false, + "keyframe_refs": [], + "last_nonzero_volume": 1.0, + "material_id": "44A8FD1E-D5C7-4b45-B75B-CF7828863A64", + "render_index": 0, + "responsive_layout": { + "enable": false, + "horizontal_pos_layout": 0, + "size_layout": 0, + "target_follow": "", + "vertical_pos_layout": 0 + }, + "reverse": false, + "source_timerange": { + "duration": 1333333, + "start": 4033333 + }, + "speed": 1.0, + "target_timerange": { + "duration": 1333333, + "start": 5900000 + }, + "template_id": "", + "template_scene": "default", + "track_attribute": 0, + "track_render_index": 0, + "uniform_scale": { + "on": true, + "value": 1.0 + }, + "visible": true, + "volume": 1.0 + }, + { + "caption_info": null, + "cartoon": false, + "clip": { + "alpha": 1.0, + "flip": { + "horizontal": false, + "vertical": false + }, + "rotation": 0.0, + "scale": { + "x": 1.0, + "y": 1.0 + }, + "transform": { + "x": 0.0, + "y": 0.0 + } + }, + "common_keyframes": [], + "enable_adjust": true, + "enable_color_correct_adjust": false, + "enable_color_curves": true, + "enable_color_match_adjust": false, + "enable_color_wheels": true, + "enable_lut": true, + "enable_smart_color_adjust": false, + "extra_material_refs": [ + "9C590E1D-32DD-4335-BC82-0CBDEC9579D6", + "872D6D55-0C73-452c-BD1C-5F6FCDBEDCF6", + "0FFDB33E-A55F-49d4-A938-253127CEA7D0", + "280D2EC5-AD9C-46b6-BFF2-1EE9C0DB4C91", + "F9E4AC34-E06F-495d-8D84-686F00E30EB6", + "09D08E6C-EB2D-411f-ACB7-1B3FFE8E090C" + ], + "group_id": "", + "hdr_settings": { + "intensity": 1.0, + "mode": 1, + "nits": 1000 + }, + "id": "E9AAA801-79E7-4937-B3D4-2E8534FAC4B9", + "intensifies_audio": false, + "is_placeholder": false, + "is_tone_modify": false, + "keyframe_refs": [], + "last_nonzero_volume": 1.0, + "material_id": "B3462906-E721-4a43-9DF5-E09EB0694C12", + "render_index": 0, + "responsive_layout": { + "enable": false, + "horizontal_pos_layout": 0, + "size_layout": 0, + "target_follow": "", + "vertical_pos_layout": 0 + }, + "reverse": false, + "source_timerange": { + "duration": 1140607, + "start": 4366666 + }, + "speed": 0.9505058333333334, + "target_timerange": { + "duration": 1200000, + "start": 7233333 + }, + "template_id": "", + "template_scene": "default", + "track_attribute": 0, + "track_render_index": 0, + "uniform_scale": { + "on": true, + "value": 1.0 + }, + "visible": true, + "volume": 1.0 + }, + { + "caption_info": null, + "cartoon": false, + "clip": { + "alpha": 1.0, + "flip": { + "horizontal": false, + "vertical": false + }, + "rotation": 0.0, + "scale": { + "x": 1.0559005849316025, + "y": 1.0559005849316025 + }, + "transform": { + "x": -0.05590058493160255, + "y": -0.05290086736077426 + } + }, + "common_keyframes": [], + "enable_adjust": true, + "enable_color_correct_adjust": false, + "enable_color_curves": true, + "enable_color_match_adjust": false, + "enable_color_wheels": true, + "enable_lut": true, + "enable_smart_color_adjust": false, + "extra_material_refs": [ + "4E6B4A3C-7263-4f38-8D60-F347F62BE50D", + "CACD8957-3030-410a-94CD-CDF488A394FD", + "B5692776-FDF0-4a7e-9B66-5C646CF4ED43", + "57A4AF29-448A-4b39-AE73-E292C3BF6B0A", + "DD1AD60F-06EE-45dd-AF94-DC36222FC206", + "167C8DE5-B592-4985-8CEB-438AA2585012" + ], + "group_id": "", + "hdr_settings": { + "intensity": 1.0, + "mode": 1, + "nits": 1000 + }, + "id": "B34D08E7-7244-4752-BBAE-D7997F490F11", + "intensifies_audio": false, + "is_placeholder": false, + "is_tone_modify": false, + "keyframe_refs": [], + "last_nonzero_volume": 1.0, + "material_id": "EE337662-05D3-4efc-9723-F0A0EE93E723", + "render_index": 0, + "responsive_layout": { + "enable": false, + "horizontal_pos_layout": 0, + "size_layout": 0, + "target_follow": "", + "vertical_pos_layout": 0 + }, + "reverse": false, + "source_timerange": { + "duration": 1066667, + "start": 6833333 + }, + "speed": 1.0, + "target_timerange": { + "duration": 1066667, + "start": 8433333 + }, + "template_id": "", + "template_scene": "default", + "track_attribute": 0, + "track_render_index": 0, + "uniform_scale": { + "on": true, + "value": 1.0 + }, + "visible": true, + "volume": 1.0 + }, + { + "caption_info": null, + "cartoon": false, + "clip": { + "alpha": 1.0, + "flip": { + "horizontal": false, + "vertical": false + }, + "rotation": 0.0, + "scale": { + "x": 1.0992980789529239, + "y": 1.0992980789529239 + }, + "transform": { + "x": 0.0, + "y": 0.052736318407960225 + } + }, + "common_keyframes": [], + "enable_adjust": true, + "enable_color_correct_adjust": false, + "enable_color_curves": true, + "enable_color_match_adjust": false, + "enable_color_wheels": true, + "enable_lut": true, + "enable_smart_color_adjust": false, + "extra_material_refs": [ + "67810649-FFF6-4b8a-AA59-D11A88DE94DD", + "A15E73E3-DB98-4cf5-8B81-CCADAF7DB80C", + "3E72214A-A2BC-4f0e-8CEE-97D0669725D0", + "37D1BED1-BB0C-418a-9B2B-BBC6F762AC08", + "FDBCBC6D-9BE4-4c81-A887-1253DAB8CEF8", + "D2E387A2-92EE-4457-BF20-F90527B9DCBC" + ], + "group_id": "", + "hdr_settings": { + "intensity": 1.0, + "mode": 1, + "nits": 1000 + }, + "id": "72B19A7C-064F-4ca0-91E5-6F70925896FC", + "intensifies_audio": false, + "is_placeholder": false, + "is_tone_modify": false, + "keyframe_refs": [], + "last_nonzero_volume": 1.0, + "material_id": "C60E636D-802A-4595-A3E6-2CAC82F631AF", + "render_index": 0, + "responsive_layout": { + "enable": false, + "horizontal_pos_layout": 0, + "size_layout": 0, + "target_follow": "", + "vertical_pos_layout": 0 + }, + "reverse": false, + "source_timerange": { + "duration": 1266667, + "start": 8133333 + }, + "speed": 1.0, + "target_timerange": { + "duration": 1266666, + "start": 9500000 + }, + "template_id": "", + "template_scene": "default", + "track_attribute": 0, + "track_render_index": 0, + "uniform_scale": { + "on": true, + "value": 1.0 + }, + "visible": true, + "volume": 1.0 + } + ], + "type": "video" + }, + { + "attribute": 0, + "flag": 0, + "id": "FD9BC8D8-A60D-4bf5-9C86-C0832E386824", + "is_default_name": true, + "name": "", + "segments": [ + { + "caption_info": null, + "cartoon": false, + "clip": null, + "common_keyframes": [], + "enable_adjust": false, + "enable_color_correct_adjust": false, + "enable_color_curves": true, + "enable_color_match_adjust": false, + "enable_color_wheels": true, + "enable_lut": false, + "enable_smart_color_adjust": false, + "extra_material_refs": [], + "group_id": "", + "hdr_settings": null, + "id": "BD45D1B2-6E51-47e0-8C90-811EC2137A8E", + "intensifies_audio": false, + "is_placeholder": false, + "is_tone_modify": false, + "keyframe_refs": [], + "last_nonzero_volume": 1.0, + "material_id": "D767E308-A05A-4516-AAD3-CB63864FB4A4", + "render_index": 11002, + "responsive_layout": { + "enable": false, + "horizontal_pos_layout": 0, + "size_layout": 0, + "target_follow": "", + "vertical_pos_layout": 0 + }, + "reverse": false, + "source_timerange": null, + "speed": 1.0, + "target_timerange": { + "duration": 1400000, + "start": 800000 + }, + "template_id": "", + "template_scene": "default", + "track_attribute": 0, + "track_render_index": 1, + "uniform_scale": null, + "visible": true, + "volume": 1.0 + } + ], + "type": "effect" + }, + { + "attribute": 0, + "flag": 2, + "id": "3BD51BC8-3446-4c7f-9A37-210DAD138DEA", + "is_default_name": true, + "name": "", + "segments": [ + { + "caption_info": null, + "cartoon": false, + "clip": { + "alpha": 1.0, + "flip": { + "horizontal": false, + "vertical": false + }, + "rotation": 0.0, + "scale": { + "x": 0.6946189001916556, + "y": 0.6946189001916556 + }, + "transform": { + "x": 0.0, + "y": 0.0 + } + }, + "common_keyframes": [], + "enable_adjust": true, + "enable_color_correct_adjust": false, + "enable_color_curves": true, + "enable_color_match_adjust": false, + "enable_color_wheels": true, + "enable_lut": true, + "enable_smart_color_adjust": false, + "extra_material_refs": [ + "F75BC4E5-12FF-4653-A7A5-6A14BABC14ED", + "43FAC945-26E7-42a0-85B1-0DB387A8CDE1", + "1D94666F-2F21-4ced-AA13-D65280870DEF", + "2D89584C-D6ED-4ba8-BC33-4EEC959E0EBC", + "07AF2049-2A24-467a-BE22-0B4FDAB1E139", + "D4C9DAFC-40F5-4db9-8BD8-09B03A11AF78" + ], + "group_id": "", + "hdr_settings": { + "intensity": 1.0, + "mode": 1, + "nits": 1000 + }, + "id": "A8BCA3E9-4EBD-4679-B3BE-71ADDBDBFBB3", + "intensifies_audio": false, + "is_placeholder": false, + "is_tone_modify": false, + "keyframe_refs": [], + "last_nonzero_volume": 1.0, + "material_id": "88754D98-04E1-4ec6-B7BE-52DDE21ECF22", + "render_index": 1, + "responsive_layout": { + "enable": false, + "horizontal_pos_layout": 0, + "size_layout": 0, + "target_follow": "", + "vertical_pos_layout": 0 + }, + "reverse": false, + "source_timerange": { + "duration": 600000, + "start": 0 + }, + "speed": 1.0, + "target_timerange": { + "duration": 600000, + "start": 10166666 + }, + "template_id": "", + "template_scene": "default", + "track_attribute": 0, + "track_render_index": 2, + "uniform_scale": { + "on": true, + "value": 1.0 + }, + "visible": true, + "volume": 1.0 + } + ], + "type": "video" + }, + { + "attribute": 0, + "flag": 0, + "id": "BA1EC32B-6B13-4920-BF98-626194CBA6B4", + "is_default_name": true, + "name": "", + "segments": [ + { + "caption_info": null, + "cartoon": false, + "clip": { + "alpha": 1.0, + "flip": { + "horizontal": false, + "vertical": false + }, + "rotation": 0.0, + "scale": { + "x": 0.453349079902575, + "y": 0.453349079902575 + }, + "transform": { + "x": 0.0, + "y": -0.09375 + } + }, + "common_keyframes": [], + "enable_adjust": false, + "enable_color_correct_adjust": false, + "enable_color_curves": true, + "enable_color_match_adjust": false, + "enable_color_wheels": true, + "enable_lut": false, + "enable_smart_color_adjust": false, + "extra_material_refs": [ + "63CF0C77-3A2B-4ea9-8827-F79504CEF085" + ], + "group_id": "", + "hdr_settings": null, + "id": "D8767F24-4E23-4684-83D1-42C7C0A025D3", + "intensifies_audio": false, + "is_placeholder": false, + "is_tone_modify": false, + "keyframe_refs": [], + "last_nonzero_volume": 1.0, + "material_id": "B3537E2F-8CF5-400e-966E-50A87EE46374", + "render_index": 14000, + "responsive_layout": { + "enable": false, + "horizontal_pos_layout": 0, + "size_layout": 0, + "target_follow": "", + "vertical_pos_layout": 0 + }, + "reverse": false, + "source_timerange": null, + "speed": 1.0, + "target_timerange": { + "duration": 10166666, + "start": 0 + }, + "template_id": "", + "template_scene": "default", + "track_attribute": 0, + "track_render_index": 3, + "uniform_scale": { + "on": true, + "value": 1.0 + }, + "visible": true, + "volume": 1.0 + } + ], + "type": "text" + }, + { + "attribute": 0, + "flag": 0, + "id": "1EB5BB4F-71F0-43ef-9A36-7925DC2BDD64", + "is_default_name": true, + "name": "", + "segments": [ + { + "caption_info": null, + "cartoon": false, + "clip": { + "alpha": 1.0, + "flip": { + "horizontal": false, + "vertical": false + }, + "rotation": 0.0, + "scale": { + "x": 0.19973412008693628, + "y": 0.19973412008693628 + }, + "transform": { + "x": 0.7007168458781361, + "y": -0.09375 + } + }, + "common_keyframes": [], + "enable_adjust": false, + "enable_color_correct_adjust": false, + "enable_color_curves": true, + "enable_color_match_adjust": false, + "enable_color_wheels": true, + "enable_lut": false, + "enable_smart_color_adjust": false, + "extra_material_refs": [], + "group_id": "", + "hdr_settings": null, + "id": "8480AA92-623C-4c7f-B526-D848C3D97F8E", + "intensifies_audio": false, + "is_placeholder": false, + "is_tone_modify": false, + "keyframe_refs": [], + "last_nonzero_volume": 1.0, + "material_id": "92160682-A5E8-4211-A503-35CFCB489346", + "render_index": 14001, + "responsive_layout": { + "enable": false, + "horizontal_pos_layout": 0, + "size_layout": 0, + "target_follow": "", + "vertical_pos_layout": 0 + }, + "reverse": false, + "source_timerange": null, + "speed": 1.0, + "target_timerange": { + "duration": 10166666, + "start": 0 + }, + "template_id": "", + "template_scene": "default", + "track_attribute": 0, + "track_render_index": 4, + "uniform_scale": { + "on": true, + "value": 1.0 + }, + "visible": true, + "volume": 1.0 + } + ], + "type": "sticker" + }, + { + "attribute": 0, + "flag": 0, + "id": "49D7621B-D965-42af-A79E-236D1D2A3494", + "is_default_name": true, + "name": "", + "segments": [ + { + "caption_info": null, + "cartoon": false, + "clip": null, + "common_keyframes": [], + "enable_adjust": false, + "enable_color_correct_adjust": false, + "enable_color_curves": true, + "enable_color_match_adjust": false, + "enable_color_wheels": true, + "enable_lut": false, + "enable_smart_color_adjust": false, + "extra_material_refs": [ + "D46D7DF1-D84A-4e5f-9A0B-E31C93A45659", + "ABA5CA59-584A-4c2c-AC7A-60C6BE9B177C", + "5862328C-A939-4e9c-ADA8-D9BE5DBB39D9", + "E1C93883-23BB-4605-80BB-A7FDA94F162B", + "B0D09A80-2B29-473f-8EAB-AE311670CF0F", + "56186FA4-1459-45bb-B686-2A638620DB69", + "C6BF7350-5D25-4fb1-A57F-0FCEF8C597D6" + ], + "group_id": "", + "hdr_settings": null, + "id": "8B3613FE-3F6B-4904-9D8B-B72127984EC2", + "intensifies_audio": false, + "is_placeholder": false, + "is_tone_modify": false, + "keyframe_refs": [], + "last_nonzero_volume": 1.0, + "material_id": "7B9ED69A-3E48-4291-91E0-8480464BC337", + "render_index": 0, + "responsive_layout": { + "enable": false, + "horizontal_pos_layout": 0, + "size_layout": 0, + "target_follow": "", + "vertical_pos_layout": 0 + }, + "reverse": false, + "source_timerange": { + "duration": 10766666, + "start": 0 + }, + "speed": 1.0, + "target_timerange": { + "duration": 10766666, + "start": 0 + }, + "template_id": "", + "template_scene": "default", + "track_attribute": 0, + "track_render_index": 0, + "uniform_scale": null, + "visible": true, + "volume": 1.0 + } + ], + "type": "audio" + } + ], + "update_time": 0, + "version": 360000 +} \ No newline at end of file diff --git a/python_core/services/__init__.py b/python_core/services/__init__.py index 85c66f3..a94d3a5 100644 --- a/python_core/services/__init__.py +++ b/python_core/services/__init__.py @@ -5,14 +5,35 @@ Services Module Provides background services, file management, and task queue functionality. """ -from .file_manager import FileManager -from .task_queue import TaskQueue -from .project_manager import ProjectManager -from .cache_manager import CacheManager +try: + from .file_manager import FileManager +except ImportError: + FileManager = None + +try: + from .task_queue import TaskQueue +except ImportError: + TaskQueue = None + +try: + from .project_manager import ProjectManager +except ImportError: + ProjectManager = None + +try: + from .cache_manager import CacheManager +except ImportError: + CacheManager = None + +try: + from .template_manager import TemplateManager +except ImportError: + TemplateManager = None __all__ = [ "FileManager", "TaskQueue", - "ProjectManager", - "CacheManager" + "ProjectManager", + "CacheManager", + "TemplateManager" ] diff --git a/python_core/services/template_manager.py b/python_core/services/template_manager.py new file mode 100644 index 0000000..a34bd7b --- /dev/null +++ b/python_core/services/template_manager.py @@ -0,0 +1,448 @@ +""" +Template Management Service +Handles template import, processing, and management +""" + +import os +import json +import shutil +import uuid +from pathlib import Path +from typing import Dict, List, Any, Optional +from dataclasses import dataclass, asdict +from datetime import datetime + +from ..utils.logger import setup_logger +from ..config import settings + +logger = setup_logger(__name__) + + +@dataclass +class TemplateInfo: + """Template information structure""" + id: str + name: str + description: str + thumbnail_path: str + draft_content_path: str + resources_path: str + created_at: str + updated_at: str + canvas_config: Dict[str, Any] + duration: int + material_count: int + track_count: int + tags: List[str] + + +@dataclass +class MaterialInfo: + """Material information structure""" + id: str + material_id: str + name: str + type: str # video, audio, image, text, sticker, etc. + original_path: str + relative_path: str + duration: Optional[int] = None + size: Optional[int] = None + + +class TemplateManager: + """Template management service""" + + def __init__(self): + self.templates_dir = settings.temp_dir / "templates" + self.templates_dir.mkdir(parents=True, exist_ok=True) + + # Template metadata file + self.metadata_file = self.templates_dir / "templates.json" + self.templates_metadata = self._load_metadata() + + def _load_metadata(self) -> Dict[str, Dict]: + """Load templates metadata""" + if self.metadata_file.exists(): + try: + with open(self.metadata_file, 'r', encoding='utf-8') as f: + return json.load(f) + except Exception as e: + logger.error(f"Failed to load templates metadata: {e}") + return {} + + def _save_metadata(self): + """Save templates metadata""" + try: + with open(self.metadata_file, 'w', encoding='utf-8') as f: + json.dump(self.templates_metadata, f, ensure_ascii=False, indent=2) + except Exception as e: + logger.error(f"Failed to save templates metadata: {e}") + + def batch_import_templates(self, source_folder: str, progress_callback=None) -> Dict[str, Any]: + """ + Batch import templates from a folder + + Args: + source_folder: Source folder containing template subfolders + progress_callback: Progress callback function + + Returns: + Import result with success/failure counts and details + """ + result = { + 'status': True, + 'msg': '', + 'imported_count': 0, + 'failed_count': 0, + 'imported_templates': [], + 'failed_templates': [] + } + + try: + if not os.path.exists(source_folder): + result['status'] = False + result['msg'] = f"Source folder does not exist: {source_folder}" + return result + + # Find all subdirectories + subdirs = [d for d in os.listdir(source_folder) + if os.path.isdir(os.path.join(source_folder, d))] + + if not subdirs: + result['status'] = False + result['msg'] = "No subdirectories found in source folder" + return result + + total_templates = len(subdirs) + logger.info(f"Found {total_templates} potential templates in {source_folder}") + + if progress_callback: + progress_callback(f"Found {total_templates} potential templates") + + for i, template_dir in enumerate(subdirs): + template_path = os.path.join(source_folder, template_dir) + + if progress_callback: + progress_callback(f"Processing template {i+1}/{total_templates}: {template_dir}") + + try: + template_info = self._import_single_template(template_path, template_dir) + if template_info: + result['imported_count'] += 1 + result['imported_templates'].append(template_info) + logger.info(f"Successfully imported template: {template_dir}") + else: + result['failed_count'] += 1 + result['failed_templates'].append({ + 'name': template_dir, + 'error': 'No draft_content.json found or invalid template' + }) + logger.warning(f"Failed to import template: {template_dir}") + + except Exception as e: + result['failed_count'] += 1 + result['failed_templates'].append({ + 'name': template_dir, + 'error': str(e) + }) + logger.error(f"Error importing template {template_dir}: {e}") + + # Save metadata + self._save_metadata() + + result['msg'] = f"Import completed. Success: {result['imported_count']}, Failed: {result['failed_count']}" + logger.info(result['msg']) + + if progress_callback: + progress_callback(result['msg']) + + except Exception as e: + result['status'] = False + result['msg'] = f"Batch import failed: {str(e)}" + logger.error(result['msg']) + + return result + + def _import_single_template(self, template_path: str, template_name: str) -> Optional[TemplateInfo]: + """ + Import a single template + + Args: + template_path: Path to template folder + template_name: Template name + + Returns: + TemplateInfo if successful, None otherwise + """ + try: + # Check for draft_content.json + draft_file = os.path.join(template_path, "draft_content.json") + if not os.path.exists(draft_file): + logger.warning(f"No draft_content.json found in {template_path}") + return None + + # Load and parse draft content + with open(draft_file, 'r', encoding='utf-8') as f: + draft_content = json.load(f) + + # Generate template ID + template_id = str(uuid.uuid4()) + + # Create template directory + template_dir = self.templates_dir / template_id + template_dir.mkdir(parents=True, exist_ok=True) + + # Create resources directory + resources_dir = template_dir / "resources" + resources_dir.mkdir(exist_ok=True) + + # Process materials and copy resources + materials = self._process_materials(draft_content, template_path, resources_dir) + + # Update draft content with relative paths + updated_draft = self._update_draft_paths(draft_content, materials) + + # Save updated draft content + draft_target = template_dir / "draft_content.json" + with open(draft_target, 'w', encoding='utf-8') as f: + json.dump(updated_draft, f, ensure_ascii=False, indent=2) + + # Create template info + template_info = TemplateInfo( + id=template_id, + name=template_name, + description=f"Imported template: {template_name}", + thumbnail_path="", # TODO: Generate thumbnail + draft_content_path=str(draft_target), + resources_path=str(resources_dir), + created_at=datetime.now().isoformat(), + updated_at=datetime.now().isoformat(), + canvas_config=draft_content.get('canvas_config', {}), + duration=draft_content.get('duration', 0), + material_count=len(materials), + track_count=len(draft_content.get('tracks', [])), + tags=[] + ) + + # Save to metadata + self.templates_metadata[template_id] = asdict(template_info) + + logger.info(f"Successfully processed template: {template_name} -> {template_id}") + return template_info + + except Exception as e: + logger.error(f"Failed to import template {template_name}: {e}") + return None + + def _process_materials(self, draft_content: Dict, source_path: str, resources_dir: Path) -> List[MaterialInfo]: + """ + Process and copy materials from draft content + + Args: + draft_content: Draft content JSON + source_path: Source template path + resources_dir: Target resources directory + + Returns: + List of processed materials + """ + materials = [] + materials_section = draft_content.get('materials', {}) + + # Process different material types + material_types = ['videos', 'audios', 'images', 'stickers', 'texts'] + + for material_type in material_types: + if material_type in materials_section: + for material in materials_section[material_type]: + material_info = self._process_single_material( + material, material_type, source_path, resources_dir + ) + if material_info: + materials.append(material_info) + + return materials + + def _process_single_material(self, material: Dict, material_type: str, + source_path: str, resources_dir: Path) -> Optional[MaterialInfo]: + """Process a single material""" + try: + material_id = material.get('id') + if not material_id: + return None + + # Get original path + original_path = material.get('path', '') + if not original_path or not os.path.exists(original_path): + logger.warning(f"Material path not found: {original_path}") + return None + + # Generate relative path + filename = os.path.basename(original_path) + relative_path = f"resources/{filename}" + target_path = resources_dir / filename + + # Copy file + shutil.copy2(original_path, target_path) + + # Get file info + file_size = os.path.getsize(target_path) + + material_info = MaterialInfo( + id=str(uuid.uuid4()), + material_id=material_id, + name=material.get('name', filename), + type=material_type, + original_path=original_path, + relative_path=relative_path, + size=file_size + ) + + logger.debug(f"Processed material: {filename} -> {relative_path}") + return material_info + + except Exception as e: + logger.error(f"Failed to process material: {e}") + return None + + def _update_draft_paths(self, draft_content: Dict, materials: List[MaterialInfo]) -> Dict: + """Update draft content with relative paths""" + # Create material ID to relative path mapping + path_mapping = {mat.material_id: mat.relative_path for mat in materials} + + # Update materials section + materials_section = draft_content.get('materials', {}) + for material_type in materials_section: + if isinstance(materials_section[material_type], list): + for material in materials_section[material_type]: + material_id = material.get('id') + if material_id in path_mapping: + material['path'] = path_mapping[material_id] + + return draft_content + + def get_templates(self) -> List[TemplateInfo]: + """Get all templates""" + templates = [] + for template_data in self.templates_metadata.values(): + templates.append(TemplateInfo(**template_data)) + return templates + + def get_template(self, template_id: str) -> Optional[TemplateInfo]: + """Get a specific template""" + if template_id in self.templates_metadata: + return TemplateInfo(**self.templates_metadata[template_id]) + return None + + def delete_template(self, template_id: str) -> bool: + """Delete a template""" + try: + if template_id in self.templates_metadata: + # Remove template directory + template_dir = self.templates_dir / template_id + if template_dir.exists(): + shutil.rmtree(template_dir) + + # Remove from metadata + del self.templates_metadata[template_id] + self._save_metadata() + + logger.info(f"Deleted template: {template_id}") + return True + except Exception as e: + logger.error(f"Failed to delete template {template_id}: {e}") + return False + + +def main(): + """Command line interface for template management.""" + import argparse + import json + import sys + + parser = argparse.ArgumentParser(description="Template Manager") + parser.add_argument("--action", required=True, help="Action to perform") + parser.add_argument("--source_folder", help="Source folder for batch import") + parser.add_argument("--template_id", help="Template ID for operations") + + args = parser.parse_args() + + try: + manager = TemplateManager() + + if args.action == "batch_import": + if not args.source_folder: + raise ValueError("Source folder is required for batch import") + + def progress_callback(message): + print(message) + + result = manager.batch_import_templates(args.source_folder, progress_callback) + # Convert TemplateInfo objects to dictionaries for JSON serialization + if 'imported_templates' in result: + result['imported_templates'] = [asdict(template) for template in result['imported_templates']] + + elif args.action == "get_templates": + templates = manager.get_templates() + result = { + "status": True, + "templates": [asdict(template) for template in templates] + } + + elif args.action == "get_template": + if not args.template_id: + raise ValueError("Template ID is required") + + template = manager.get_template(args.template_id) + if template: + result = { + "status": True, + "template": asdict(template) + } + else: + result = { + "status": False, + "msg": f"Template not found: {args.template_id}" + } + + elif args.action == "delete_template": + if not args.template_id: + raise ValueError("Template ID is required") + + success = manager.delete_template(args.template_id) + result = { + "status": success, + "msg": "Template deleted successfully" if success else "Failed to delete template" + } + + else: + raise ValueError(f"Unknown action: {args.action}") + + # Use safe encoding for final output + result_json = json.dumps(result, ensure_ascii=True, indent=2) + if hasattr(sys.stdout, 'buffer'): + sys.stdout.buffer.write(result_json.encode('utf-8')) + sys.stdout.buffer.write(b'\n') + sys.stdout.buffer.flush() + else: + print(result_json) + sys.stdout.flush() + + except Exception as e: + error_result = { + "status": False, + "error": str(e) + } + error_json = json.dumps(error_result, ensure_ascii=True, indent=2) + if hasattr(sys.stdout, 'buffer'): + sys.stdout.buffer.write(error_json.encode('utf-8')) + sys.stdout.buffer.write(b'\n') + sys.stdout.buffer.flush() + else: + print(error_json) + sys.stdout.flush() + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/src-tauri/src/commands/mod.rs b/src-tauri/src/commands/mod.rs index b0d03bd..a671415 100644 --- a/src-tauri/src/commands/mod.rs +++ b/src-tauri/src/commands/mod.rs @@ -4,6 +4,7 @@ pub mod video; pub mod ai_video; pub mod file_system; pub mod project; +pub mod template; // Re-export all commands for easy access pub use basic::*; @@ -11,3 +12,4 @@ pub use video::*; pub use ai_video::*; pub use file_system::*; pub use project::*; +pub use template::*; diff --git a/src-tauri/src/commands/template.rs b/src-tauri/src/commands/template.rs new file mode 100644 index 0000000..8a21f7e --- /dev/null +++ b/src-tauri/src/commands/template.rs @@ -0,0 +1,349 @@ +use serde::{Deserialize, Serialize}; +use std::process::Command; +use std::io::{BufRead, BufReader, Read}; +use std::time::{Duration, Instant}; +use std::thread; +use std::sync::{Arc, Mutex}; +use std::sync::mpsc; + +#[derive(Debug, Deserialize)] +pub struct BatchImportRequest { + pub source_folder: String, +} + +#[derive(Debug, Serialize)] +pub struct TemplateInfo { + pub id: String, + pub name: String, + pub description: String, + pub thumbnail_path: String, + pub draft_content_path: String, + pub resources_path: String, + pub created_at: String, + pub updated_at: String, + pub canvas_config: serde_json::Value, + pub duration: i64, + pub material_count: i32, + pub track_count: i32, + pub tags: Vec, +} + +async fn execute_python_template_command(_app: tauri::AppHandle, args: &[String]) -> Result { + println!("Executing Python template command with args: {:?}", args); + + // Get project root directory + let current_dir = std::env::current_dir() + .map_err(|e| format!("Failed to get current directory: {}", e))?; + + let project_root = if current_dir.ends_with("src-tauri") { + current_dir.parent().unwrap_or(¤t_dir).to_path_buf() + } else { + current_dir + }; + + println!("Working directory: {:?}", project_root); + + // Try multiple Python commands in order of preference + let python_commands = if cfg!(target_os = "windows") { + vec!["python", "python3", "py"] + } else { + vec!["python3", "python"] + }; + + let mut child = None; + let mut last_error = String::new(); + + for python_cmd in python_commands { + println!("Trying Python command: {}", python_cmd); + let mut cmd = Command::new(python_cmd); + cmd.current_dir(&project_root); + cmd.args(args); + cmd.stdout(std::process::Stdio::piped()); + cmd.stderr(std::process::Stdio::piped()); + + // Set environment variables for consistent encoding + cmd.env("PYTHONIOENCODING", "utf-8"); + cmd.env("PYTHONUNBUFFERED", "1"); + + // On Windows, ensure UTF-8 console output + if cfg!(target_os = "windows") { + cmd.env("PYTHONUTF8", "1"); + } + + match cmd.spawn() { + Ok(process) => { + println!("Successfully started Python process with: {}", python_cmd); + child = Some(process); + break; + } + Err(e) => { + last_error = format!("Failed to start {} process: {}", python_cmd, e); + println!("{}", last_error); + continue; + } + } + } + + let mut child = child.ok_or_else(|| { + format!("Failed to start Python process with any command. Last error: {}", last_error) + })?; + + let stdout = child.stdout.take().unwrap(); + let stderr = child.stderr.take().unwrap(); + + // Use channels for concurrent reading + let (stdout_tx, _stdout_rx) = mpsc::channel(); + let (stderr_tx, _stderr_rx) = mpsc::channel(); + let (result_tx, result_rx) = mpsc::channel(); + + let progress_messages = Arc::new(Mutex::new(Vec::new())); + let error_messages = Arc::new(Mutex::new(Vec::new())); + let final_result = Arc::new(Mutex::new(None::)); + + // Spawn thread for reading stdout + let progress_messages_clone = Arc::clone(&progress_messages); + let final_result_clone = Arc::clone(&final_result); + thread::spawn(move || { + let mut reader = BufReader::new(stdout); + let mut buffer = Vec::new(); + + // Read raw bytes and handle encoding issues + loop { + buffer.clear(); + match reader.read_until(b'\n', &mut buffer) { + Ok(0) => break, // EOF + Ok(_) => { + // Convert bytes to string, handling invalid UTF-8 + let line = String::from_utf8_lossy(&buffer); + let line = line.trim_end_matches('\n').trim_end_matches('\r'); + + if line.is_empty() { + continue; + } + + println!("Python stdout: {}", line); + + // Parse JSON-RPC messages + if line.starts_with("JSONRPC:") { + let json_str = &line[8..]; // Remove "JSONRPC:" prefix + if let Ok(json_value) = serde_json::from_str::(json_str) { + if let Some(method) = json_value.get("method") { + if method == "progress" { + if let Ok(mut messages) = progress_messages_clone.lock() { + messages.push(json_str.to_string()); + } + println!("Progress: {}", json_str); + } + } else if json_value.get("result").is_some() || json_value.get("error").is_some() { + // This is a final result or error response + if let Ok(mut result) = final_result_clone.lock() { + *result = Some(json_str.to_string()); + } + println!("JSON-RPC result found: {}", json_str); + } + } + } else if line.trim().starts_with('{') && line.trim().ends_with('}') { + // Fallback: try to parse as direct JSON result + if let Ok(json_value) = serde_json::from_str::(line.trim()) { + // Check if this looks like a final result (has status field) + if json_value.get("status").is_some() { + if let Ok(mut result) = final_result_clone.lock() { + *result = Some(line.trim().to_string()); + } + println!("Direct JSON result: {}", line.trim()); + } + } + } else { + println!("Python other: {}", line); + } + + if stdout_tx.send(line.to_string()).is_err() { + break; + } + } + Err(e) => { + println!("Error reading stdout: {}", e); + break; + } + } + } + }); + + // Spawn thread for reading stderr + let error_messages_clone = Arc::clone(&error_messages); + thread::spawn(move || { + let mut reader = BufReader::new(stderr); + let mut buffer = Vec::new(); + + // Read raw bytes and handle encoding issues + loop { + buffer.clear(); + match reader.read_until(b'\n', &mut buffer) { + Ok(0) => break, // EOF + Ok(_) => { + // Convert bytes to string, handling invalid UTF-8 + let line = String::from_utf8_lossy(&buffer); + let line = line.trim_end_matches('\n').trim_end_matches('\r'); + + if line.is_empty() { + continue; + } + + println!("Python stderr: {}", line); + if let Ok(mut messages) = error_messages_clone.lock() { + messages.push(line.to_string()); + } + if stderr_tx.send(line.to_string()).is_err() { + break; + } + } + Err(e) => { + println!("Error reading stderr: {}", e); + break; + } + } + } + }); + + // Spawn thread to wait for process completion + thread::spawn(move || { + match child.wait() { + Ok(status) => { + let _ = result_tx.send(Ok(status)); + } + Err(e) => { + let _ = result_tx.send(Err(format!("Failed to wait for process: {}", e))); + } + } + }); + + // Wait for process to complete with timeout + println!("Waiting for Python process to complete..."); + let timeout_duration = Duration::from_secs(600); // 10 minutes timeout + let start_time = Instant::now(); + + let exit_status = loop { + match result_rx.try_recv() { + Ok(Ok(status)) => break status, + Ok(Err(e)) => return Err(e), + Err(mpsc::TryRecvError::Empty) => { + if start_time.elapsed() > timeout_duration { + return Err("Python process timed out after 10 minutes".to_string()); + } + thread::sleep(Duration::from_millis(100)); + continue; + } + Err(mpsc::TryRecvError::Disconnected) => { + return Err("Process monitoring thread disconnected".to_string()); + } + } + }; + + println!("Python process terminated with code: {:?}", exit_status.code()); + + // Extract final results from shared state + let final_result_value = { + final_result.lock().unwrap().clone() + }; + let progress_messages_value = { + progress_messages.lock().unwrap().clone() + }; + let error_messages_value = { + error_messages.lock().unwrap().clone() + }; + + // Return the final result if we found one + if let Some(result) = final_result_value { + println!("Extracted JSON-RPC result: {}", result); + return Ok(result); + } + + // If no JSON-RPC result found, provide detailed error information + if exit_status.success() { + // Process succeeded but no output - this is unusual + let error_msg = if progress_messages_value.is_empty() { + "Python script completed successfully but produced no output" + } else { + "Python script completed but did not return a valid JSON result" + }; + Err(error_msg.to_string()) + } else { + // Process failed - provide detailed error based on exit code + let error_msg = if let Some(code) = exit_status.code() { + match code { + 1 => "Python script failed with general error. Check if all dependencies are installed.".to_string(), + 120 => "Python module import failed or function not supported. This may be due to missing dependencies or incompatible Python environment.".to_string(), + _ => format!("Python script failed with exit code: {}. This may indicate a system or environment issue.", code) + } + } else { + "Python process was killed by external signal.".to_string() + }; + + // Include any error output we captured + let mut full_error = error_msg; + + if !progress_messages_value.is_empty() { + full_error.push_str(&format!("\n\nLast stdout: {}", progress_messages_value.join("\n"))); + } + + if !error_messages_value.is_empty() { + full_error.push_str(&format!("\n\nStderr: {}", error_messages_value.join("\n"))); + } + + Err(full_error) + } +} + +#[tauri::command] +pub async fn batch_import_templates(app: tauri::AppHandle, request: BatchImportRequest) -> Result { + let args = vec![ + "-m".to_string(), + "python_core.services.template_manager".to_string(), + "--action".to_string(), + "batch_import".to_string(), + "--source_folder".to_string(), + request.source_folder, + ]; + + execute_python_template_command(app, &args).await +} + +#[tauri::command] +pub async fn get_templates(app: tauri::AppHandle) -> Result { + let args = vec![ + "-m".to_string(), + "python_core.services.template_manager".to_string(), + "--action".to_string(), + "get_templates".to_string(), + ]; + + execute_python_template_command(app, &args).await +} + +#[tauri::command] +pub async fn get_template(app: tauri::AppHandle, template_id: String) -> Result { + let args = vec![ + "-m".to_string(), + "python_core.services.template_manager".to_string(), + "--action".to_string(), + "get_template".to_string(), + "--template_id".to_string(), + template_id, + ]; + + execute_python_template_command(app, &args).await +} + +#[tauri::command] +pub async fn delete_template(app: tauri::AppHandle, template_id: String) -> Result { + let args = vec![ + "-m".to_string(), + "python_core.services.template_manager".to_string(), + "--action".to_string(), + "delete_template".to_string(), + "--template_id".to_string(), + template_id, + ]; + + execute_python_template_command(app, &args).await +} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 27822a7..412bf44 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -34,7 +34,11 @@ pub fn run() { commands::test_ai_video_environment, commands::select_image_file, commands::select_folder, - commands::open_folder + commands::open_folder, + commands::batch_import_templates, + commands::get_templates, + commands::get_template, + commands::delete_template ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); diff --git a/src/App.tsx b/src/App.tsx index f75aa21..b47bb73 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -5,6 +5,7 @@ import HomePage from './pages/HomePage' import EditorPage from './pages/EditorPage' import AIVideoPage from './pages/AIVideoPage' import SettingsPage from './pages/SettingsPage' +import TemplateManagePage from './pages/TemplateManagePage' function App() { return ( @@ -13,6 +14,7 @@ function App() { } /> } /> } /> + } /> } /> diff --git a/src/components/AIVideoResultPreview.tsx b/src/components/AIVideoResultPreview.tsx index 50a9cb5..3521e1e 100644 --- a/src/components/AIVideoResultPreview.tsx +++ b/src/components/AIVideoResultPreview.tsx @@ -145,16 +145,6 @@ const AIVideoResultPreview: React.FC = ({ 打开文件夹 )} - - {videoResult.video_url && ( - - )} ) diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index 1b5a070..9bf62c8 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -1,6 +1,6 @@ import React, { useState } from 'react' import { Link, useLocation } from 'react-router-dom' -import { Home, Video, Settings, FolderOpen, Music, Image, Sparkles, List, Play, Clock, CheckCircle, XCircle } from 'lucide-react' +import { Home, Video, Settings, FolderOpen, Music, Image, Sparkles, List, Play, Clock, CheckCircle, XCircle, Layout } from 'lucide-react' import { clsx } from 'clsx' import { useAIVideoJobs } from '../stores/useAIVideoStore' @@ -13,6 +13,7 @@ const Sidebar: React.FC = () => { { path: '/', icon: Home, label: '首页' }, { path: '/editor', icon: Video, label: '编辑器' }, { path: '/ai-video', icon: Sparkles, label: 'AI 视频' }, + { path: '/templates', icon: Layout, label: '模板管理' }, { path: '/projects', icon: FolderOpen, label: '项目' }, { path: '/media', icon: Image, label: '媒体库' }, { path: '/audio', icon: Music, label: '音频' }, diff --git a/src/components/ai-video/VideoSettings.tsx b/src/components/ai-video/VideoSettings.tsx index 4db7352..fd451b0 100644 --- a/src/components/ai-video/VideoSettings.tsx +++ b/src/components/ai-video/VideoSettings.tsx @@ -27,7 +27,6 @@ const VideoSettings: React.FC = ({ onChange={(e) => onDurationChange(e.target.value)} className="input w-full" > - @@ -45,7 +44,6 @@ const VideoSettings: React.FC = ({ className="input w-full" > - diff --git a/src/pages/TemplateManagePage.tsx b/src/pages/TemplateManagePage.tsx new file mode 100644 index 0000000..1b01125 --- /dev/null +++ b/src/pages/TemplateManagePage.tsx @@ -0,0 +1,433 @@ +import React, { useState, useEffect } from 'react' +import { Upload, FolderOpen, Trash2, Eye, Download, Search, Filter, Grid, List } from 'lucide-react' +import { TemplateService } from '../services/tauri' + +interface TemplateInfo { + id: string + name: string + description: string + thumbnail_path: string + draft_content_path: string + resources_path: string + created_at: string + updated_at: string + canvas_config: any + duration: number + material_count: number + track_count: number + tags: string[] +} + +interface ImportResult { + status: boolean + msg: string + imported_count: number + failed_count: number + imported_templates: TemplateInfo[] + failed_templates: Array<{ + name: string + error: string + }> +} + +const TemplateManagePage: React.FC = () => { + const [templates, setTemplates] = useState([]) + const [loading, setLoading] = useState(false) + const [importing, setImporting] = useState(false) + const [searchTerm, setSearchTerm] = useState('') + const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid') + const [selectedTemplate, setSelectedTemplate] = useState(null) + const [importResult, setImportResult] = useState(null) + + // Load templates on component mount + useEffect(() => { + loadTemplates() + }, []) + + const loadTemplates = async () => { + try { + setLoading(true) + const result = await TemplateService.getTemplates() + if (result.status) { + setTemplates(result.templates || []) + } else { + console.error('Failed to load templates:', result.msg) + } + } catch (error) { + console.error('Error loading templates:', error) + } finally { + setLoading(false) + } + } + + const handleBatchImport = async () => { + try { + // Select folder using Tauri dialog + const folderResult = await window.__TAURI_INVOKE__('select_folder') + if (!folderResult) return + + setImporting(true) + setImportResult(null) + + const result = await TemplateService.batchImportTemplates(folderResult) + setImportResult(result) + + if (result.status && result.imported_count > 0) { + // Reload templates + await loadTemplates() + } + } catch (error) { + console.error('Import failed:', error) + setImportResult({ + status: false, + msg: error instanceof Error ? error.message : 'Unknown error', + imported_count: 0, + failed_count: 0, + imported_templates: [], + failed_templates: [] + }) + } finally { + setImporting(false) + } + } + + const handleDeleteTemplate = async (templateId: string) => { + if (!confirm('确定要删除这个模板吗?此操作不可撤销。')) return + + try { + const result = await TemplateService.deleteTemplate(templateId) + if (result.status) { + setTemplates(templates.filter(t => t.id !== templateId)) + if (selectedTemplate?.id === templateId) { + setSelectedTemplate(null) + } + } else { + alert('删除失败: ' + result.msg) + } + } catch (error) { + console.error('Delete failed:', error) + alert('删除失败: ' + (error instanceof Error ? error.message : 'Unknown error')) + } + } + + const filteredTemplates = templates.filter(template => + template.name.toLowerCase().includes(searchTerm.toLowerCase()) || + template.description.toLowerCase().includes(searchTerm.toLowerCase()) + ) + + const formatDuration = (duration: number) => { + const seconds = Math.floor(duration / 1000000) + const minutes = Math.floor(seconds / 60) + const remainingSeconds = seconds % 60 + return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}` + } + + const formatDate = (dateString: string) => { + return new Date(dateString).toLocaleDateString('zh-CN') + } + + return ( +
+
+ {/* Header */} +
+

模板管理

+

管理和导入视频编辑模板

+
+ + {/* Actions Bar */} +
+
+
+ + +
+ + setSearchTerm(e.target.value)} + className="pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent" + /> +
+
+ +
+ + +
+
+
+ + {/* Import Result */} + {importResult && ( +
+
+
+

+ 导入结果 +

+

+ {importResult.msg} +

+ {importResult.imported_count > 0 && ( +

+ 成功导入 {importResult.imported_count} 个模板 +

+ )} + {importResult.failed_count > 0 && ( +
+ + {importResult.failed_count} 个模板导入失败 (点击查看详情) + +
+ {importResult.failed_templates.map((failed, index) => ( +
+ {failed.name}: {failed.error} +
+ ))} +
+
+ )} +
+ +
+
+ )} + + {/* Templates Grid/List */} + {loading ? ( +
+
+ 加载中... +
+ ) : filteredTemplates.length === 0 ? ( +
+ +

暂无模板

+

+ {searchTerm ? '没有找到匹配的模板' : '点击上方按钮开始导入模板'} +

+ {!searchTerm && ( + + )} +
+ ) : ( +
+ {filteredTemplates.map((template) => ( +
+ {viewMode === 'grid' ? ( + <> + {/* Thumbnail */} +
+ {template.thumbnail_path ? ( + {template.name} + ) : ( +
+ +
+ )} +
+ + {/* Content */} +
+

+ {template.name} +

+

+ {template.description} +

+ +
+
时长: {formatDuration(template.duration)}
+
素材: {template.material_count} 个
+
轨道: {template.track_count} 个
+
创建: {formatDate(template.created_at)}
+
+ + {/* Actions */} +
+ + +
+
+ + ) : ( + <> + {/* List View */} +
+
+
+

{template.name}

+

{template.description}

+
+
+ {formatDuration(template.duration)} + {template.material_count} 素材 + {template.track_count} 轨道 + {formatDate(template.created_at)} +
+
+ + +
+
+
+ + )} +
+ ))} +
+ )} + + {/* Template Detail Modal */} + {selectedTemplate && ( +
+
+
+
+

模板详情

+ +
+ +
+
+ +

{selectedTemplate.name}

+
+ +
+ +

{selectedTemplate.description}

+
+ +
+
+ +

{formatDuration(selectedTemplate.duration)}

+
+
+ +

{selectedTemplate.material_count}

+
+
+ +

{selectedTemplate.track_count}

+
+
+ +

+ {selectedTemplate.canvas_config?.width || 0} × {selectedTemplate.canvas_config?.height || 0} +

+
+
+ +
+ +

{new Date(selectedTemplate.created_at).toLocaleString('zh-CN')}

+
+ +
+ +

{new Date(selectedTemplate.updated_at).toLocaleString('zh-CN')}

+
+ +
+ + +
+
+
+
+
+ )} +
+
+ ) +} + +export default TemplateManagePage diff --git a/src/services/tauri.ts b/src/services/tauri.ts index 12b1131..3e76bab 100644 --- a/src/services/tauri.ts +++ b/src/services/tauri.ts @@ -466,3 +466,59 @@ export class AIVideoService { } } } + +// Template management operations +export class TemplateService { + /** + * Batch import templates from a folder + */ + static async batchImportTemplates(sourceFolder: string): Promise { + try { + const request = { source_folder: sourceFolder } + const result = await invoke('batch_import_templates', { request }) + return JSON.parse(result as string) + } catch (error) { + console.error('Failed to batch import templates:', error) + throw error + } + } + + /** + * Get all templates + */ + static async getTemplates(): Promise { + try { + const result = await invoke('get_templates') + return JSON.parse(result as string) + } catch (error) { + console.error('Failed to get templates:', error) + throw error + } + } + + /** + * Get a specific template + */ + static async getTemplate(templateId: string): Promise { + try { + const result = await invoke('get_template', { templateId }) + return JSON.parse(result as string) + } catch (error) { + console.error('Failed to get template:', error) + throw error + } + } + + /** + * Delete a template + */ + static async deleteTemplate(templateId: string): Promise { + try { + const result = await invoke('delete_template', { templateId }) + return JSON.parse(result as string) + } catch (error) { + console.error('Failed to delete template:', error) + throw error + } + } +} diff --git a/src/services/templateService.ts b/src/services/templateService.ts new file mode 100644 index 0000000..54bd3a9 --- /dev/null +++ b/src/services/templateService.ts @@ -0,0 +1,277 @@ +import { invoke } from '@tauri-apps/api/tauri' + +export interface TemplateInfo { + id: string + name: string + description: string + thumbnail_path: string + draft_content_path: string + resources_path: string + created_at: string + updated_at: string + canvas_config: any + duration: number + material_count: number + track_count: number + tags: string[] +} + +export interface BatchImportRequest { + source_folder: string +} + +export interface BatchImportResult { + status: boolean + msg: string + imported_count: number + failed_count: number + imported_templates: TemplateInfo[] + failed_templates: Array<{ + name: string + error: string + }> +} + +export interface TemplateResult { + status: boolean + msg?: string + template?: TemplateInfo + templates?: TemplateInfo[] +} + +export class TemplateService { + /** + * Batch import templates from a folder + */ + static async batchImportTemplates(sourceFolder: string): Promise { + try { + const request: BatchImportRequest = { + source_folder: sourceFolder + } + + const response = await invoke('batch_import_templates', { request }) + const result = JSON.parse(response) + + if (!result.status) { + throw new Error(result.error || result.msg || 'Import failed') + } + + return result + } catch (error) { + console.error('Batch import templates failed:', error) + throw error + } + } + + /** + * Get all templates + */ + static async getTemplates(): Promise { + try { + const response = await invoke('get_templates') + const result = JSON.parse(response) + + if (!result.status) { + throw new Error(result.error || result.msg || 'Failed to get templates') + } + + return result + } catch (error) { + console.error('Get templates failed:', error) + throw error + } + } + + /** + * Get a specific template + */ + static async getTemplate(templateId: string): Promise { + try { + const response = await invoke('get_template', { templateId }) + const result = JSON.parse(response) + + if (!result.status) { + throw new Error(result.error || result.msg || 'Failed to get template') + } + + return result + } catch (error) { + console.error('Get template failed:', error) + throw error + } + } + + /** + * Delete a template + */ + static async deleteTemplate(templateId: string): Promise { + try { + const response = await invoke('delete_template', { templateId }) + const result = JSON.parse(response) + + if (!result.status) { + throw new Error(result.error || result.msg || 'Failed to delete template') + } + + return result + } catch (error) { + console.error('Delete template failed:', error) + throw error + } + } + + /** + * Validate template folder structure + */ + static validateTemplateFolder(folderPath: string): boolean { + // This would be implemented to check if the folder contains valid template structure + // For now, we'll rely on the backend validation + return true + } + + /** + * Generate template thumbnail + */ + static async generateThumbnail(templateId: string): Promise { + // This would be implemented to generate a thumbnail from the template + // For now, return empty string + return '' + } + + /** + * Export template + */ + static async exportTemplate(templateId: string, targetPath: string): Promise { + try { + // This would be implemented to export a template to a specific location + // For now, just return true + return true + } catch (error) { + console.error('Export template failed:', error) + return false + } + } + + /** + * Duplicate template + */ + static async duplicateTemplate(templateId: string, newName: string): Promise { + try { + // This would be implemented to create a copy of an existing template + // For now, just return a placeholder result + return { + status: false, + msg: 'Duplicate functionality not implemented yet' + } + } catch (error) { + console.error('Duplicate template failed:', error) + throw error + } + } + + /** + * Update template metadata + */ + static async updateTemplate(templateId: string, updates: Partial): Promise { + try { + // This would be implemented to update template metadata + // For now, just return a placeholder result + return { + status: false, + msg: 'Update functionality not implemented yet' + } + } catch (error) { + console.error('Update template failed:', error) + throw error + } + } + + /** + * Search templates + */ + static async searchTemplates(query: string, filters?: { + tags?: string[] + duration_min?: number + duration_max?: number + material_count_min?: number + material_count_max?: number + }): Promise { + try { + // This would be implemented to search templates with filters + // For now, just get all templates and filter on frontend + const result = await this.getTemplates() + + if (result.status && result.templates) { + const filteredTemplates = result.templates.filter(template => + template.name.toLowerCase().includes(query.toLowerCase()) || + template.description.toLowerCase().includes(query.toLowerCase()) + ) + + return { + status: true, + templates: filteredTemplates + } + } + + return result + } catch (error) { + console.error('Search templates failed:', error) + throw error + } + } + + /** + * Get template statistics + */ + static async getTemplateStats(): Promise<{ + total_templates: number + total_materials: number + total_size: number + most_used_tags: string[] + }> { + try { + const result = await this.getTemplates() + + if (result.status && result.templates) { + const templates = result.templates + const totalTemplates = templates.length + const totalMaterials = templates.reduce((sum, t) => sum + t.material_count, 0) + + // Extract all tags and count frequency + const tagCounts: { [key: string]: number } = {} + templates.forEach(template => { + template.tags.forEach(tag => { + tagCounts[tag] = (tagCounts[tag] || 0) + 1 + }) + }) + + const mostUsedTags = Object.entries(tagCounts) + .sort(([, a], [, b]) => b - a) + .slice(0, 10) + .map(([tag]) => tag) + + return { + total_templates: totalTemplates, + total_materials: totalMaterials, + total_size: 0, // Would need to calculate from file sizes + most_used_tags: mostUsedTags + } + } + + return { + total_templates: 0, + total_materials: 0, + total_size: 0, + most_used_tags: [] + } + } catch (error) { + console.error('Get template stats failed:', error) + return { + total_templates: 0, + total_materials: 0, + total_size: 0, + most_used_tags: [] + } + } + } +} diff --git a/template.md b/template.md new file mode 100644 index 0000000..3b693a9 --- /dev/null +++ b/template.md @@ -0,0 +1,7 @@ +# 开发一个模板管理页面 需要有批量导入功能 +1. 选择文件夹 批量上传模板 +2. 解析文件夹下的每个子文件夹 每个子文件夹都是一个模板 +3. 判断文件夹内是否有 draft_content.json 文件 +4. 解析 draft_content.json 内容 分析轨道素材信息 +5. 每个轨道有多个片段 每个片段 {id,material_id,source_timerange,target_timerange} +6. 根据material_id找到素材位置 然后 将素材单独存放在 软件 attachments/templates/模板名/resources目录下 并将对应素材的path替换成相对路径 \ No newline at end of file diff --git a/test_template_manager.py b/test_template_manager.py new file mode 100644 index 0000000..794a046 --- /dev/null +++ b/test_template_manager.py @@ -0,0 +1,243 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Test script for template manager functionality +""" + +import os +import json +import shutil +import tempfile +from pathlib import Path + +def create_test_template(template_dir: Path, template_name: str): + """Create a test template with draft_content.json""" + + # Create template directory + template_path = template_dir / template_name + template_path.mkdir(parents=True, exist_ok=True) + + # Create sample draft_content.json + draft_content = { + "version": "1.0", + "canvas_config": { + "width": 1920, + "height": 1080, + "fps": 30 + }, + "duration": 5000000, # 5 seconds in microseconds + "tracks": [ + { + "id": "track_1", + "type": "video", + "segments": [ + { + "id": "segment_1", + "material_id": "video_1", + "source_timerange": {"start": 0, "end": 3000000}, + "target_timerange": {"start": 0, "end": 3000000} + } + ] + }, + { + "id": "track_2", + "type": "audio", + "segments": [ + { + "id": "segment_2", + "material_id": "audio_1", + "source_timerange": {"start": 0, "end": 5000000}, + "target_timerange": {"start": 0, "end": 5000000} + } + ] + } + ], + "materials": { + "videos": [ + { + "id": "video_1", + "name": "sample_video.mp4", + "path": str(template_path / "sample_video.mp4"), + "duration": 3000000, + "width": 1920, + "height": 1080 + } + ], + "audios": [ + { + "id": "audio_1", + "name": "sample_audio.mp3", + "path": str(template_path / "sample_audio.mp3"), + "duration": 5000000 + } + ], + "images": [ + { + "id": "image_1", + "name": "sample_image.jpg", + "path": str(template_path / "sample_image.jpg"), + "width": 1920, + "height": 1080 + } + ] + } + } + + # Save draft_content.json + with open(template_path / "draft_content.json", 'w', encoding='utf-8') as f: + json.dump(draft_content, f, ensure_ascii=False, indent=2) + + # Create dummy media files + (template_path / "sample_video.mp4").touch() + (template_path / "sample_audio.mp3").touch() + (template_path / "sample_image.jpg").touch() + + print(f"Created test template: {template_name}") + return template_path + +def test_template_manager(): + """Test the template manager functionality""" + + # Create temporary directory for test templates + with tempfile.TemporaryDirectory() as temp_dir: + temp_path = Path(temp_dir) + + print(f"Creating test templates in: {temp_path}") + + # Create multiple test templates + templates = [ + "Wedding_Template", + "Birthday_Template", + "Corporate_Template", + "Travel_Template" + ] + + for template_name in templates: + create_test_template(temp_path, template_name) + + # Test the template manager + print("\nTesting template manager...") + + try: + from python_core.services.template_manager import TemplateManager + + manager = TemplateManager() + + # Test batch import + print("Testing batch import...") + result = manager.batch_import_templates(str(temp_path)) + + print(f"Import result: {result}") + + if result['status']: + print(f"Successfully imported {result['imported_count']} templates") + + # Test get templates + print("\nTesting get templates...") + templates = manager.get_templates() + print(f"Found {len(templates)} templates") + + for template in templates: + print(f" - {template.name} (ID: {template.id})") + print(f" Duration: {template.duration/1000000:.1f}s") + print(f" Materials: {template.material_count}") + print(f" Tracks: {template.track_count}") + + # Test get specific template + if templates: + template_id = templates[0].id + print(f"\nTesting get specific template: {template_id}") + template = manager.get_template(template_id) + if template: + print(f"Retrieved template: {template.name}") + else: + print("Failed to retrieve template") + + # Test delete template + if templates: + template_id = templates[0].id + print(f"\nTesting delete template: {template_id}") + success = manager.delete_template(template_id) + if success: + print("Template deleted successfully") + + # Verify deletion + remaining_templates = manager.get_templates() + print(f"Remaining templates: {len(remaining_templates)}") + else: + print("Failed to delete template") + else: + print(f"Import failed: {result['msg']}") + + except Exception as e: + print(f"Error testing template manager: {e}") + import traceback + traceback.print_exc() + +def test_command_line(): + """Test the command line interface""" + + print("\nTesting command line interface...") + + # Create temporary directory for test templates + with tempfile.TemporaryDirectory() as temp_dir: + temp_path = Path(temp_dir) + + # Create a test template + create_test_template(temp_path, "CLI_Test_Template") + + # Test CLI commands + import subprocess + import sys + + try: + # Test batch import + print("Testing CLI batch import...") + result = subprocess.run([ + sys.executable, "-m", "python_core.services.template_manager", + "--action", "batch_import", + "--source_folder", str(temp_path) + ], capture_output=True, text=True, cwd=".") + + print(f"CLI Exit code: {result.returncode}") + print(f"CLI Stdout: {result.stdout}") + if result.stderr: + print(f"CLI Stderr: {result.stderr}") + + if result.returncode == 0: + # Parse result + import json + cli_result = json.loads(result.stdout) + print(f"CLI Import result: {cli_result}") + + # Test get templates + print("\nTesting CLI get templates...") + result = subprocess.run([ + sys.executable, "-m", "python_core.services.template_manager", + "--action", "get_templates" + ], capture_output=True, text=True, cwd=".") + + if result.returncode == 0: + templates_result = json.loads(result.stdout) + print(f"CLI Templates: {len(templates_result.get('templates', []))}") + else: + print(f"CLI get templates failed: {result.stderr}") + else: + print("CLI batch import failed") + + except Exception as e: + print(f"Error testing CLI: {e}") + import traceback + traceback.print_exc() + +if __name__ == "__main__": + print("Template Manager Test Script") + print("=" * 50) + + # Test the template manager class + test_template_manager() + + # Test the command line interface + test_command_line() + + print("\nTest completed!")