11package extract
22
33import (
4- "flag "
4+ "errors "
55 "fmt"
6- "github.com/AlecAivazis/survey/v2"
7- "github.com/AlecAivazis/survey/v2/terminal"
8- "github.com/briandowns/spinner"
6+ "github.com/charmbracelet/huh"
7+ "github.com/charmbracelet/huh/spinner"
98 "github.com/coding-socks/matroska"
109 "github.com/coding-socks/matroska/cmd/mkc/internal/cli"
10+ flag "github.com/spf13/pflag"
1111 "log"
1212 "os"
1313 "path/filepath"
1414 "strings"
15- "time"
1615)
1716
1817var Cmd = & cli.Command {
@@ -23,89 +22,126 @@ func init() {
2322 Cmd .Run = run
2423}
2524
26- var flagOutput = Cmd .Flags .String ("output" , "" , "Path to the output folder." )
25+ var flagOutput = Cmd .Flags .StringP ("output" , "o" , "" , "Path to the output folder." )
26+ var flagTracks = Cmd .Flags .UintSliceP ("tracks" , "t" , []uint {}, "Id of track to extract" )
2727
2828type arguments struct {
2929 Input string
3030 Output string
31+ Tracks []uint
3132}
3233
3334func run (flags * flag.FlagSet ) {
3435 args := arguments {
3536 Input : flags .Arg (0 ),
3637 Output : * flagOutput ,
38+ Tracks : * flagTracks ,
3739 }
38- var questions []* survey.Question
3940 if args .Input == "" {
40- questions = append (questions , & survey.Question {
41- Name : "input" ,
42- Prompt : & survey.Input {Message : "Source matroska file:" },
43- Validate : survey .ComposeValidators (survey .Required , cli .ValidatorFile ),
44- })
41+ err := huh .NewInput ().
42+ Title ("Source matroska file:" ).
43+ Prompt ("?" ).
44+ Validate (cli .ValidatorFile ).
45+ Value (& args .Input ).
46+ Run ()
47+ if errors .Is (err , huh .ErrUserAborted ) {
48+ return
49+ } else if err != nil {
50+ log .Fatal (err )
51+ }
4552 }
4653 if args .Output == "" {
47- questions = append (questions , & survey.Question {
48- Name : "output" ,
49- Prompt : & survey.Input {Message : "Output folder:" },
50- Validate : survey .ComposeValidators (survey .Required , cli .ValidatorDir ),
51- })
52- }
53- if err := survey .Ask (questions , & args ); err == terminal .InterruptErr {
54- return
55- } else if err != nil {
56- log .Fatal (err )
54+ err := huh .NewInput ().
55+ Title ("Output folder:" ).
56+ Prompt ("?" ).
57+ Validate (cli .ValidatorDir ).
58+ Value (& args .Output ).
59+ Run ()
60+ if errors .Is (err , huh .ErrUserAborted ) {
61+ return
62+ } else if err != nil {
63+ log .Fatal (err )
64+ }
5765 }
5866
5967 f , err := os .Open (args .Input )
6068 if err != nil {
6169 log .Fatalf ("Could not open input file: %s" , args .Input )
6270 }
6371 defer f .Close ()
64- spin := spinner .New (cli .SpinnerCharSet , 200 * time .Millisecond )
65- spin .Prefix = "Loading file into memory "
66- spin .Start ()
67- s , err := matroska .NewScanner (f )
68- spin .Stop ()
69- if err != nil {
70- log .Fatal (err )
71- }
72- tracks := s .Tracks ()
73- options := make ([]string , len (tracks .TrackEntry ))
74- for i , e := range tracks .TrackEntry {
75- options [i ] = fmt .Sprintf ("Track %02d [%s]" , e .TrackNumber , e .CodecID )
76- }
77- var trackIndex int
78- err = survey .AskOne (& survey.Select {
79- Message : "Select track" ,
80- Options : options ,
81- }, & trackIndex )
82- if err == terminal .InterruptErr {
72+
73+ var (
74+ s * matroska.Scanner
75+ actionErr error
76+ )
77+ err = spinner .New ().
78+ Title ("Loading file into memory" ).
79+ Action (func () {
80+ s , actionErr = matroska .NewScanner (f )
81+ }).
82+ Run ()
83+ if errors .Is (err , huh .ErrUserAborted ) {
8384 return
8485 } else if err != nil {
8586 log .Fatal (err )
8687 }
87- te := tracks .TrackEntry [trackIndex ]
8888
89- spin .Start ()
90- fname := filepath .Base (args .Input )
91- fname = strings .TrimSuffix (fname , filepath .Ext (fname ))
92- fname = fmt .Sprintf ("%s_Track_%02d" , fname , te .TrackNumber )
93- suffix := ""
94- ext := GuessExt (te .CodecID )
95- for i := 1 ; ; i ++ {
96- _ , err := os .Stat (filepath .Join (args .Output , fname + suffix + ext ))
97- if os .IsNotExist (err ) {
98- break
99- }
100- suffix = fmt .Sprintf ("_%d" , i )
89+ if err = actionErr ; err != nil {
90+ log .Fatal (err )
10191 }
102- f , err = os .Create (filepath .Join (args .Output , fname + suffix + ext ))
103- if err != nil {
104- log .Fatalf ("Could not create ouput file: %s" , err )
92+ tracks := s .Tracks ()
93+ if len (args .Tracks ) == 0 {
94+ options := make ([]huh.Option [uint ], len (tracks .TrackEntry ))
95+ for i , e := range tracks .TrackEntry {
96+ options [i ] = huh .NewOption (fmt .Sprintf ("Track %02d [%s]" , e .TrackNumber , e .CodecID ), e .TrackNumber )
97+ }
98+ err = huh .NewMultiSelect [uint ]().
99+ Title ("Track:" ).
100+ Options (options ... ).
101+ Value (& args .Tracks ).
102+ Run ()
103+ if errors .Is (err , huh .ErrUserAborted ) {
104+ return
105+ } else if err != nil {
106+ log .Fatal (err )
107+ }
105108 }
106- if err := matroska .ExtractTract (f , s , te ); err != nil {
107- os .Remove (filepath .Join (args .Output , fname ))
108- log .Fatalf ("Could not extract track: %s" , err )
109+
110+ for _ , trackIndex := range args .Tracks {
111+ te := tracks .TrackEntry [trackIndex - 1 ]
112+
113+ err = spinner .New ().
114+ Action (func () {
115+ fname := filepath .Base (args .Input )
116+ fname = strings .TrimSuffix (fname , filepath .Ext (fname ))
117+ fname = fmt .Sprintf ("%s_Track_%02d" , fname , te .TrackNumber )
118+ suffix := ""
119+ ext := GuessExt (te .CodecID )
120+ for i := 1 ; ; i ++ {
121+ _ , err := os .Stat (filepath .Join (args .Output , fname + suffix + ext ))
122+ if os .IsNotExist (err ) {
123+ break
124+ }
125+ suffix = fmt .Sprintf ("_%d" , i )
126+ }
127+ f , err := os .Create (filepath .Join (args .Output , fname + suffix + ext ))
128+ if err != nil {
129+ actionErr = fmt .Errorf ("could not create ouput file: %s" , err )
130+ return
131+ }
132+ if err := matroska .ExtractTract (f , s , te ); err != nil {
133+ os .Remove (filepath .Join (args .Output , fname ))
134+ log .Fatalf ("Could not extract track: %s" , err )
135+ }
136+ }).
137+ Run ()
138+ if errors .Is (err , huh .ErrUserAborted ) {
139+ return
140+ } else if err != nil {
141+ log .Fatal (err )
142+ } else if err = actionErr ; err != nil {
143+ log .Fatal (err )
144+ }
109145 }
110146}
111147
@@ -116,8 +152,12 @@ func GuessExt(codecID string) string {
116152 return ".aac"
117153 case matroska .AudioCodecAC3 :
118154 return ".ac3"
155+ case matroska .AudioCodecMP2 :
156+ return ".mp2"
119157 case matroska .AudioCodecMP3 :
120158 return ".mp3"
159+ case matroska .AudioCodecVORBIS :
160+ return ".ogg"
121161 // Video
122162 case matroska .VideoCodecMSCOMP :
123163 return ".avi"
0 commit comments