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.14 2007/09/06 12:10:50 floriaen Exp $
018    
019    
020    package sears.main;
021    import java.awt.EventQueue;
022    import java.io.File;
023    import java.util.ArrayList;
024    
025    import sears.file.SrtFile;
026    import sears.file.Subtitle;
027    import sears.file.SubtitleFile;
028    import sears.file.exception.io.FileConversionException;
029    import sears.gui.MainWindow;
030    import sears.tools.Trace;
031    import sears.tools.Utils;
032    import sears.tools.eawt.AppException;
033    import sears.tools.eawt.SearsApp;
034    
035    /**
036     * Class Main.
037     * <br><b>Summary:</b><br>
038     * This is the main class.
039     * It contains the main method that launches the Sears GUI or the inline commands.
040     */
041    public class Main {
042            /**The delay action*/
043            private static String KEY_DELAY = "delay";
044            /**The resynch action*/
045            private static String KEY_RESYNCHRO = "resyn";
046            /**The split action*/
047            private static String KEY_SPLIT = "split";
048            /**The append action*/
049            private static String KEY_APPEND = "appen";
050    
051            /** The NEWLINE String, for this OS. */
052            private static final String NEWL = System.getProperty("line.separator");
053    
054            /** The usage, that will displayed if user fails to enter correct parameters.*/
055            private static String USAGE = "Sears"+NEWL
056            +"DESCRIPTION:"+NEWL
057            +"SEARS is a Subtitle Editor And Re-Synchroniser."+NEWL
058            +"USAGE:"+NEWL
059            +"java -jar sears.jar [action  ActionParameters]"+NEWL
060            +"ACTIONS:"+NEWL
061            +"-delay:"+NEWL
062            +"  java -jar sears.jar "+KEY_DELAY+" d input_file [output_file]"+NEWL
063            +"  will delay the input's file subtitle using the given integer delay d."+NEWL
064            +"  result will be write in the input_file, or in output_file, if output_file is given."+NEWL
065            +"-resyn:"+NEWL
066            +"  java -jar sears.jar "+KEY_RESYNCHRO+" src1 dest1 src2 dest2 input_file [output_file]"+NEWL
067            +"  will apply a resynchro to given input file."+NEWL
068            +"  src and dest must be precised under format HH:MM:SS.MMM"+NEWL
069            +"  result will be write in the input_file, or in output_file, if output_file is given."+NEWL
070            +"-split:"+NEWL
071            +"  java -jar sears.jar "+KEY_SPLIT+" index input_file output_file1 output_file2"+NEWL
072            +"  will apply a split to given input file."+NEWL
073            +"  It will cut the input file at subtitle which index is given."+NEWL
074            +"  result will be write in the 2 output files output_file1 and output_file2."+NEWL
075            +"-append:"+NEWL
076            +"  java -jar sears.jar "+KEY_APPEND+" d input_file1 input_file2 [output_file]"+NEWL
077            +"  will apply a append to given input file."+NEWL
078            +"  It will append the subtitle of input_file2 with a delay of d."+NEWL
079            +"  result will be write in the input_file, or in output_file, if output_file is given."+NEWL
080            +""+NEWL
081            +""+NEWL;
082    
083            /**The inputSubtitleFile.*/
084            private static SubtitleFile inputSubtitleFile;
085            /**The outputFile.*/
086            private static File outputFile;
087    
088            private static MainWindow mainWindow = null;
089    
090    
091            /**
092             * Method main.
093             * <br><b>Summary:</b><br>
094             * This is the method that launches the Sears main GUI.
095             * @param args  Arguments. Sears can take arguments to make inline operations.
096             *        arguments syntax is:
097             *        java.Main.main [actionKey] [action's parameters]
098             *         
099             */
100            public static void main(String[] args) {
101                    if(args.length == 0){
102                            launchMainWindow(null);
103                    }else{
104                            String actionKey = args[0];
105                            //Check for an action key word.
106                            if(actionKey.equals(KEY_DELAY)){
107                                    delayAction(args);
108                                    System.out.println("Delay performed.");
109                            }else if(actionKey.equals(KEY_RESYNCHRO)){
110                                    resynchroAction(args);
111                                    System.out.println("Resynchro performed.");
112                            }else if(actionKey.equals(KEY_SPLIT)){
113                                    splitAction(args);
114                                    System.out.println("Split performed.");
115                            }else if(actionKey.equals(KEY_APPEND)){
116                                    appendAction(args);
117                                    System.out.println("Append performed.");
118                            }else if(args[0].endsWith(".srt")){ 
119                                    launchMainWindow(args[0]);
120                            }else{
121                                    //If not action key word has benn recognized, displayUsage.
122                                    displayUsage();
123                            }
124                    }
125            }
126            
127            /**
128             * Quick reboot, closed the main window, free the resource and reopened it
129             * <br>Useful when a gui changing is called
130             * @param file  must be the opened file (could be null)
131             * @param reset if a properties reset is called
132             */
133            public static void quickReboot(File file, boolean reset) {
134                    // TODO 0.9 version
135            }
136    
137            /**
138             * Method launchMainWindow.
139             * <br><b>Summary:</b><br>
140             * This method permits to launch a Sears mainWindows, with an opened file.
141             * @param file          The file to open, or null if no file is to be opened.
142             */
143            private static void launchMainWindow(String file) {
144                    Trace.trace("Constructing main window", Trace.ALGO_PRIORITY);
145                    if( !launchMainWindowOnMac() ) {
146                            mainWindow = new MainWindow();
147                            Runnable mainWindowVisible = new Runnable() {
148                                    public void run() {     
149                                            mainWindow.setVisible(true);                                    
150                                    }                               
151                            };
152                            // THREAD-SAFE, put the setVisible method in the SWING "event dispatching thread"
153                            EventQueue.invokeLater(mainWindowVisible);
154                            //If a file has been given open it.
155                            if(file != null){
156                                    mainWindow.openFile(new File(file));
157                            }
158                    }
159            }
160    
161            /**
162             * If Sears is launched on a Mac Machine, this method try to run 
163             * the implementation of Sears for Mac OS X
164             * @return true if operation is succeeded, false if not
165             */
166            private static boolean launchMainWindowOnMac() {
167                    boolean state = false;
168                    // if Sears are launched on Mac OS:
169                    if( Utils.isMacPlatform ){
170                            // we create an instance of SearsApp class
171                            // which will make possible to have a better
172                            // integration of Sears on Mac OS X
173                            try {
174                                    new SearsApp();
175                                    state = true;
176                            } catch (AppException e) {
177                                    state = false;
178                            }
179                    }
180                    return state;
181            }
182    
183            /**
184             * Method <b>displayUsage</b>
185             * <br><b>Summary:</b><br>
186             * This method display the Searc command line usage.
187             */
188            private static void displayUsage() {
189                    System.out.println(USAGE);
190            }
191    
192            /**
193             * Method <b>appendAction</b>
194             * <br><b>Summary:</b><br>
195             * This method is called to perform an append action.
196             *  Must have at least 4 parameters.
197             *  appen d input_file1 input_file2 [output_file]
198             *  will apply a append to given input file.
199             *  It will append the subtitle of input_file2 with a delay of d.
200             *  result will be write in the input_file, or in output_file, if output_file is given.
201             * Parameters:
202             * @param args  The command line arguments.
203             * 
204             */
205            private static void appendAction(String[] args) {
206                    Trace.trace("Append action.", Trace.MESSAGE_PRIORITY);
207                    //Split must have at least 4 parameters
208                    String errorMessage = "";
209                    if(args.length >= 4){
210                            double delay =0;
211                            SubtitleFile fileToAppend = null;
212                            try{
213                                    //get the delay.
214                                    delay = Double.parseDouble(args[1]);
215                                    //open the file to append, it will be save temporally in inputSubtitleFile.
216                                    errorMessage += openInputFile(args[3]);
217                                    //save it.
218                                    fileToAppend = inputSubtitleFile;
219                                    //open the src file.
220                                    errorMessage += openInputFile(args[2]);
221                                    //If outputfile is precised, read it.
222                                    if(args.length >= 5){
223                                            errorMessage += openOutputFile(args[4]);
224                                    }
225                            }catch (NumberFormatException e){
226                                    errorMessage += "Delay must be a number."+NEWL;
227                            }
228                            //If there is no error, proceed to resynchro.
229                            if(errorMessage != null && errorMessage.equals("")){
230                                    Trace.trace("Proceed to Append: with delay "+ delay +" on file : "+inputSubtitleFile.getFile(), Trace.MESSAGE_PRIORITY);
231                                    //delay * 1000 because delay is in seconds, with milliseconds.
232                                    inputSubtitleFile.append(fileToAppend, (int) (delay*1000));
233                                    if(outputFile != null){
234                                            Trace.trace("Write result to: "+outputFile, Trace.MESSAGE_PRIORITY);
235                                            try {
236                                                    inputSubtitleFile.writeToFile(outputFile);
237                                            } catch (FileConversionException e) {
238                                                    // TODO Auto-generated catch block
239                                                    e.printStackTrace();
240                                            }
241                                    }else{
242                                            Trace.trace("Write result to: "+inputSubtitleFile.getFile(), Trace.MESSAGE_PRIORITY);
243                                            try {
244                                                    inputSubtitleFile.writeToFile(inputSubtitleFile.getFile());
245                                            } catch (FileConversionException e) {
246                                                    // TODO Auto-generated catch block
247                                                    e.printStackTrace();
248                                            }
249                                    }
250                            }
251                    }else{
252                            errorMessage += "Append action command must have at least 4 arguments."+NEWL+"appen d input_file1 input_file2 [output_file]";
253                    }
254                    if(errorMessage != null && !errorMessage.equals("")){
255                            displayErrorMessage(errorMessage);
256                    }
257            }
258    
259            /**
260             * Method <b>splitAction</b>
261             * <br><b>Summary:</b><br>
262             *  java -jar sears.jar split index input_file output_file1 output_file2
263             *   will apply a split to given input file.
264             *   It must have at least 5 parameters.
265             * Parameters:
266             * @param args  The command line arguments.
267             * 
268             */
269            private static void splitAction(String[] args) {
270                    Trace.trace("Split action.", Trace.MESSAGE_PRIORITY);
271                    //Split must have at least 5 parameters
272                    String errorMessage = "";
273                    if(args.length >= 5){
274                            int index =0;
275                            File[] destinationFiles = new File[2];
276                            try{
277                                    //get the index.
278                                    index = Integer.parseInt(args[1]);
279                                    //open the input file.
280                                    errorMessage += openInputFile(args[2]);
281                                    //open first output file
282                                    errorMessage += openOutputFile(args[3]);
283                                    //save it.
284                                    destinationFiles[0] = outputFile;
285                                    //open second outputFile
286                                    errorMessage += openOutputFile(args[4]);
287                                    destinationFiles[1] = outputFile;
288                            }catch (NumberFormatException e){
289                                    errorMessage += "Index must be a number."+NEWL;
290                            }
291                            //If there is no error, proceed to resynchro.
292                            if(errorMessage != null && errorMessage.equals("")){
293                                    Trace.trace("Proceed to Split: At index "+ index +" on file : "+inputSubtitleFile.getFile(), Trace.MESSAGE_PRIORITY);
294                                    SubtitleFile[] resultFiles = inputSubtitleFile.split(destinationFiles, (index-1), 1);
295                                    //save result files.
296                                    try {
297                                            resultFiles[0].writeToFile(resultFiles[0].getFile());
298                                    } catch (FileConversionException e) {
299                                            // TODO Auto-generated catch block
300                                            e.printStackTrace();
301                                    }
302                                    try {
303                                            resultFiles[1].writeToFile(resultFiles[1].getFile());
304                                    } catch (FileConversionException e) {
305                                            // TODO Auto-generated catch block
306                                            e.printStackTrace();
307                                    }
308                            }
309                    }else{
310                            errorMessage += "Split action command must have at least 5 arguments."+NEWL+"split index input_file output_file1 output_file2";
311                    }
312                    if(errorMessage != null && !errorMessage.equals("")){
313                            displayErrorMessage(errorMessage);
314                    }
315            }
316    
317            /**
318             * Method <b>resynchroAction</b>
319             * <br><b>Summary:</b><br>
320             * This method is called to perform a resynchro action.
321             * Must have at least 6 parameters:
322             *  resynch src1 dest1 src2 dest2 input_file [output_file]
323             *  src and dest must be precised under format HH:MM:SS.
324             *  result will be write in the input_file, or in output_file, if output_file is given.
325             * Parameters:
326             * @param args  The command line arguments.
327             * 
328             */
329            private static void resynchroAction(String[] args) {
330                    Trace.trace("Resynchro action.", Trace.MESSAGE_PRIORITY);
331                    //Resynch must have at least 6 parameters.
332                    String errorMessage = "";
333                    if(args.length >= 6){
334                            int[] resynchParameters = new int[4];
335                            try{
336                                    //get source and destinations.
337                                    resynchParameters[0]  = SubtitleFile.stringToTime(args[1]);
338                                    resynchParameters[1]  = SubtitleFile.stringToTime(args[2]);
339                                    resynchParameters[2]  = SubtitleFile.stringToTime(args[3]);
340                                    resynchParameters[3]  = SubtitleFile.stringToTime(args[4]);
341                                    //open the input file.
342                                    errorMessage += openInputFile(args[5]);
343                                    //If outputfile is precised, read it.
344                                    if(args.length >= 7){
345                                            errorMessage += openOutputFile(args[6]);
346                                    }
347                            }catch (NumberFormatException e){
348                                    errorMessage += "Source and destination times must be under HH:MM:SS.MMM format."+NEWL;
349                            }
350                            //If there is no error, proceed to resynchro.
351                            if(errorMessage != null && errorMessage.equals("")){
352                                    Trace.trace("Proceed to Resynch: on file : "+inputSubtitleFile.getFile(), Trace.MESSAGE_PRIORITY);
353                                    inputSubtitleFile.resynchro(resynchParameters);
354                                    if(outputFile != null){
355                                            Trace.trace("Write result to: "+outputFile, Trace.MESSAGE_PRIORITY);
356                                            try {
357                                                    inputSubtitleFile.writeToFile(outputFile);
358                                            } catch (FileConversionException e) {
359                                                    // TODO Auto-generated catch block
360                                                    e.printStackTrace();
361                                            }
362                                    }else{
363                                            Trace.trace("Write result to: "+inputSubtitleFile.getFile(), Trace.MESSAGE_PRIORITY);
364                                            try {
365                                                    inputSubtitleFile.writeToFile(inputSubtitleFile.getFile());
366                                            } catch (FileConversionException e) {
367                                                    // TODO Auto-generated catch block
368                                                    e.printStackTrace();
369                                            }
370                                    }
371                            }
372    
373    
374                    }else{
375                            errorMessage += "Resynchro action command must have at least 6 arguments."+NEWL+"resyn src1 dest1 src2 dest2 input_file [output_file]";
376                    }
377                    if(errorMessage != null && !errorMessage.equals("")){
378                            displayErrorMessage(errorMessage);
379                    }
380            }
381    
382            /**
383             * Method <b>delayAction</b>
384             * <br><b>Summary:</b><br>
385             * This method is called to perform a delay action.
386             * Delay action command must have at least 3 arguments.
387             * delay d input_file [output_file]
388             * d is the delay to apply, an integer.
389             * input_file is the file to perform the delay.
390             * [output_file] optionnal, if precised, action will be saved here.
391             * Parameters:
392             * @param args  The command line arguments.
393             * 
394             */
395            private static void delayAction(String[] args) {
396                    Trace.trace("Delay action.", Trace.MESSAGE_PRIORITY);
397                    //Delay action command must have at least 3 arguments.
398                    //if not display message error.
399                    String errorMessage = "";
400                    //must have at least 3 parameter for a delay.
401                    if(args.length >= 3){
402                            try{
403                                    //Get the delay to apply.
404                                    double delay = Double.parseDouble(args[1]);
405                                    //open the input file.
406                                    errorMessage += openInputFile(args[2]);
407                                    //If outputfile is precised, read it.
408                                    if(args.length >= 4){
409                                            errorMessage += openOutputFile(args[3]);
410                                    }
411                                    if(errorMessage != null && errorMessage.equals("")){
412                                            Trace.trace("Proceed to Delay: delay "+delay+ " on file : "+inputSubtitleFile.getFile(), Trace.MESSAGE_PRIORITY);
413                                            //delay * 1000 because delay is in seconds, with milliseconds.
414                                            inputSubtitleFile.delay((int)(delay*1000));
415                                            if(outputFile != null){
416                                                    Trace.trace("Write result to: "+outputFile, Trace.MESSAGE_PRIORITY);
417                                                    try {
418                                                            inputSubtitleFile.writeToFile(outputFile);
419                                                    } catch (FileConversionException e) {
420                                                            // TODO Auto-generated catch block
421                                                            e.printStackTrace();
422                                                    }
423                                            }else{
424                                                    Trace.trace("Write result to: "+inputSubtitleFile.getFile(), Trace.MESSAGE_PRIORITY);
425                                                    try {
426                                                            inputSubtitleFile.writeToFile(inputSubtitleFile.getFile());
427                                                    } catch (FileConversionException e) {
428                                                            // TODO Auto-generated catch block
429                                                            e.printStackTrace();
430                                                    }
431                                            }
432                                    }
433                            }catch(NumberFormatException e){
434                                    errorMessage += "Must enter an integer delay";
435                            }
436    
437                    }else{
438                            errorMessage += "Delay action command must have at least 3 arguments."+NEWL+"delay d input_file [output_file]";
439                    }
440                    if(errorMessage != null && !errorMessage.equals("")){
441                            displayErrorMessage(errorMessage);
442                    }
443            }
444    
445    
446            /**
447             * Method <b>openOutputFile</b>
448             * <br><b>Summary:</b><br>
449             * This method open the given file.
450             * Store it in output file.
451             * Parameters:
452             * @param   file                The file to open.    
453             * @return  <b>String</b>       The error message, or "" if no error has been found.
454             */
455            private static String openOutputFile(String file) {
456                    //The result of the Method
457                    String message ="";
458                    //Construct file
459                    File output = new File(file);
460                    //check that file could be opened.
461                    if(output.exists() && !output.canWrite()){
462                            message += "File "+file+" can not be written."+NEWL;
463                    }else{
464                            outputFile = output;
465                    }
466                    //return the error message.
467                    return message;
468            }
469    
470    
471            /**
472             * Method <b>openInputFile</b>
473             * <br><b>Summary:</b><br>
474             * This method open the given file.
475             * Store it in input file.
476             * Parameters:
477             * @param   file                The file to open.    
478             * @return  <b>String</b>       The error message, or "" if no error has been found.
479             * 
480             */
481            private static String openInputFile(String file) {
482                    //The result of the Method
483                    String message ="";
484                    //Construct file
485                    File inputFile = new File(file);
486                    //check that file could be opened.
487                    if(!inputFile.canRead()){
488                            message += "File "+file+" can not be read."+NEWL;
489                    }
490                    //Construct subtitleFile
491                    try {
492                            inputSubtitleFile = new SrtFile(inputFile, new ArrayList<Subtitle>());
493                    } catch (FileConversionException e) {
494                            e.printStackTrace();
495                    }
496                    //return the error message.
497                    return message;
498            }
499    
500    
501            /**
502             * Method <b>displayErrorMessage</b>
503             * <br><b>Summary:</b><br>
504             * This method display an error.
505             * Parameters:
506             * @param error    The error to display.
507             * 
508             */
509            private static void displayErrorMessage(String error) {
510                    System.out.println("Error : "+NEWL+error+NEWL);
511            }
512    }
513