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