@@ -787,3 +787,221 @@ impl Command for UpdateTrackMetadataCommand {
787787 format ! ( "Update track {} metadata" , self . track_index)
788788 }
789789}
790+
791+ pub struct DetachAudioTracksCommand {
792+ track_index : usize ,
793+ old_metadata : Option < Arc < Metadata > > ,
794+ detached_track_indices : Option < Vec < usize > > ,
795+ }
796+
797+ impl DetachAudioTracksCommand {
798+ pub fn new ( track_index : usize ) -> Self {
799+ Self {
800+ track_index,
801+ old_metadata : None ,
802+ detached_track_indices : None ,
803+ }
804+ }
805+ }
806+
807+ impl Command for DetachAudioTracksCommand {
808+ fn execute ( & mut self , manager : & mut Manager ) -> Result < ( ) > {
809+ execute_detach (
810+ manager,
811+ self . track_index ,
812+ DetachType :: Audio ,
813+ & mut self . old_metadata ,
814+ & mut self . detached_track_indices ,
815+ )
816+ }
817+
818+ fn undo ( & mut self , manager : & mut Manager ) -> Result < ( ) > {
819+ undo_detach (
820+ manager,
821+ self . track_index ,
822+ & mut self . old_metadata ,
823+ & mut self . detached_track_indices ,
824+ )
825+ }
826+
827+ fn describe ( & self ) -> String {
828+ format ! ( "Detach audio tracks from track {}" , self . track_index)
829+ }
830+ }
831+
832+ pub struct DetachSubtitleTracksCommand {
833+ track_index : usize ,
834+ old_metadata : Option < Arc < Metadata > > ,
835+ detached_track_indices : Option < Vec < usize > > ,
836+ }
837+
838+ impl DetachSubtitleTracksCommand {
839+ pub fn new ( track_index : usize ) -> Self {
840+ Self {
841+ track_index,
842+ old_metadata : None ,
843+ detached_track_indices : None ,
844+ }
845+ }
846+ }
847+
848+ impl Command for DetachSubtitleTracksCommand {
849+ fn execute ( & mut self , manager : & mut Manager ) -> Result < ( ) > {
850+ execute_detach (
851+ manager,
852+ self . track_index ,
853+ DetachType :: Subtitle ,
854+ & mut self . old_metadata ,
855+ & mut self . detached_track_indices ,
856+ )
857+ }
858+
859+ fn undo ( & mut self , manager : & mut Manager ) -> Result < ( ) > {
860+ undo_detach (
861+ manager,
862+ self . track_index ,
863+ & mut self . old_metadata ,
864+ & mut self . detached_track_indices ,
865+ )
866+ }
867+
868+ fn describe ( & self ) -> String {
869+ format ! ( "Detach subtitle tracks from track {}" , self . track_index)
870+ }
871+ }
872+
873+ #[ derive( Clone , Copy ) ]
874+ enum DetachType {
875+ Audio ,
876+ Subtitle ,
877+ }
878+
879+ fn execute_detach (
880+ manager : & mut Manager ,
881+ track_index : usize ,
882+ detach_type : DetachType ,
883+ old_metadata : & mut Option < Arc < Metadata > > ,
884+ detached_track_indices : & mut Option < Vec < usize > > ,
885+ ) -> Result < ( ) > {
886+ let manager_len = manager. len ( ) ;
887+ if track_index >= manager_len {
888+ return Err ( Error :: IndexOutOfBounds ( track_index, manager_len) ) ;
889+ }
890+
891+ let track = manager
892+ . get ( track_index)
893+ . ok_or_else ( || Error :: IndexOutOfBounds ( track_index, manager_len) ) ?;
894+
895+ let video_track = match track {
896+ Track :: Video ( vt) => vt,
897+ _ => {
898+ return Err ( Error :: InvalidConfig ( format ! (
899+ "Only video tracks can have detached {}" ,
900+ match detach_type {
901+ DetachType :: Audio => "audio" ,
902+ DetachType :: Subtitle => "subtitles" ,
903+ }
904+ ) ) ) ;
905+ }
906+ } ;
907+
908+ let has_streams = match detach_type {
909+ DetachType :: Audio => !video_track. track . metadata . audios . is_empty ( ) ,
910+ DetachType :: Subtitle => !video_track. track . metadata . subtitles . is_empty ( ) ,
911+ } ;
912+
913+ if !has_streams {
914+ return Err ( Error :: InvalidConfig ( format ! (
915+ "Video track has no {} streams to detach" ,
916+ match detach_type {
917+ DetachType :: Audio => "audio" ,
918+ DetachType :: Subtitle => "subtitle" ,
919+ }
920+ ) ) ) ;
921+ }
922+
923+ let mut video_track_mut = video_track. as_ref ( ) . clone ( ) ;
924+ * old_metadata = Some ( video_track. track . metadata . clone ( ) ) ;
925+
926+ let detached_tracks: Vec < Track > = match detach_type {
927+ DetachType :: Audio => video_track_mut
928+ . detach_audio_tracks ( )
929+ . into_iter ( )
930+ . map ( |at| Track :: Audio ( Arc :: new ( at) ) )
931+ . collect ( ) ,
932+ DetachType :: Subtitle => video_track_mut
933+ . detach_subtitle_tracks ( )
934+ . into_iter ( )
935+ . map ( |st| Track :: Subtitle ( Arc :: new ( st) ) )
936+ . collect ( ) ,
937+ } ;
938+
939+ if detached_tracks. is_empty ( ) {
940+ return Err ( Error :: InvalidConfig ( format ! (
941+ "No {} tracks were created{}" ,
942+ match detach_type {
943+ DetachType :: Audio => "audio" ,
944+ DetachType :: Subtitle => "subtitle" ,
945+ } ,
946+ match detach_type {
947+ DetachType :: Audio => "" ,
948+ DetachType :: Subtitle => " (extraction may have failed)" ,
949+ }
950+ ) ) ) ;
951+ }
952+
953+ // Update video track metadata
954+ let new_metadata = video_track_mut. track . metadata . clone ( ) ;
955+ let track = manager
956+ . get_mut ( track_index)
957+ . ok_or_else ( || Error :: IndexOutOfBounds ( track_index, manager_len) ) ?;
958+
959+ if let Track :: Video ( vt) = track {
960+ let vt = Arc :: make_mut ( vt) ;
961+ vt. track . metadata = new_metadata;
962+ }
963+
964+ // Insert detached tracks
965+ let mut inserted_indices = Vec :: new ( ) ;
966+ for ( i, track) in detached_tracks. into_iter ( ) . enumerate ( ) {
967+ let insert_idx = track_index + 1 + i;
968+ manager. insert_track ( insert_idx, track) ?;
969+ inserted_indices. push ( insert_idx) ;
970+ }
971+
972+ * detached_track_indices = Some ( inserted_indices) ;
973+ Ok ( ( ) )
974+ }
975+
976+ fn undo_detach (
977+ manager : & mut Manager ,
978+ track_index : usize ,
979+ old_metadata : & mut Option < Arc < Metadata > > ,
980+ detached_track_indices : & mut Option < Vec < usize > > ,
981+ ) -> Result < ( ) > {
982+ let old_meta = old_metadata
983+ . take ( )
984+ . ok_or_else ( || Error :: InvalidConfig ( "Command not yet executed" . into ( ) ) ) ?;
985+
986+ let detached_indices = detached_track_indices
987+ . take ( )
988+ . ok_or_else ( || Error :: InvalidConfig ( "Command not yet executed" . into ( ) ) ) ?;
989+
990+ // Remove detached tracks in reverse order
991+ for idx in detached_indices. into_iter ( ) . rev ( ) {
992+ manager. remove_track ( idx) ?;
993+ }
994+
995+ // Restore original metadata
996+ let manager_len = manager. len ( ) ;
997+ let track = manager
998+ . get_mut ( track_index)
999+ . ok_or_else ( || Error :: IndexOutOfBounds ( track_index, manager_len) ) ?;
1000+
1001+ if let Track :: Video ( vt) = track {
1002+ let vt = Arc :: make_mut ( vt) ;
1003+ vt. track . metadata = old_meta;
1004+ }
1005+
1006+ Ok ( ( ) )
1007+ }
0 commit comments