001    /////////////////////////////////////////////////
002    // This file is part of Sears project.
003    // Subtitle Editor And Re-Synch
004    // A tool to easily modify and resynch movies subtitles.
005    /////////////////////////////////////////////////
006    //This program is free software; 
007    //you can redistribute it and/or modify it under the terms 
008    //of the GNU General Public License 
009    //as published by the Free Software Foundation; 
010    //either version 2 of the License, or (at your option) any later version.
011    /////////////////////////////////////////////////
012    //Sears project is available under sourceforge
013    // at adress: http://sourceforge.net/projects/sears/
014    //Copyright (C) 2005 Booba Skaya
015    //Mail: booba.skaya@gmail.com
016    //
017    // $Id: Main.java,v 1.6 2007/02/20 19:45:35 booba_skaya Exp $
018    ////////////////////////////////////////////////
019    
020    package sears.main;
021    import java.io.File;
022    import java.util.ArrayList;
023    import sears.file.SrtFile;
024    import sears.file.Subtitle;
025    import sears.file.SubtitleFile;
026    import sears.gui.MainWindow;
027    import sears.tools.Trace;
028    
029    /**
030     * Class Main.
031     * <br><b>Summary:</b><br>
032     * This is the main class.
033     * It contains the main method that launches the Sears GUI or the inline commands.
034     */
035    public class Main {
036        /**The delay action*/
037        private static String KEY_DELAY = "delay";
038        /**The resynch action*/
039        private static String KEY_RESYNCHRO = "resyn";
040        /**The split action*/
041        private static String KEY_SPLIT = "split";
042        /**The append action*/
043        private static String KEY_APPEND = "appen";
044    
045        /** The NEWLINE String, for this OS. */
046        private static final String NEWL = System.getProperty("line.separator");
047        
048        /** The usage, that will displayed if user fails to enter correct parameters.*/
049        private static String USAGE = "Sears"+NEWL
050        +"DESCRIPTION:"+NEWL
051        +"SEARS is a Subtitle Editor And Re-Synchroniser."+NEWL
052        +"USAGE:"+NEWL
053        +"java -jar sears.jar [action  ActionParameters]"+NEWL
054        +"ACTIONS:"+NEWL
055        +"-delay:"+NEWL
056        +"  java -jar sears.jar "+KEY_DELAY+" d input_file [output_file]"+NEWL
057        +"  will delay the input's file subtitle using the given integer delay d."+NEWL
058        +"  result will be write in the input_file, or in output_file, if output_file is given."+NEWL
059        +"-resyn:"+NEWL
060        +"  java -jar sears.jar "+KEY_RESYNCHRO+" src1 dest1 src2 dest2 input_file [output_file]"+NEWL
061        +"  will apply a resynchro to given input file."+NEWL
062        +"  src and dest must be precised under format HH:MM:SS.MMM"+NEWL
063        +"  result will be write in the input_file, or in output_file, if output_file is given."+NEWL
064        +"-split:"+NEWL
065        +"  java -jar sears.jar "+KEY_SPLIT+" index input_file output_file1 output_file2"+NEWL
066        +"  will apply a split to given input file."+NEWL
067        +"  It will cut the input file at subtitle which index is given."+NEWL
068        +"  result will be write in the 2 output files output_file1 and output_file2."+NEWL
069        +"-append:"+NEWL
070        +"  java -jar sears.jar "+KEY_APPEND+" d input_file1 input_file2 [output_file]"+NEWL
071        +"  will apply a append to given input file."+NEWL
072        +"  It will append the subtitle of input_file2 with a delay of d."+NEWL
073        +"  result will be write in the input_file, or in output_file, if output_file is given."+NEWL
074        +""+NEWL
075        +""+NEWL;
076        
077        /**The inputSubtitleFile.*/
078        private static SubtitleFile inputSubtitleFile;
079        /**The outputFile.*/
080        private static File outputFile;
081        
082        
083        /**
084         * Method main.
085         * <br><b>Summary:</b><br>
086         * This is the method that launches the Sears main GUI.
087         * @param args  Arguments. Sears can take arguments to make inline operations.
088         *        arguments syntax is:
089         *        java.Main.main [actionKey] [action's parameters]
090         *         
091         */
092        public static void main(String[] args) {
093            if(args.length == 0){
094                    launchMainWindow(null);
095            }else{
096                String actionKey = args[0];
097                //Check for an action key word.
098                if(actionKey.equals(KEY_DELAY)){
099                    delayAction(args);
100                    System.out.println("Delay performed.");
101                }else if(actionKey.equals(KEY_RESYNCHRO)){
102                    resynchroAction(args);
103                    System.out.println("Resynchro performed.");
104                }else if(actionKey.equals(KEY_SPLIT)){
105                    splitAction(args);
106                    System.out.println("Split performed.");
107                }else if(actionKey.equals(KEY_APPEND)){
108                    appendAction(args);
109                    System.out.println("Append performed.");
110                }else if(args[0].endsWith(".srt")){ 
111                    launchMainWindow(args[0]);
112                }else{
113                    //If not action key word has benn recognized, displayUsage.
114                    displayUsage();
115                }
116            }
117        }
118    
119    
120            /**
121             * Method launchMainWindow.
122             * <br><b>Summary:</b><br>
123             * This method permits to launch a Sears mainWindows, with an opened file.
124             * @param file          The file to open, or null if no file is to be opened.
125             */
126            private static void launchMainWindow(String file) {
127                    //if Sears are launched on Mac OS:
128                    if(System.getProperty("mrj.version") != null){
129                            // set the menu bar on the top of the screen,
130                            // if default style "apple.laf.AquaLookAndFeel" has changed by user,
131                            // the menu bar returns on the top of the main JFrame.
132                            System.setProperty( "apple.laf.useScreenMenuBar", "true" );
133                            // others properties:
134                            System.setProperty( "apple.awt.showGrowBox", "true" );
135                            System.setProperty("apple.awt.textantialiasing" , "true");
136                    }   
137                    Trace.trace("Constructing main window", Trace.ALGO_PRIORITY);
138                    MainWindow mainWindow = new MainWindow();
139                    mainWindow.setVisible(true);
140                    //If a file has been given open it.
141                    if(file != null){
142                            mainWindow.openFile(new File(file));
143                    }
144            }
145    
146    
147        /**
148         * Method <b>displayUsage</b>
149         * <br><b>Summary:</b><br>
150         * This method display the Searc command line usage.
151         */
152        private static void displayUsage() {
153            System.out.println(USAGE);
154        }
155    
156    
157        /**
158         * Method <b>appendAction</b>
159         * <br><b>Summary:</b><br>
160         * This method is called to perform an append action.
161         *  Must have at least 4 parameters.
162         *  appen d input_file1 input_file2 [output_file]
163         *  will apply a append to given input file.
164         *  It will append the subtitle of input_file2 with a delay of d.
165         *  result will be write in the input_file, or in output_file, if output_file is given.
166         * Parameters:
167         * @param args  The command line arguments.
168         * 
169         */
170        private static void appendAction(String[] args) {
171            Trace.trace("Append action.", Trace.MESSAGE_PRIORITY);
172            //Split must have at least 4 parameters
173            String errorMessage = "";
174            if(args.length >= 4){
175                double delay =0;
176                SubtitleFile fileToAppend = null;
177                try{
178                    //get the delay.
179                    delay = Double.parseDouble(args[1]);
180                    //open the file to append, it will be save temporally in inputSubtitleFile.
181                    errorMessage += openInputFile(args[3]);
182                    //save it.
183                    fileToAppend = inputSubtitleFile;
184                    //open the src file.
185                    errorMessage += openInputFile(args[2]);
186                    //If outputfile is precised, read it.
187                    if(args.length >= 5){
188                        errorMessage += openOutputFile(args[4]);
189                    }
190                }catch (NumberFormatException e){
191                    errorMessage += "Delay must be a number."+NEWL;
192                }
193                //If there is no error, proceed to resynchro.
194                if(errorMessage != null && errorMessage.equals("")){
195                    Trace.trace("Proceed to Append: with delay "+ delay +" on file : "+inputSubtitleFile.getFile(), Trace.MESSAGE_PRIORITY);
196                    //delay * 1000 because delay is in seconds, with milliseconds.
197                    inputSubtitleFile.append(fileToAppend, (int) (delay*1000));
198                    if(outputFile != null){
199                        Trace.trace("Write result to: "+outputFile, Trace.MESSAGE_PRIORITY);
200                        inputSubtitleFile.writeToFile(outputFile);
201                    }else{
202                        Trace.trace("Write result to: "+inputSubtitleFile.getFile(), Trace.MESSAGE_PRIORITY);
203                        inputSubtitleFile.writeToFile(inputSubtitleFile.getFile());
204                    }
205                }
206            }else{
207                errorMessage += "Append action command must have at least 4 arguments."+NEWL+"appen d input_file1 input_file2 [output_file]";
208            }
209            if(errorMessage != null && !errorMessage.equals("")){
210                displayErrorMessage(errorMessage);
211            }
212    
213        }
214    
215    
216        /**
217         * Method <b>splitAction</b>
218         * <br><b>Summary:</b><br>
219         *  java -jar sears.jar split index input_file output_file1 output_file2
220         *   will apply a split to given input file.
221         *   It must have at least 5 parameters.
222         * Parameters:
223         * @param args  The command line arguments.
224         * 
225         */
226        private static void splitAction(String[] args) {
227            Trace.trace("Split action.", Trace.MESSAGE_PRIORITY);
228            //Split must have at least 5 parameters
229            String errorMessage = "";
230            if(args.length >= 5){
231                int index =0;
232                File[] destinationFiles = new File[2];
233                try{
234                    //get the index.
235                    index = Integer.parseInt(args[1]);
236                    //open the input file.
237                    errorMessage += openInputFile(args[2]);
238                    //open first output file
239                    errorMessage += openOutputFile(args[3]);
240                    //save it.
241                    destinationFiles[0] = outputFile;
242                    //open second outputFile
243                    errorMessage += openOutputFile(args[4]);
244                    destinationFiles[1] = outputFile;
245                }catch (NumberFormatException e){
246                    errorMessage += "Index must be a number."+NEWL;
247                }
248                //If there is no error, proceed to resynchro.
249                if(errorMessage != null && errorMessage.equals("")){
250                    Trace.trace("Proceed to Split: At index "+ index +" on file : "+inputSubtitleFile.getFile(), Trace.MESSAGE_PRIORITY);
251                    SubtitleFile[] resultFiles = inputSubtitleFile.split(destinationFiles, (index-1), 1);
252                    //save result files.
253                    resultFiles[0].writeToFile(resultFiles[0].getFile());
254                    resultFiles[1].writeToFile(resultFiles[1].getFile());
255                }
256            }else{
257                errorMessage += "Split action command must have at least 5 arguments."+NEWL+"split index input_file output_file1 output_file2";
258            }
259            if(errorMessage != null && !errorMessage.equals("")){
260                displayErrorMessage(errorMessage);
261            }
262        }
263    
264    
265        /**
266         * Method <b>resynchroAction</b>
267         * <br><b>Summary:</b><br>
268         * This method is called to perform a resynchro action.
269         * Must have at least 6 parameters:
270         *  resynch src1 dest1 src2 dest2 input_file [output_file]
271         *  src and dest must be precised under format HH:MM:SS.
272         *  result will be write in the input_file, or in output_file, if output_file is given.
273         * Parameters:
274         * @param args  The command line arguments.
275         * 
276         */
277        private static void resynchroAction(String[] args) {
278            Trace.trace("Resynchro action.", Trace.MESSAGE_PRIORITY);
279            //Resynch must have at least 6 parameters.
280            String errorMessage = "";
281            if(args.length >= 6){
282                int[] resynchParameters = new int[4];
283                try{
284                    //get source and destinations.
285                    resynchParameters[0]  = SubtitleFile.stringToTime(args[1]);
286                    resynchParameters[1]  = SubtitleFile.stringToTime(args[2]);
287                    resynchParameters[2]  = SubtitleFile.stringToTime(args[3]);
288                    resynchParameters[3]  = SubtitleFile.stringToTime(args[4]);
289                    //open the input file.
290                    errorMessage += openInputFile(args[5]);
291                    //If outputfile is precised, read it.
292                    if(args.length >= 7){
293                        errorMessage += openOutputFile(args[6]);
294                    }
295                }catch (NumberFormatException e){
296                    errorMessage += "Source and destination times must be under HH:MM:SS.MMM format."+NEWL;
297                }
298                //If there is no error, proceed to resynchro.
299                if(errorMessage != null && errorMessage.equals("")){
300                    Trace.trace("Proceed to Resynch: on file : "+inputSubtitleFile.getFile(), Trace.MESSAGE_PRIORITY);
301                    inputSubtitleFile.resynchro(resynchParameters);
302                    if(outputFile != null){
303                        Trace.trace("Write result to: "+outputFile, Trace.MESSAGE_PRIORITY);
304                        inputSubtitleFile.writeToFile(outputFile);
305                    }else{
306                        Trace.trace("Write result to: "+inputSubtitleFile.getFile(), Trace.MESSAGE_PRIORITY);
307                        inputSubtitleFile.writeToFile(inputSubtitleFile.getFile());
308                    }
309                }
310            
311            
312            }else{
313                errorMessage += "Resynchro action command must have at least 6 arguments."+NEWL+"resyn src1 dest1 src2 dest2 input_file [output_file]";
314            }
315            if(errorMessage != null && !errorMessage.equals("")){
316                displayErrorMessage(errorMessage);
317            }
318        }
319    
320    
321        /**
322         * Method <b>delayAction</b>
323         * <br><b>Summary:</b><br>
324         * This method is called to perform a delay action.
325         * Delay action command must have at least 3 arguments.
326         * delay d input_file [output_file]
327         * d is the delay to apply, an integer.
328         * input_file is the file to perform the delay.
329         * [output_file] optionnal, if precised, action will be saved here.
330         * Parameters:
331         * @param args  The command line arguments.
332         * 
333         */
334        private static void delayAction(String[] args) {
335            Trace.trace("Delay action.", Trace.MESSAGE_PRIORITY);
336            //Delay action command must have at least 3 arguments.
337            //if not display message error.
338            String errorMessage = "";
339            //must have at least 3 parameter for a delay.
340            if(args.length >= 3){
341                try{
342                    //Get the delay to apply.
343                    double delay = Double.parseDouble(args[1]);
344                    //open the input file.
345                    errorMessage += openInputFile(args[2]);
346                    //If outputfile is precised, read it.
347                    if(args.length >= 4){
348                        errorMessage += openOutputFile(args[3]);
349                    }
350                    if(errorMessage != null && errorMessage.equals("")){
351                        Trace.trace("Proceed to Delay: delay "+delay+ " on file : "+inputSubtitleFile.getFile(), Trace.MESSAGE_PRIORITY);
352                        //delay * 1000 because delay is in seconds, with milliseconds.
353                        inputSubtitleFile.delay((int)(delay*1000));
354                        if(outputFile != null){
355                            Trace.trace("Write result to: "+outputFile, Trace.MESSAGE_PRIORITY);
356                            inputSubtitleFile.writeToFile(outputFile);
357                        }else{
358                            Trace.trace("Write result to: "+inputSubtitleFile.getFile(), Trace.MESSAGE_PRIORITY);
359                            inputSubtitleFile.writeToFile(inputSubtitleFile.getFile());
360                        }
361                    }
362                }catch(NumberFormatException e){
363                    errorMessage += "Must enter an integer delay";
364                }
365                
366            }else{
367                errorMessage += "Delay action command must have at least 3 arguments."+NEWL+"delay d input_file [output_file]";
368            }
369            if(errorMessage != null && !errorMessage.equals("")){
370                displayErrorMessage(errorMessage);
371            }
372        }
373    
374    
375        /**
376         * Method <b>openOutputFile</b>
377         * <br><b>Summary:</b><br>
378         * This method open the given file.
379         * Store it in output file.
380         * Parameters:
381         * @param   file                The file to open.    
382         * @return  <b>String</b>       The error message, or "" if no error has been found.
383          */
384        private static String openOutputFile(String file) {
385            //The result of the Method
386            String message ="";
387            //Construct file
388            File output = new File(file);
389            //check that file could be opened.
390            if(output.exists() && !output.canWrite()){
391                message += "File "+file+" can not be written."+NEWL;
392            }else{
393                outputFile = output;
394            }
395            //return the error message.
396            return message;
397        }
398    
399    
400        /**
401         * Method <b>openInputFile</b>
402         * <br><b>Summary:</b><br>
403         * This method open the given file.
404         * Store it in input file.
405         * Parameters:
406         * @param   file                The file to open.    
407         * @return  <b>String</b>       The error message, or "" if no error has been found.
408         * 
409         */
410        private static String openInputFile(String file) {
411            //The result of the Method
412            String message ="";
413            //Construct file
414            File inputFile = new File(file);
415            //check that file could be opened.
416            if(!inputFile.canRead()){
417                message += "File "+file+" can not be read."+NEWL;
418            }
419            //Construct subtitleFile
420            inputSubtitleFile = new SrtFile(inputFile, new ArrayList<Subtitle>());
421            //return the error message.
422            return message;
423        }
424    
425    
426        /**
427         * Method <b>displayErrorMessage</b>
428         * <br><b>Summary:</b><br>
429         * This method display an error.
430         * Parameters:
431         * @param error    The error to display.
432         * 
433         */
434        private static void displayErrorMessage(String error) {
435            System.out.println("Error : "+NEWL+error+NEWL);
436        }
437    }
438