bdsup2sub.cli.CommandLineParser Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2014 Miklos Juhasz (mjuhasz)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package bdsup2sub.cli;
import bdsup2sub.core.*;
import bdsup2sub.utils.FilenameUtils;
import bdsup2sub.utils.optional.Optional;
import bdsup2sub.utils.SubtitleUtils;
import bdsup2sub.utils.ToolBox;
import org.apache.commons.cli.*;
import java.io.File;
import java.util.Arrays;
import java.util.Comparator;
import static bdsup2sub.cli.CommandLineOptions.*;
import static bdsup2sub.core.Configuration.*;
import static bdsup2sub.core.Constants.LANGUAGES;
public class CommandLineParser {
private boolean printHelpMode;
private boolean printVersionMode;
private boolean cliMode;
private File inputFile;
private File outputFile;
private Optional outputMode = Optional.absent();
private boolean loadSettings;
private Optional resolution = Optional.absent();
private Optional sourceFrameRate = Optional.absent();
private Optional targetFrameRate = Optional.absent();
private boolean convertFpsMode;
private boolean synchronizeFpsMode;
private Optional delay = Optional.absent();
private Optional scalingFilter = Optional.absent();
private Optional paletteMode = Optional.absent();
private Optional minimumDisplayTime = Optional.absent();
private Optional maximumTimeDifference = Optional.absent();
private Optional moveModeY = Optional.absent();
private double screenRatio;
private int moveYOffset;
private Optional moveModeX = Optional.absent();
private Optional moveXOffset = Optional.absent();
private Optional cropLines = Optional.absent();
private Optional alphaCropThreshold = Optional.absent();
private Optional scaleX = Optional.absent();
private Optional scaleY = Optional.absent();
private Optional exportPalette = Optional.absent();
private Optional exportForcedSubtitlesOnly = Optional.absent();
private Optional forcedFlagState = Optional.absent();
private Optional swapCrCb = Optional.absent();
private Optional fixInvisibleFrames = Optional.absent();
private Optional verbose = Optional.absent();
private Optional alphaThreshold = Optional.absent();
private Optional lumLowMedThreshold = Optional.absent();
private Optional lumMedHighThreshold = Optional.absent();
private Optional languageIndex = Optional.absent();
private File paletteFile;
private Options options;
public CommandLineParser() {
this.options = new CommandLineOptions().getOptions();
}
public void parse(String... args) throws ParseException {
CommandLine line = new PosixParser().parse(options, args);
if (line.hasOption(HELP)) {
printHelpMode = true;
} else if (line.hasOption(VERSION)) {
printVersionMode = true;
} else {
parseInputFileOption(line);
parseOutputFileOption(line);
cliMode = line.hasOption(OUTPUT_FILE);
loadSettings = line.hasOption(LOAD_SETTINGS) || !cliMode;
parseResolutionOption(line);
parseTargetFramerateOption(line);
parseConvertFramerateOption(line);
parseDelayOption(line);
parseScalingFilterOption(line);
parsePaletteModeOption(line);
parseMinimumDisplayTimeOption(line);
parseMaxTimeDiffOption(line);
parseMoveYOption(line);
parseMoveXOption(line);
parseCropLinesOption(line);
parseAlphaCropThresholdOption(line);
parseScaleOption(line);
exportPalette = line.hasOption(EXPORT_PALETTE) ? Optional.of(Boolean.TRUE) : Optional.absent();
exportForcedSubtitlesOnly = line.hasOption(EXPORT_FORCED_SUBTITLES_ONLY) ? Optional.of(Boolean.TRUE) : Optional.absent();
parseForcedFlagOption(line);
swapCrCb = line.hasOption(SWAP_CR_CB) ? Optional.of(Boolean.TRUE) : Optional.absent();
fixInvisibleFrames = line.hasOption(FIX_INVISIBLE_FRAMES) ? Optional.of(Boolean.TRUE) : Optional.absent(); // TODO: accept only for SUB/IDX or SUP/IFO as target
verbose = line.hasOption(VERBOSE) ? Optional.of(Boolean.TRUE) : Optional.absent();
parseAlphaThresholdOption(line);
parseLuminanceThresholdOption(line);
parseLanguageCodeOption(line);
parsePaletteFileOption(line);
}
}
private void parseInputFileOption(CommandLine line) throws ParseException {
if (line.getArgList().isEmpty() && line.hasOption(OUTPUT_FILE)) {
throw new ParseException("Missing input file.");
} else if (line.getArgList().size() > 1) {
throw new ParseException("Too many input files.");
} else if (line.getArgList().size() == 1) {
inputFile = new File(line.getArgList().get(0).toString());
if (!inputFile.exists()) {
throw new ParseException("Input file not found: " + inputFile.getAbsolutePath());
}
}
}
private void parseOutputFileOption(CommandLine line) throws ParseException {
if (line.hasOption(OUTPUT_FILE)) {
String value = line.getOptionValue(OUTPUT_FILE);
outputFile = new File(value);
String extension = FilenameUtils.getExtension(value);
if (extension.isEmpty()) {
throw new ParseException("No extension given for output " + outputFile);
}
if (extension.equalsIgnoreCase("sup")) {
outputMode = Optional.of(OutputMode.BDSUP);
} else if (extension.equalsIgnoreCase("sub") || extension.equals("idx")) {
outputMode = Optional.of(OutputMode.VOBSUB);
} else if (extension.equalsIgnoreCase("xml")) {
outputMode = Optional.of(OutputMode.XML);
} else if (extension.equalsIgnoreCase("ifo")) {
outputMode = Optional.of(OutputMode.SUPIFO);
} else {
throw new ParseException("Unknown extension of output " + outputFile);
}
}
}
private void parseResolutionOption(CommandLine line) throws ParseException {
if (line.hasOption(RESOLUTION)) {
String value = line.getOptionValue(RESOLUTION);
if (value.equalsIgnoreCase("keep")) {
// keep undefined
} else if (value.equalsIgnoreCase("pal") || value.equalsIgnoreCase("576")) {
resolution = Optional.of(Resolution.PAL);
} else if (value.equalsIgnoreCase("ntsc") || value.equalsIgnoreCase("480")) {
resolution = Optional.of(Resolution.NTSC);
} else if (value.equalsIgnoreCase("720p") || value.equalsIgnoreCase("720")) {
resolution = Optional.of(Resolution.HD_720);
} else if (value.equalsIgnoreCase("1440x1080")) {
resolution = Optional.of(Resolution.HD_1440x1080);
} else if (value.equalsIgnoreCase("1080p") || value.equalsIgnoreCase("1080")) {
resolution = Optional.of(Resolution.HD_1080);
} else {
throw new ParseException("Illegal resolution: " + value);
}
}
}
private void parseTargetFramerateOption(CommandLine line) throws ParseException {
if (line.hasOption(TARGET_FRAMERATE)) {
synchronizeFpsMode = true;
String value = line.getOptionValue(TARGET_FRAMERATE);
if (value.equalsIgnoreCase("keep")) {
// keep undefined
} else {
targetFrameRate = Optional.of(SubtitleUtils.getFps(value));
if (targetFrameRate.get() <= 0) {
throw new ParseException("Invalid target framerate: " + value);
}
}
}
}
private void parseConvertFramerateOption(CommandLine line) throws ParseException {
if (line.hasOption(CONVERT_FRAMERATE)) {
convertFpsMode = true;
if (line.getOptionValues(CONVERT_FRAMERATE).length != 2) {
throw new ParseException("2 arguments needed for framerate conversion.");
}
String value = line.getOptionValues(CONVERT_FRAMERATE)[0];
if (value.equalsIgnoreCase("auto")) {
// keep undefined
} else {
sourceFrameRate = Optional.of(SubtitleUtils.getFps(value));
if (sourceFrameRate.get() <= 0) {
throw new ParseException("Invalid source framerate: " + value);
}
}
value = line.getOptionValues(CONVERT_FRAMERATE)[1];
targetFrameRate = Optional.of(SubtitleUtils.getFps(value));
if (targetFrameRate.get() <= 0) {
throw new ParseException("Invalid target framerate: " + value);
}
}
}
private void parseDelayOption(CommandLine line) throws ParseException {
if (line.hasOption(DELAY)) {
String value = line.getOptionValue(DELAY);
try {
delay = Optional.of(Double.parseDouble(value.trim()));
} catch (NumberFormatException ex) {
throw new ParseException("Illegal delay value: " + value);
}
}
}
private void parseScalingFilterOption(CommandLine line) throws ParseException {
if (line.hasOption(SCALING_FILTER)) {
String value = line.getOptionValue(SCALING_FILTER);
boolean found = false;
for (ScalingFilter f : ScalingFilter.values()) {
if (f.toString().equalsIgnoreCase(value)) {
scalingFilter = Optional.of(f);
found = true;
break;
}
}
if (!found) {
throw new ParseException("Illegal scaling filter value: " + value);
}
}
}
private void parsePaletteModeOption(CommandLine line) throws ParseException {
if (line.hasOption(PALETTE_MODE)) {
String value = line.getOptionValue(PALETTE_MODE);
if (value.equalsIgnoreCase("keep")) {
paletteMode = Optional.of(PaletteMode.KEEP_EXISTING);
} else if (value.equalsIgnoreCase("create")) {
paletteMode = Optional.of(PaletteMode.CREATE_NEW);
} else if (value.equalsIgnoreCase("dither")) {
paletteMode = Optional.of(PaletteMode.CREATE_DITHERED);
} else {
throw new ParseException("Invalid palette mode: " + value);
}
}
}
private void parseMinimumDisplayTimeOption(CommandLine line) throws ParseException {
if (line.hasOption(MIN_DISPLAY_TIME)) {
String value = line.getOptionValue(MIN_DISPLAY_TIME);
try {
minimumDisplayTime = Optional.of(Double.parseDouble(value.trim()));
} catch (NumberFormatException ex) {
throw new ParseException("Illegal minimum display time value: " + value);
}
if (minimumDisplayTime.get() <= 0) {
throw new ParseException("Illegal minimum display time value: " + value);
}
}
}
private void parseMaxTimeDiffOption(CommandLine line) throws ParseException {
if (line.hasOption(MAX_TIME_DIFF)) {
String value = line.getOptionValue(MAX_TIME_DIFF);
try {
maximumTimeDifference = Optional.of(Double.parseDouble(value.trim()));
} catch (NumberFormatException ex) {
throw new ParseException("Illegal maximum merge time difference value: " + value);
}
if (maximumTimeDifference.get() < 0) {
throw new ParseException("Illegal maximum merge time difference value: " + value);
}
}
}
private void parseMoveYOption(CommandLine line) throws ParseException {
if (line.hasOption(MOVE_IN) || line.hasOption(MOVE_OUT)) {
moveModeY = line.hasOption(MOVE_IN) ? Optional.of(CaptionMoveModeY.MOVE_INSIDE_BOUNDS) : Optional.of(CaptionMoveModeY.MOVE_OUTSIDE_BOUNDS);
String option = line.hasOption(MOVE_IN) ? MOVE_IN : MOVE_OUT;
if (line.getOptionValues(option).length != 2) {
throw new ParseException("2 arguments needed for moving captions.");
}
screenRatio = ToolBox.getDouble(line.getOptionValues(option)[0]);
if (screenRatio <= (16.0 / 9)) {
throw new ParseException("Invalid screen ratio: " + screenRatio);
}
moveYOffset = ToolBox.getInt(line.getOptionValues(option)[1]);
if (moveYOffset < 0) {
throw new ParseException("Invalid pixel offset: " + moveYOffset);
}
}
}
private void parseMoveXOption(CommandLine line) throws ParseException {
if (line.hasOption(MOVE_X)) {
if (line.getOptionValues(MOVE_X) == null || line.getOptionValues(MOVE_X).length < 1) {
throw new ParseException("Missing argument for moving captions.");
}
String value = line.getOptionValues(MOVE_X)[0];
if (value.equalsIgnoreCase("left")) {
moveModeX = Optional.of(CaptionMoveModeX.LEFT);
} else if (value.equalsIgnoreCase("center")) {
moveModeX = Optional.of(CaptionMoveModeX.CENTER);
} else if (value.equalsIgnoreCase("right")) {
moveModeX = Optional.of(CaptionMoveModeX.RIGHT);
} else {
throw new ParseException("Invalid move mode: " + value);
}
if ((moveModeX.get() == CaptionMoveModeX.LEFT || moveModeX.get() == CaptionMoveModeX.RIGHT) && line.getOptionValues(MOVE_X).length > 1) {
value = line.getOptionValues(MOVE_X)[1];
moveXOffset = Optional.of(ToolBox.getInt(value));
if (moveXOffset.get() < 0) {
throw new ParseException("Invalid pixel offset: " + value);
}
}
}
}
private void parseCropLinesOption(CommandLine line) throws ParseException {
if (line.hasOption(CROP_LINES)) {
String value = line.getOptionValue(CROP_LINES);
cropLines = Optional.of(ToolBox.getInt(value.trim()));
if (cropLines.get() < 0) {
throw new ParseException("Invalid crop lines value: " + value);
}
}
}
private void parseAlphaCropThresholdOption(CommandLine line) throws ParseException {
if (line.hasOption(ALPHA_CROP_THRESHOLD)) {
String value = line.getOptionValue(ALPHA_CROP_THRESHOLD);
alphaCropThreshold = Optional.of(ToolBox.getInt(value.trim()));
if (alphaCropThreshold.get() < 0 || alphaCropThreshold.get() > 255) {
throw new ParseException("Illegal number range for alpha cropping threshold: " + value);
}
}
}
private void parseScaleOption(CommandLine line) throws ParseException {
if (line.hasOption(SCALE)) {
if (line.getOptionValues(SCALE).length != 2) {
throw new ParseException("2 arguments needed for scaling.");
}
String value = line.getOptionValues(SCALE)[0];
scaleX = Optional.of(ToolBox.getDouble(value));
if (scaleX.get() < MIN_FREE_SCALE_FACTOR || scaleX.get() > MAX_FREE_SCALE_FACTOR) {
throw new ParseException("Invalid x scaling factor: " + value);
}
value = line.getOptionValues(SCALE)[1];
scaleY = Optional.of(ToolBox.getDouble(value));
if (scaleY.get() < MIN_FREE_SCALE_FACTOR || scaleY.get() > MAX_FREE_SCALE_FACTOR) {
throw new ParseException("Invalid y scaling factor: " + value);
}
}
}
private void parseForcedFlagOption(CommandLine line) throws ParseException {
if (line.hasOption(FORCED_FLAG)) {
String value = line.getOptionValue(FORCED_FLAG);
if (value.equalsIgnoreCase("set")) {
forcedFlagState = Optional.of(ForcedFlagState.SET);
} else if (value.equalsIgnoreCase("clear")) {
forcedFlagState = Optional.of(ForcedFlagState.CLEAR);
} else {
throw new ParseException("Invalid forced flag state: " + value);
}
}
}
private void parseAlphaThresholdOption(CommandLine line) throws ParseException {
if (line.hasOption(ALPHA_THRESHOLD)) { // TODO: accept only for SUB/IDX or SUP/IFO as target
String value = line.getOptionValue(ALPHA_THRESHOLD);
alphaThreshold = Optional.of(ToolBox.getInt(value.trim()));
if (alphaThreshold.get() < 0 || alphaThreshold.get() > 255) {
throw new ParseException("Illegal number range for alpha threshold: " + value);
}
}
}
private void parseLuminanceThresholdOption(CommandLine line) throws ParseException {
if (line.hasOption(LUM_LOW_MED_THRESHOLD)) { // TODO: accept only for SUB/IDX or SUP/IFO as target
String value = line.getOptionValue(LUM_LOW_MED_THRESHOLD);
lumLowMedThreshold = Optional.of(ToolBox.getInt(value.trim()));
if (lumLowMedThreshold.get() < 0 || lumLowMedThreshold.get() > 255) {
throw new ParseException("Illegal number range for luminance threshold: " + value);
}
}
if (line.hasOption(LUM_MED_HIGH_THRESHOLD)) { // TODO: accept only for SUB/IDX or SUP/IFO as target
String value = line.getOptionValue(LUM_MED_HIGH_THRESHOLD);
lumMedHighThreshold = Optional.of(ToolBox.getInt(value.trim()));
if (lumMedHighThreshold.get() < 0 || lumMedHighThreshold.get() > 255) {
throw new ParseException("Illegal number range for luminance threshold: " + value);
}
}
if (lumLowMedThreshold.isPresent() || lumMedHighThreshold.isPresent()) {
int lowMed = lumLowMedThreshold.isPresent() ? lumLowMedThreshold.get() : DEFAULT_LUMINANCE_LOW_MED_THRESHOLD;
int medHigh = lumMedHighThreshold.isPresent() ? lumMedHighThreshold.get() : DEFAULT_LUMINANCE_MED_HIGH_THRESHOLD;
if (lowMed > medHigh) {
throw new ParseException("Invalid luminance threshold values: " + lumLowMedThreshold + ", " + lumMedHighThreshold);
}
}
}
private void parseLanguageCodeOption(CommandLine line) throws ParseException {
if (line.hasOption(LANGUAGE_CODE)) { //TODO: only sub/idx
String value = line.getOptionValue(LANGUAGE_CODE);
boolean found = false;
for (int i = 0; i < LANGUAGES.length; i++)
if (LANGUAGES[i][1].equalsIgnoreCase(value)) {
languageIndex = Optional.of(i);
found = true;
break;
}
if (!found) {
StringBuilder sb = new StringBuilder();
sb.append("Unknown language code: " + value + "\n");
sb.append("Use one of the following 2 character codes:\n");
for (String[] language : LANGUAGES) {
sb.append(" " + language[1] + " - " + language[0] + "\n");
}
throw new ParseException(sb.toString());
}
}
}
private void parsePaletteFileOption(CommandLine line) throws ParseException {
if (line.hasOption(PALETTE_FILE)) { //TODO: only sub/idx
String value = line.getOptionValue(PALETTE_FILE);
paletteFile = new File(value);
if (!paletteFile.exists()) {
throw new ParseException("Palette file not found: " + value);
} else {
if (!Arrays.equals("#COL".getBytes(), ToolBox.getFileID(value, 4))) {
throw new ParseException("Invalid palette file: " + value);
}
}
}
}
public boolean isPrintHelpMode() {
return printHelpMode;
}
public boolean isPrintVersionMode() {
return printVersionMode;
}
public boolean isCliMode() {
return cliMode;
}
public File getInputFile() {
return inputFile;
}
public File getOutputFile() {
return outputFile;
}
public Optional getOutputMode() {
return outputMode;
}
public boolean isLoadSettings() {
return loadSettings;
}
public Optional getResolution() {
return resolution;
}
public Optional getSourceFrameRate() {
return sourceFrameRate;
}
public Optional getTargetFrameRate() {
return targetFrameRate;
}
public boolean isConvertFpsMode() {
return convertFpsMode;
}
public boolean isSynchronizeFpsMode() {
return synchronizeFpsMode;
}
public Optional getDelay() {
return delay;
}
public Optional getScalingFilter() {
return scalingFilter;
}
public Optional getPaletteMode() {
return paletteMode;
}
public Optional getMinimumDisplayTime() {
return minimumDisplayTime;
}
public Optional getMaximumTimeDifference() {
return maximumTimeDifference;
}
public Optional getMoveModeY() {
return moveModeY;
}
public int getMoveYOffset() {
return moveYOffset;
}
public Optional getMoveModeX() {
return moveModeX;
}
public Optional getMoveXOffset() {
return moveXOffset;
}
public double getScreenRatio() {
return screenRatio;
}
public Optional getCropLines() {
return cropLines;
}
public Optional getAlphaCropThreshold() {
return alphaCropThreshold;
}
public Optional getScaleX() {
return scaleX;
}
public Optional getScaleY() {
return scaleY;
}
public Optional isExportPalette() {
return exportPalette;
}
public Optional isExportForcedSubtitlesOnly() {
return exportForcedSubtitlesOnly;
}
public Optional getForcedFlagState() {
return forcedFlagState;
}
public Optional isSwapCrCb() {
return swapCrCb;
}
public Optional isFixInvisibleFrames() {
return fixInvisibleFrames;
}
public Optional isVerbose() {
return verbose;
}
public Optional getAlphaThreshold() {
return alphaThreshold;
}
public Optional getLumLowMedThreshold() {
return lumLowMedThreshold;
}
public Optional getLumMedHighThreshold() {
return lumMedHighThreshold;
}
public Optional getLanguageIndex() {
return languageIndex;
}
public File getPaletteFile() {
return paletteFile;
}
public void printHelp() {
HelpFormatter formatter = new HelpFormatter();
formatter.setOptionComparator(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
Option opt1 = (Option) o1;
Option opt2 = (Option) o2;
int opt1Index = OPTION_ORDER.indexOf(opt1.getOpt());
int opt2Index = OPTION_ORDER.indexOf(opt2.getOpt());
return (int) Math.signum(opt1Index - opt2Index);
}
});
formatter.setWidth(79);
String command = System.getProperty("wrapper") == null ? "java -jar BDSup2Sub" : "bdsup2sub";
formatter.printHelp(command + " [options] -o