11use super :: {
22 command:: { refresh_preview, sync_manager_to_ui, with_history_manager} ,
33 project:: PROJECT_STATE ,
4- segment:: { async_load_segment_audio , async_load_segment_thumbnail } ,
4+ segment:: refresh_affected_segments ,
55} ;
66use crate :: {
77 global_store,
@@ -19,8 +19,10 @@ use std::{path::PathBuf, sync::Arc, time::Duration};
1919use uuid:: Uuid ;
2020use video_editor:: {
2121 commands:: {
22- BatchCommand , ExecuteResult ,
23- segment:: { InsertSegmentAtTimeCommand , SplitSegmentCommand } ,
22+ AffectedSegment , BatchCommand , ExecuteResult ,
23+ segment:: {
24+ InsertSegmentAtTimeCommand , ShiftSubsequentSegmentsCommand , SplitSegmentCommand ,
25+ } ,
2426 track:: AddTrackCommand ,
2527 } ,
2628 metadata:: { Metadata , MetadataType , get_metadata} ,
@@ -153,168 +155,132 @@ pub fn async_add_item_to_track(ui_weak: Weak<AppWindow>, name: String, file_path
153155 let timeline_offset_ms = global_store ! ( ui) . get_video_editor_timeline_offset ( ) ;
154156 let timeline_offset = Duration :: from_millis ( timeline_offset_ms as u64 ) ;
155157
156- let result: Result < ( ExecuteResult , usize , usize , String ) > =
157- with_history_manager ( |state| {
158- // Check if current track matches metadata type
159- if current_track_index >= 0
160- && let Some ( current_track) =
161- state. tracks_manager . get ( current_track_index as usize )
162- && track_matches_metadata_type ( current_track, & metadata)
163- {
164- // Add segment to existing track at playhead position
165- let track_index = current_track_index as usize ;
166- let track = state. tracks_manager . get ( track_index) . expect ( "track exists" ) ;
167-
168- let segment_duration = if metadata. is_image ( ) && metadata. duration . is_zero ( )
169- {
170- Duration :: from_secs ( 5 )
171- } else {
172- metadata. duration
173- } ;
174-
175- // Determine insertion point and whether we need to split
176- let mut need_split = false ;
177- let mut split_segment_index = 0 ;
178- let mut insert_index = track. segments_count ( ) ;
179- let mut split_time = Duration :: ZERO ;
180-
181- for ( i, segment) in track. segments ( ) . iter ( ) . enumerate ( ) {
182- let segment_start = segment. timeline_offset ;
183- let segment_end = segment. timeline_offset + segment. duration ;
184-
185- if timeline_offset >= segment_start && timeline_offset < segment_end {
186- // Playhead is within this segment, need to split
187- insert_index = i + 1 ;
188- need_split = true ;
189- split_segment_index = i;
190- split_time = timeline_offset - segment_start;
191- break ;
192- } else if timeline_offset < segment_start {
193- // Playhead is before this segment
194- insert_index = i;
195- break ;
196- }
197- }
158+ let result: Result < ExecuteResult > = with_history_manager ( |state| {
159+ // Check if current track matches metadata type
160+ if current_track_index >= 0
161+ && let Some ( current_track) =
162+ state. tracks_manager . get ( current_track_index as usize )
163+ && track_matches_metadata_type ( current_track, & metadata)
164+ {
165+ // Add segment to existing track at playhead position
166+ let track_index = current_track_index as usize ;
167+ let track = state. tracks_manager . get ( track_index) . expect ( "track exists" ) ;
168+
169+ let segment_duration = if metadata. is_image ( ) && metadata. duration . is_zero ( ) {
170+ Duration :: from_secs ( 5 )
171+ } else {
172+ metadata. duration
173+ } ;
198174
199- // Create the new segment at playhead position
200- let mut segment =
201- Segment :: new ( timeline_offset, segment_duration, metadata. clone ( ) ) ;
202- segment. uuid = Uuid :: new_v4 ( ) . to_string ( ) ;
203- let segment = Arc :: new ( segment) ;
175+ // Determine insertion point and whether we need to split
176+ let mut need_split = false ;
177+ let mut split_segment_index = 0 ;
178+ let mut insert_index = track. segments_count ( ) ;
179+ let mut split_time = Duration :: ZERO ;
180+
181+ for ( i, segment) in track. segments ( ) . iter ( ) . enumerate ( ) {
182+ let segment_start = segment. timeline_offset ;
183+ let segment_end = segment. timeline_offset + segment. duration ;
184+
185+ if timeline_offset >= segment_start && timeline_offset < segment_end {
186+ // Playhead is within this segment, need to split
187+ insert_index = i + 1 ;
188+ need_split = true ;
189+ split_segment_index = i;
190+ split_time = timeline_offset - segment_start;
191+ break ;
192+ } else if timeline_offset < segment_start {
193+ // Playhead is before this segment
194+ insert_index = i;
195+ break ;
196+ }
197+ }
204198
205- let mut batch_command = BatchCommand :: new ( format ! ( "Add {} to track" , name) ) ;
199+ // Create the new segment at playhead position
200+ let mut segment =
201+ Segment :: new ( timeline_offset, segment_duration, metadata. clone ( ) ) ;
202+ segment. uuid = Uuid :: new_v4 ( ) . to_string ( ) ;
203+ let segment = Arc :: new ( segment) ;
206204
207- if need_split {
208- batch_command. add_command ( Box :: new ( SplitSegmentCommand :: new (
209- track_index,
210- split_segment_index,
211- split_time,
212- ) ) ) ;
213- }
205+ let mut batch_command = BatchCommand :: new ( format ! ( "Add {} to track" , name) ) ;
214206
215- batch_command. add_command ( Box :: new ( InsertSegmentAtTimeCommand :: new (
207+ if need_split {
208+ batch_command. add_command ( Box :: new ( SplitSegmentCommand :: new (
216209 track_index,
217- insert_index,
218- segment,
219- need_split, // shift timeline if we split
210+ split_segment_index,
211+ split_time,
220212 ) ) ) ;
221213
222- let execute_result = state
223- . history_manager
224- . execute ( & mut state. tracks_manager , Box :: new ( batch_command) ) ?;
225-
226- // Determine segment index after insertion
227- // If we split, the right split part will be at insert_index + 1 after our insert
228- // So our inserted segment is at insert_index
229- let segment_index = insert_index;
230- let segment_uuid = state
231- . tracks_manager
232- . get ( track_index)
233- . and_then ( |t| t. segments ( ) . get ( segment_index) . map ( |s| s. uuid . clone ( ) ) )
234- . unwrap_or_default ( ) ;
235-
236- return Ok ( ( execute_result, track_index, segment_index, segment_uuid) ) ;
237- }
214+ batch_command. add_extra_affected_segment (
215+ AffectedSegment :: with_both_thumbnails (
216+ track_index,
217+ split_segment_index, // 被分割的左边
218+ ) ,
219+ ) ;
238220
239- let tracks = Track :: new ( & metadata. path )
240- . map_err ( |e| video_editor:: Error :: InvalidConfig ( e. to_string ( ) ) ) ?;
221+ batch_command. add_extra_affected_segment (
222+ AffectedSegment :: with_both_thumbnails (
223+ track_index,
224+ split_segment_index + 2 , // 被分割的右边
225+ ) ,
226+ ) ;
227+ }
241228
242- let mut batch_command = BatchCommand :: new ( format ! ( "Add {} to track" , name) ) ;
229+ batch_command. add_command ( Box :: new ( InsertSegmentAtTimeCommand :: new (
230+ track_index,
231+ insert_index,
232+ segment. clone ( ) ,
233+ need_split, // shift inside this command only when splitting
234+ ) ) ) ;
243235
244- for track in tracks {
245- let command = AddTrackCommand :: new ( track) ;
246- batch_command. add_command ( Box :: new ( command) ) ;
236+ // When inserting at a gap, shift all subsequent segments
237+ if !need_split && insert_index < track. segments_count ( ) {
238+ batch_command. add_command ( Box :: new ( ShiftSubsequentSegmentsCommand :: new (
239+ track_index,
240+ insert_index + 1 , // Shift segments after the newly inserted one
241+ segment_duration,
242+ ) ) ) ;
247243 }
248244
249245 let execute_result = state
250246 . history_manager
251247 . execute ( & mut state. tracks_manager , Box :: new ( batch_command) ) ?;
252248
253- // Find the inserted track index by looking for the main track type
254- // based on metadata. Track::new creates tracks with matching types.
255- let ( track_index, segment_index, segment_uuid) = {
256- let track_index = state
257- . tracks_manager
258- . iter ( )
259- . enumerate ( )
260- . find ( |( _, t) | track_matches_metadata_type ( t, & metadata) )
261- . map ( |( i, _) | i)
262- . unwrap_or_else ( || state. tracks_manager . len ( ) . saturating_sub ( 1 ) ) ;
263-
264- let track = state. tracks_manager . get ( track_index) . unwrap ( ) ;
265- let segment_index = track. segments_count ( ) - 1 ;
266- let segment_uuid = track
267- . segments ( )
268- . last ( )
269- . map ( |s| s. uuid . clone ( ) )
270- . unwrap_or_default ( ) ;
271-
272- ( track_index, segment_index, segment_uuid)
273- } ;
249+ return Ok ( execute_result) ;
250+ }
274251
275- Ok ( ( execute_result , track_index , segment_index , segment_uuid ) )
276- } ) ;
252+ let tracks = Track :: new ( & metadata . path )
253+ . map_err ( |e| video_editor :: Error :: InvalidConfig ( e . to_string ( ) ) ) ? ;
277254
278- match result {
279- Ok ( ( execute_result, inserted_track_index, segment_index, segment_uuid) ) => {
280- let tracks_manager = with_history_manager ( |state| state. tracks_manager . clone ( ) ) ;
255+ let mut batch_command = BatchCommand :: new ( format ! ( "Add {} to track" , name) ) ;
256+
257+ for track in tracks {
258+ let command = AddTrackCommand :: new ( track) ;
259+ batch_command. add_command ( Box :: new ( command) ) ;
260+ }
261+
262+ let execute_result = state
263+ . history_manager
264+ . execute ( & mut state. tracks_manager , Box :: new ( batch_command) ) ?;
281265
266+ Ok ( execute_result)
267+ } ) ;
268+
269+ match result {
270+ Ok ( execute_result) => {
282271 sync_manager_to_ui ( & ui) ;
283272
284- if execute_result. affected_segments . tracks_changed
273+ let needs_preview_refresh = execute_result. affected_segments . tracks_changed
285274 && ( matches ! ( metadata. get_type( ) , MetadataType :: Video )
286- || metadata. is_image ( ) )
287- {
288- refresh_preview ( & ui) ;
289- }
275+ || metadata. is_image ( ) ) ;
290276
291- crate :: toast_success! ( ui, format! ( "Added {} to track" , name ) ) ;
277+ refresh_affected_segments ( & ui, execute_result . affected_segments ) ;
292278
293- // Load thumbnails and audio in background
294- if matches ! ( metadata. get_type( ) , MetadataType :: Video ) || metadata. is_image ( ) {
295- for index in 0 ..2 {
296- async_load_segment_thumbnail (
297- ui. as_weak ( ) ,
298- tracks_manager. clone ( ) ,
299- inserted_track_index,
300- segment_index,
301- segment_uuid. clone ( ) ,
302- index % 2 == 0 ,
303- ) ;
304- }
279+ if needs_preview_refresh {
280+ refresh_preview ( & ui) ;
305281 }
306282
307- if matches ! ( metadata. get_type( ) , MetadataType :: Video )
308- || matches ! ( metadata. get_type( ) , MetadataType :: Audio )
309- {
310- async_load_segment_audio (
311- ui. as_weak ( ) ,
312- tracks_manager. clone ( ) ,
313- inserted_track_index,
314- segment_index,
315- segment_uuid,
316- ) ;
317- }
283+ crate :: toast_success!( ui, format!( "Added {} to track" , name) ) ;
318284 }
319285 Err ( e) => crate :: toast_warn!( ui, e. to_string( ) ) ,
320286 }
0 commit comments