]> git.xonotic.org Git - xonotic/xonotic.git/blob - misc/tools/NexuizDemoRecorder/main/src/main/java/com/nexuiz/demorecorder/application/jobs/RecordJob.java
initial checkin from nexuiz svn r8756
[xonotic/xonotic.git] / misc / tools / NexuizDemoRecorder / main / src / main / java / com / nexuiz / demorecorder / application / jobs / RecordJob.java
1 package com.nexuiz.demorecorder.application.jobs;\r
2 \r
3 import java.io.BufferedReader;\r
4 import java.io.File;\r
5 import java.io.IOException;\r
6 import java.io.InputStream;\r
7 import java.io.InputStreamReader;\r
8 import java.io.Serializable;\r
9 import java.util.ArrayList;\r
10 import java.util.HashMap;\r
11 import java.util.List;\r
12 import java.util.Map;\r
13 import java.util.Properties;\r
14 \r
15 import com.nexuiz.demorecorder.application.DemoRecorderApplication;\r
16 import com.nexuiz.demorecorder.application.DemoRecorderException;\r
17 import com.nexuiz.demorecorder.application.DemoRecorderUtils;\r
18 import com.nexuiz.demorecorder.application.NDRPreferences;\r
19 import com.nexuiz.demorecorder.application.DemoRecorderApplication.Preferences;\r
20 import com.nexuiz.demorecorder.application.democutter.DemoCutter;\r
21 import com.nexuiz.demorecorder.application.democutter.DemoCutterException;\r
22 import com.nexuiz.demorecorder.application.plugins.EncoderPlugin;\r
23 import com.nexuiz.demorecorder.application.plugins.EncoderPluginException;\r
24 \r
25 public class RecordJob implements Runnable, Serializable {\r
26         \r
27         private static final long serialVersionUID = -4585637490345587912L;\r
28 \r
29         public enum State {\r
30                 WAITING, PROCESSING, ERROR, ERROR_PLUGIN, DONE\r
31         }\r
32         \r
33         public static final String CUT_DEMO_FILE_SUFFIX = "_autocut";\r
34         public static final String CUT_DEMO_CAPVIDEO_NAMEFORMAT_OVERRIDE = "autocap";\r
35         public static final String CUT_DEMO_CAPVIDEO_NUMBER_OVERRIDE = "1234567";\r
36         protected static final String[] VIDEO_FILE_ENDINGS = {"avi", "ogv"};\r
37         \r
38         private DemoRecorderApplication appLayer;\r
39         protected String jobName;\r
40         private int jobIndex;\r
41         protected File enginePath;\r
42         protected String engineParameters;\r
43         protected File demoFile;\r
44         protected String relativeDemoPath;\r
45         protected File dpVideoPath;\r
46         protected File videoDestination;\r
47         protected String executeBeforeCap;\r
48         protected String executeAfterCap;\r
49         protected float startSecond;\r
50         protected float endSecond;\r
51         protected State state = State.WAITING;\r
52         protected DemoRecorderException lastException = null;\r
53         \r
54         /**\r
55          * Points to the actual final file, including possible suffixes, e.g. _copy1, and the actualy ending\r
56          */\r
57         protected File actualVideoDestination = null;\r
58         /**\r
59          * Map that identifies the plug-in by its name (String) and maps to the plug-in's job-specific settings\r
60          */\r
61         protected Map<String, Properties> encoderPluginSettings = new HashMap<String, Properties>();\r
62         \r
63         private List<File> cleanUpFiles = null;\r
64         \r
65         public RecordJob(\r
66                 DemoRecorderApplication appLayer,\r
67                 String jobName,\r
68                 int jobIndex,\r
69                 File enginePath,\r
70                 String engineParameters,\r
71                 File demoFile,\r
72                 String relativeDemoPath,\r
73                 File dpVideoPath,\r
74                 File videoDestination,\r
75                 String executeBeforeCap,\r
76                 String executeAfterCap,\r
77                 float startSecond,\r
78                 float endSecond\r
79         ) {\r
80                 this.appLayer = appLayer;\r
81                 this.jobName = jobName;\r
82                 this.jobIndex = jobIndex;\r
83                 \r
84                 this.setEnginePath(enginePath);\r
85                 this.setEngineParameters(engineParameters);\r
86                 this.setDemoFile(demoFile);\r
87                 this.setRelativeDemoPath(relativeDemoPath);\r
88                 this.setDpVideoPath(dpVideoPath);\r
89                 this.setVideoDestination(videoDestination);\r
90                 this.setExecuteBeforeCap(executeBeforeCap);\r
91                 this.setExecuteAfterCap(executeAfterCap);\r
92                 this.setStartSecond(startSecond);\r
93                 this.setEndSecond(endSecond);\r
94         }\r
95         \r
96         public RecordJob(){}\r
97         \r
98         /**\r
99          * Constructor that can be used by other classes such as job templates. Won't throw exceptions\r
100          * as it won't check the input for validity.\r
101          */\r
102         protected RecordJob(\r
103                 File enginePath,\r
104                 String engineParameters,\r
105                 File demoFile,\r
106                 String relativeDemoPath,\r
107                 File dpVideoPath,\r
108                 File videoDestination,\r
109                 String executeBeforeCap,\r
110                 String executeAfterCap,\r
111                 float startSecond,\r
112                 float endSecond\r
113                 ) {\r
114                 this.jobIndex = -1;\r
115                 this.enginePath = enginePath;\r
116                 this.engineParameters = engineParameters;\r
117                 this.demoFile = demoFile;\r
118                 this.relativeDemoPath = relativeDemoPath;\r
119                 this.dpVideoPath = dpVideoPath;\r
120                 this.videoDestination = videoDestination;\r
121                 this.executeBeforeCap = executeBeforeCap;\r
122                 this.executeAfterCap = executeAfterCap;\r
123                 this.startSecond = startSecond;\r
124                 this.endSecond = endSecond;\r
125         }\r
126         \r
127         public void execute() {\r
128                 if (this.state == State.PROCESSING) {\r
129                         return;\r
130                 }\r
131                 boolean errorOccurred = false;\r
132                 this.setState(State.PROCESSING);\r
133                 this.appLayer.fireUserInterfaceUpdate(this);\r
134                 cleanUpFiles = new ArrayList<File>();\r
135                 \r
136                 File cutDemo = computeCutDemoFile();\r
137                 cutDemo.delete(); //delete possibly old cutDemoFile\r
138                 \r
139                 EncoderPlugin recentEncoder = null;\r
140                 \r
141                 try {\r
142                         this.cutDemo(cutDemo);\r
143                         this.removeOldAutocaps();\r
144                         this.recordClip(cutDemo);\r
145                         this.moveRecordedClip();\r
146                         for (EncoderPlugin plugin : this.appLayer.getEncoderPlugins()) {\r
147                                 recentEncoder = plugin;\r
148                                 plugin.executeEncoder(this);\r
149                         }\r
150                 } catch (DemoRecorderException e) {\r
151                         errorOccurred = true;\r
152                         this.lastException = e;\r
153                         this.setState(State.ERROR);\r
154                 } catch (EncoderPluginException e) {\r
155                         errorOccurred = true;\r
156                         this.lastException = new DemoRecorderException("Encoder plug-in " + recentEncoder.getName() + " failed: "\r
157                                         + e.getMessage(), e);\r
158                         this.setState(State.ERROR_PLUGIN);\r
159                 } catch (Exception e) {\r
160                         errorOccurred = true;\r
161                         this.lastException = new DemoRecorderException("Executing job failed, click on details for more info", e);\r
162                 } finally {\r
163                         NDRPreferences preferences = this.appLayer.getPreferences();\r
164                         if (!Boolean.valueOf(preferences.getProperty(NDRPreferences.MAIN_APPLICATION, Preferences.DO_NOT_DELETE_CUT_DEMOS))) {\r
165                                 cleanUpFiles.add(cutDemo);\r
166                         }\r
167                         if (!errorOccurred) {\r
168                                 this.setState(State.DONE);\r
169                         }\r
170                         this.cleanUpFiles();\r
171                         this.appLayer.fireUserInterfaceUpdate(this);\r
172                         this.appLayer.saveJobQueue();\r
173                 }\r
174         }\r
175         \r
176         /**\r
177          * Will execute just the specified encoder plug-in on an already "done" job.\r
178          * @param pluginName\r
179          */\r
180         public void executePlugin(EncoderPlugin plugin) {\r
181                 if (this.getState() != State.DONE) {\r
182                         return;\r
183                 }\r
184                 this.setState(State.PROCESSING);\r
185                 this.appLayer.fireUserInterfaceUpdate(this);\r
186                 \r
187                 try {\r
188                         plugin.executeEncoder(this);\r
189                         this.setState(State.DONE);\r
190                 } catch (EncoderPluginException e) {\r
191                         this.lastException = new DemoRecorderException("Encoder plug-in " + plugin.getName() + " failed: "\r
192                                         + e.getMessage(), e);\r
193                         this.setState(State.ERROR_PLUGIN);\r
194                 }\r
195                 \r
196                 this.appLayer.fireUserInterfaceUpdate(this);\r
197         }\r
198         \r
199         private void cleanUpFiles() {\r
200                 try {\r
201                         for (File f : this.cleanUpFiles) {\r
202                                 f.delete();\r
203                         }\r
204                 } catch (Exception e) {}\r
205                 \r
206         }\r
207         \r
208         private void moveRecordedClip() {\r
209                 //1. Figure out whether the file is .avi or .ogv\r
210                 File sourceFile = null;\r
211                 for (String videoExtension : VIDEO_FILE_ENDINGS) {\r
212                         String fileString = this.dpVideoPath.getAbsolutePath() + File.separator + CUT_DEMO_CAPVIDEO_NAMEFORMAT_OVERRIDE\r
213                         + CUT_DEMO_CAPVIDEO_NUMBER_OVERRIDE + "." + videoExtension;\r
214                         File videoFile = new File(fileString);\r
215                         if (videoFile.exists()) {\r
216                                 sourceFile = videoFile;\r
217                                 break;\r
218                         }\r
219                 }\r
220                 \r
221                 if (sourceFile == null) {\r
222                         String p = this.dpVideoPath.getAbsolutePath() + File.separator + CUT_DEMO_CAPVIDEO_NAMEFORMAT_OVERRIDE\r
223                         + CUT_DEMO_CAPVIDEO_NUMBER_OVERRIDE;\r
224                         throw new DemoRecorderException("Could not locate the expected video file being generated by Nexuiz (should have been at "\r
225                                         + p + ".avi/.ogv");\r
226                 }\r
227                 cleanUpFiles.add(sourceFile);\r
228                 \r
229                 File destinationFile = null;\r
230                 NDRPreferences preferences = this.appLayer.getPreferences();\r
231                 String sourceFileExtension = DemoRecorderUtils.getFileExtension(sourceFile);\r
232                 String destinationFilePath = this.videoDestination + "." + sourceFileExtension;\r
233                 destinationFile = new File(destinationFilePath);\r
234                 if (destinationFile.exists()) {\r
235                         if (Boolean.valueOf(preferences.getProperty(NDRPreferences.MAIN_APPLICATION, Preferences.OVERWRITE_VIDEO_FILE))) {\r
236                                 if (!destinationFile.delete()) {\r
237                                         throw new DemoRecorderException("Could not delete the existing video destinatin file " + destinationFile.getAbsolutePath()\r
238                                                         + " (application setting to overwrite existing video files is enabled!)");\r
239                                 }\r
240                         } else {\r
241                                 destinationFilePath = this.videoDestination + "_copy" + this.getVideoDestinationCopyNr(sourceFileExtension) + "." + sourceFileExtension;\r
242                                 destinationFile = new File(destinationFilePath);\r
243                         }\r
244                 }\r
245                 \r
246                 //finally move the file\r
247                 if (!sourceFile.renameTo(destinationFile)) {\r
248                         cleanUpFiles.add(destinationFile);\r
249                         throw new DemoRecorderException("Could not move the video file from " + sourceFile.getAbsolutePath()\r
250                                         + " to " + destinationFile.getAbsolutePath());\r
251                 }\r
252                 \r
253                 this.actualVideoDestination = destinationFile;\r
254         }\r
255         \r
256         /**\r
257          * As destination video files, e.g. "test"[.avi] can already exist, we have to save the\r
258          * the video file to a file name such as test_copy1 or test_copy2.\r
259          * This function will figure out what the number (1, 2....) is.\r
260          * @return\r
261          */\r
262         private int getVideoDestinationCopyNr(String sourceFileExtension) {\r
263                 int i = 1;\r
264                 File lastFile;\r
265                 while (true) {\r
266                         lastFile = new File(this.videoDestination + "_copy" + i + "." + sourceFileExtension);\r
267                         if (!lastFile.exists()) {\r
268                                 break;\r
269                         }\r
270                         \r
271                         i++;\r
272                 }\r
273                 return i;\r
274         }\r
275 \r
276         private File computeCutDemoFile() {\r
277                 String origFileString = this.demoFile.getAbsolutePath();\r
278                 int lastIndex = origFileString.lastIndexOf(File.separator);\r
279                 String autoDemoFileName = origFileString.substring(lastIndex+1, origFileString.length());\r
280                 //strip .dem ending\r
281                 autoDemoFileName = autoDemoFileName.substring(0, autoDemoFileName.length()-4);\r
282                 autoDemoFileName = autoDemoFileName + CUT_DEMO_FILE_SUFFIX + ".dem";\r
283                 String finalString = origFileString.substring(0, lastIndex) + File.separator + autoDemoFileName;\r
284                 File f = new File(finalString);\r
285                 \r
286                 return f;\r
287         }\r
288         \r
289         private void cutDemo(File cutDemo) {\r
290                 String injectAtStart = "";\r
291                 String injectBeforeCap = "";\r
292                 String injectAfterCap = "";\r
293                 \r
294                 NDRPreferences preferences = this.appLayer.getPreferences();\r
295                 if (Boolean.valueOf(preferences.getProperty(NDRPreferences.MAIN_APPLICATION, Preferences.DISABLE_RENDERING))) {\r
296                         injectAtStart += "r_render 0;";\r
297                         injectBeforeCap += "r_render 1;";\r
298                 }\r
299                 if (Boolean.valueOf(preferences.getProperty(NDRPreferences.MAIN_APPLICATION, Preferences.DISABLE_SOUND))) {\r
300                         injectAtStart += "set _volume $volume;volume 0;";\r
301                         injectBeforeCap += "set volume $_volume;";\r
302                 }\r
303                 injectBeforeCap += this.executeBeforeCap + "\n";\r
304                 injectBeforeCap += "set _cl_capturevideo_nameformat $cl_capturevideo_nameformat;set _cl_capturevideo_number $cl_capturevideo_number;";\r
305                 injectBeforeCap += "cl_capturevideo_nameformat " + CUT_DEMO_CAPVIDEO_NAMEFORMAT_OVERRIDE + ";";\r
306                 injectBeforeCap += "cl_capturevideo_number " + CUT_DEMO_CAPVIDEO_NUMBER_OVERRIDE + ";";\r
307                 \r
308                 injectAfterCap += this.executeAfterCap + "\n";\r
309                 injectAfterCap += "cl_capturevideo_nameformat $_cl_capturevideo_nameformat;cl_capturevideo_number $_cl_capturevideo_number;";\r
310                 \r
311                 \r
312                 DemoCutter cutter = new DemoCutter();\r
313                 int fwdSpeedFirstStage, fwdSpeedSecondStage;\r
314                 try {\r
315                         fwdSpeedFirstStage = Integer.parseInt(preferences.getProperty(NDRPreferences.MAIN_APPLICATION, Preferences.FFW_SPEED_FIRST_STAGE));\r
316                         fwdSpeedSecondStage = Integer.parseInt(preferences.getProperty(NDRPreferences.MAIN_APPLICATION, Preferences.FFW_SPEED_SECOND_STAGE));\r
317                 } catch (NumberFormatException e) {\r
318                         throw new DemoRecorderException("Make sure that you specified valid numbers for the settings "\r
319                                         + Preferences.FFW_SPEED_FIRST_STAGE + " and " + Preferences.FFW_SPEED_SECOND_STAGE, e);\r
320                 }\r
321                 \r
322                 try {\r
323                         cutter.cutDemo(\r
324                                 this.demoFile,\r
325                                 cutDemo,\r
326                                 this.startSecond,\r
327                                 this.endSecond,\r
328                                 injectAtStart,\r
329                                 injectBeforeCap,\r
330                                 injectAfterCap,\r
331                                 fwdSpeedFirstStage,\r
332                                 fwdSpeedSecondStage\r
333                         );\r
334                 } catch (DemoCutterException e) {\r
335                         throw new DemoRecorderException("Error occurred while trying to cut the demo: " + e.getMessage(), e);\r
336                 }\r
337                 \r
338         }\r
339         \r
340         private void removeOldAutocaps() {\r
341                 for (String videoExtension : VIDEO_FILE_ENDINGS) {\r
342                         String fileString = this.dpVideoPath.getAbsolutePath() + File.separator + CUT_DEMO_CAPVIDEO_NAMEFORMAT_OVERRIDE\r
343                         + CUT_DEMO_CAPVIDEO_NUMBER_OVERRIDE + "." + videoExtension;\r
344                         File videoFile = new File(fileString);\r
345                         cleanUpFiles.add(videoFile);\r
346                         if (videoFile.exists()) {\r
347                                 if (!videoFile.delete()) {\r
348                                         throw new DemoRecorderException("Could not delete old obsolete video file " + fileString);\r
349                                 }\r
350                         }\r
351                 }\r
352         }\r
353         \r
354         private void recordClip(File cutDemo) {\r
355                 Process nexProc;\r
356                 String demoFileName = DemoRecorderUtils.getJustFileNameOfPath(cutDemo);\r
357                 String execPath = this.enginePath.getAbsolutePath() + " " + this.engineParameters + " -demo "\r
358                                                 + this.relativeDemoPath + "/" + demoFileName;\r
359                 File engineDir = this.enginePath.getParentFile();\r
360                 try {\r
361                         nexProc = Runtime.getRuntime().exec(execPath, null, engineDir);\r
362                         nexProc.getErrorStream();\r
363                         nexProc.getOutputStream();\r
364                         InputStream is = nexProc.getInputStream();\r
365                         InputStreamReader isr = new InputStreamReader(is);\r
366                         BufferedReader br = new BufferedReader(isr);\r
367                         while (br.readLine() != null) {\r
368                                 //System.out.println(line);\r
369                         }\r
370                 } catch (IOException e) {\r
371                         throw new DemoRecorderException("I/O Exception occurred when trying to execute the Nexuiz binary", e);\r
372                 }\r
373         }\r
374 \r
375         public void run() {\r
376                 this.execute();\r
377         }\r
378         \r
379         public void setAppLayer(DemoRecorderApplication appLayer) {\r
380                 this.appLayer = appLayer;\r
381         }\r
382 \r
383         public int getJobIndex() {\r
384                 return jobIndex;\r
385         }\r
386 \r
387         public File getEnginePath() {\r
388                 return enginePath;\r
389         }\r
390 \r
391         public void setEnginePath(File enginePath) {\r
392                 this.checkForProcessingState();\r
393                 if (enginePath == null || !enginePath.exists()) {\r
394                         throw new DemoRecorderException("Could not locate engine binary!");\r
395                 }\r
396                 if (!enginePath.canExecute()) {\r
397                         throw new DemoRecorderException("The file you specified is not executable!");\r
398                 }\r
399                 this.enginePath = enginePath.getAbsoluteFile();\r
400         }\r
401 \r
402         public String getEngineParameters() {\r
403                 return engineParameters;\r
404         }\r
405 \r
406         public void setEngineParameters(String engineParameters) {\r
407                 this.checkForProcessingState();\r
408                 if (engineParameters == null) {\r
409                         engineParameters = "";\r
410                 }\r
411                 this.engineParameters = engineParameters.trim();\r
412         }\r
413 \r
414         public File getDemoFile() {\r
415                 return demoFile;\r
416         }\r
417 \r
418         public void setDemoFile(File demoFile) {\r
419                 this.checkForProcessingState();\r
420                 if (demoFile == null) {\r
421                         throw new DemoRecorderException("Could not locate demo file!");\r
422                 }\r
423                 if (!demoFile.exists()) {\r
424                         throw new DemoRecorderException("Could not locate demo file!: " + demoFile.getAbsolutePath());\r
425                 }\r
426                 if (!doReadWriteTest(demoFile.getParentFile())) {\r
427                         throw new DemoRecorderException("The directory you specified for the demo to be recorded is not writable!");\r
428                 }\r
429                 if (!demoFile.getAbsolutePath().endsWith(".dem")) {\r
430                         throw new DemoRecorderException("The demo file you specified must have the ending .dem");\r
431                 }\r
432                 \r
433                 this.demoFile = demoFile.getAbsoluteFile();\r
434         }\r
435 \r
436         public String getRelativeDemoPath() {\r
437                 return relativeDemoPath;\r
438         }\r
439 \r
440         public void setRelativeDemoPath(String relativeDemoPath) {\r
441                 this.checkForProcessingState();\r
442                 if (relativeDemoPath == null) {\r
443                         relativeDemoPath = "";\r
444                 }\r
445                 \r
446                 //get rid of possible slashes\r
447                 while (relativeDemoPath.startsWith("/") || relativeDemoPath.startsWith("\\")) {\r
448                         relativeDemoPath = relativeDemoPath.substring(1, relativeDemoPath.length());\r
449                 }\r
450                 while (relativeDemoPath.endsWith("/") || relativeDemoPath.endsWith("\\")) {\r
451                         relativeDemoPath = relativeDemoPath.substring(0, relativeDemoPath.length() - 1);\r
452                 }\r
453                 \r
454                 this.relativeDemoPath = relativeDemoPath.trim();\r
455         }\r
456 \r
457         public File getDpVideoPath() {\r
458                 return dpVideoPath;\r
459         }\r
460 \r
461         public void setDpVideoPath(File dpVideoPath) {\r
462                 this.checkForProcessingState();\r
463                 if (dpVideoPath == null || !dpVideoPath.isDirectory()) {\r
464                         throw new DemoRecorderException("Could not locate the specified DPVideo directory!");\r
465                 }\r
466                 \r
467                 if (!this.doReadWriteTest(dpVideoPath)) {\r
468                         throw new DemoRecorderException("The DPVideo directory is not writable! It needs to be writable so that the file can be moved to its new location");\r
469                 }\r
470                 this.dpVideoPath = dpVideoPath.getAbsoluteFile();\r
471         }\r
472 \r
473         public File getVideoDestination() {\r
474                 return videoDestination;\r
475         }\r
476 \r
477         public void setVideoDestination(File videoDestination) {\r
478                 this.checkForProcessingState();\r
479                 //keep in mind, the parameter videoDestination points to the final avi/ogg file w/o extension!\r
480                 if (videoDestination == null || !videoDestination.getParentFile().isDirectory()) {\r
481                         throw new DemoRecorderException("Could not locate the specified video destination");\r
482                 }\r
483                 \r
484                 if (!this.doReadWriteTest(videoDestination.getParentFile())) {\r
485                         throw new DemoRecorderException("The video destination directory is not writable! It needs to be writable so that the file can be moved to its new location");\r
486                 }\r
487                 \r
488                 this.videoDestination = videoDestination.getAbsoluteFile();\r
489         }\r
490 \r
491         public String getExecuteBeforeCap() {\r
492                 return executeBeforeCap;\r
493         }\r
494 \r
495         public void setExecuteBeforeCap(String executeBeforeCap) {\r
496                 this.checkForProcessingState();\r
497                 if (executeBeforeCap == null) {\r
498                         executeBeforeCap = "";\r
499                 }\r
500                 executeBeforeCap = executeBeforeCap.trim();\r
501                 while (executeBeforeCap.endsWith(";")) {\r
502                         executeBeforeCap = executeBeforeCap.substring(0, executeBeforeCap.length()-1);\r
503                 }\r
504                 this.executeBeforeCap = executeBeforeCap;\r
505         }\r
506 \r
507         public String getExecuteAfterCap() {\r
508                 return executeAfterCap;\r
509         }\r
510 \r
511         public void setExecuteAfterCap(String executeAfterCap) {\r
512                 this.checkForProcessingState();\r
513                 if (executeAfterCap == null) {\r
514                         executeAfterCap = "";\r
515                 }\r
516                 executeAfterCap = executeAfterCap.trim();\r
517                 while (executeAfterCap.endsWith(";")) {\r
518                         executeAfterCap = executeAfterCap.substring(0, executeAfterCap.length()-1);\r
519                 }\r
520                 if (executeAfterCap.contains("cl_capturevideo_number") || executeAfterCap.contains("cl_capturevideo_nameformat")) {\r
521                         throw new DemoRecorderException("Execute after String cannot contain cl_capturevideo_number or _nameformat changes!");\r
522                 }\r
523                 this.executeAfterCap = executeAfterCap;\r
524         }\r
525 \r
526         public float getStartSecond() {\r
527                 return startSecond;\r
528         }\r
529 \r
530         public void setStartSecond(float startSecond) {\r
531                 this.checkForProcessingState();\r
532                 if (startSecond < 0) {\r
533                         throw new DemoRecorderException("Start second cannot be < 0");\r
534                 }\r
535                 this.startSecond = startSecond;\r
536         }\r
537 \r
538         public float getEndSecond() {\r
539                 return endSecond;\r
540         }\r
541 \r
542         public void setEndSecond(float endSecond) {\r
543                 this.checkForProcessingState();\r
544                 if (endSecond < this.startSecond) {\r
545                         throw new DemoRecorderException("End second cannot be < start second");\r
546                 }\r
547                 this.endSecond = endSecond;\r
548         }\r
549 \r
550         public State getState() {\r
551                 return state;\r
552         }\r
553 \r
554         public void setState(State state) {\r
555                 this.state = state;\r
556                 this.appLayer.fireUserInterfaceUpdate(this);\r
557         }\r
558 \r
559         public String getJobName() {\r
560                 if (this.jobName == null || this.jobName.equals("")) {\r
561                         return "Job " + this.jobIndex;\r
562                 }\r
563                 return this.jobName;\r
564         }\r
565         \r
566         public void setJobName(String jobName) {\r
567                 if (jobName == null || jobName.equals("")) {\r
568                         this.jobIndex = appLayer.getNewJobIndex();\r
569                         this.jobName = "Job " + this.jobIndex;\r
570                 } else {\r
571                         this.jobName = jobName;\r
572                 }\r
573         }\r
574 \r
575         public DemoRecorderException getLastException() {\r
576                 return lastException;\r
577         }\r
578         \r
579         /**\r
580          * Tests whether the given directory is writable by creating a file in there and deleting\r
581          * it again.\r
582          * @param directory\r
583          * @return true if directory is writable\r
584          */\r
585         protected boolean doReadWriteTest(File directory) {\r
586                 boolean writable = false;\r
587                 String fileName = "tmp." + Math.random()*10000 + ".dat";\r
588                 File tempFile = new File(directory, fileName);\r
589                 try {\r
590                         writable = tempFile.createNewFile();\r
591                         if (writable) {\r
592                                 tempFile.delete();\r
593                         }\r
594                 } catch (IOException e) {\r
595                         writable = false;\r
596                 }\r
597                 return writable;\r
598         }\r
599         \r
600         private void checkForProcessingState() {\r
601                 if (this.state == State.PROCESSING) {\r
602                         throw new DemoRecorderException("Cannot modify this job while it is processing!");\r
603                 }\r
604         }\r
605 \r
606         public Properties getEncoderPluginSettings(EncoderPlugin plugin) {\r
607                 if (this.encoderPluginSettings.containsKey(plugin.getName())) {\r
608                         return this.encoderPluginSettings.get(plugin.getName());\r
609                 } else {\r
610                         return new Properties();\r
611                 }\r
612         }\r
613 \r
614         public void setEncoderPluginSetting(String pluginName, String pluginSettingKey, String value) {\r
615                 Properties p = this.encoderPluginSettings.get(pluginName);\r
616                 if (p == null) {\r
617                         p = new Properties();\r
618                         this.encoderPluginSettings.put(pluginName, p);\r
619                 }\r
620                 \r
621                 p.put(pluginSettingKey, value);\r
622         }\r
623 \r
624         public Map<String, Properties> getEncoderPluginSettings() {\r
625                 return encoderPluginSettings;\r
626         }\r
627 \r
628         public void setEncoderPluginSettings(Map<String, Properties> encoderPluginSettings) {\r
629                 this.encoderPluginSettings = encoderPluginSettings;\r
630         }\r
631 \r
632         public File getActualVideoDestination() {\r
633                 return actualVideoDestination;\r
634         }\r
635         \r
636         public void setActualVideoDestination(File actualVideoDestination) {\r
637                 this.actualVideoDestination = actualVideoDestination;\r
638         }\r
639 }\r