package net.knarcraft.ffmpegconverter.converter; import net.knarcraft.ffmpegconverter.container.FFMpegCommand; import net.knarcraft.ffmpegconverter.container.StreamProbeResult; import net.knarcraft.ffmpegconverter.handler.AvailableHardwareEncoderHandler; import net.knarcraft.ffmpegconverter.utility.FFMpegHelper; import net.knarcraft.ffmpegconverter.utility.FileUtil; import net.knarcraft.ffmpegconverter.utility.OutputUtil; import org.apache.commons.configuration2.ex.ConfigurationException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; /** * Implements all methods which can be useful for any implementation of a converter. */ public abstract class AbstractConverter implements Converter { final boolean debug = false; private final String newExtension; protected String ffprobePath; protected String ffmpegPath; protected String[] audioFormats; protected String[] videoFormats; protected AvailableHardwareEncoderHandler encoderHandler = null; /** * Initializes variables used by the abstract converter */ AbstractConverter(@Nullable String newExtension) { this.newExtension = newExtension; OutputUtil.setDebug(this.debug); try { audioFormats = FileUtil.readFileLines("audio_formats.txt"); videoFormats = FileUtil.readFileLines("video_formats.txt"); } catch (IOException e) { System.out.println("Unable to read audio and/or video formats from internal files."); System.exit(1); } } @Override public void convert(@NotNull File file) throws IOException { StreamProbeResult probeResult = FFMpegHelper.probeFile(ffprobePath, file); if (probeResult.parsedStreams().isEmpty()) { throw new IllegalArgumentException("The file has no valid streams. Please make sure the file exists and" + " is not corrupt."); } String outExtension = newExtension != null ? newExtension : FileUtil.getExtension(file.getName()); String newPath = FileUtil.getNonCollidingPath(file.getParentFile(), file, outExtension); OutputUtil.println(); OutputUtil.println("Preparing to start process..."); OutputUtil.println("Converting " + file); FFMpegCommand ffMpegCommand = generateConversionCommand(ffmpegPath, probeResult, newPath); // If the command is null, that means the file does not need conversion if (ffMpegCommand == null) { return; } String[] command = ffMpegCommand.getResult(); // If no commands were given, no conversion is necessary if (command.length == 0) { return; } ProcessBuilder processBuilder = new ProcessBuilder(command); int exitCode = FFMpegHelper.runProcess(processBuilder, file.getParentFile(), "\n", true).exitCode(); if (exitCode != 0) { try { handleError(ffMpegCommand, file, newPath); } catch (ConfigurationException e) { throw new RuntimeException(e); } } } /** * Handles an ffmpeg conversion error * * @param ffMpegCommand

The failed ffmpeg command

* @param file

The file that was to be converted

* @param newPath

The path of the output file

* @throws IOException

If unable to produce output

* @throws ConfigurationException

If unable

*/ private void handleError(@NotNull FFMpegCommand ffMpegCommand, @NotNull File file, @NotNull String newPath) throws IOException, ConfigurationException { File outputFile = new File(newPath); if (outputFile.exists() && !outputFile.delete()) { OutputUtil.println("Failed to remove failed output file. Please remove it manually"); } String outputCodec = ffMpegCommand.getOutputVideoCodec(); if (outputCodec != null && outputCodec.contains("_")) { String[] parts = outputCodec.split("_"); String hardwareAcceleration = parts[parts.length - 1]; List encodingMethods = getAvailableHardwareEncodingMethods(); if (encodingMethods.contains(hardwareAcceleration)) { OutputUtil.println("Disabling " + hardwareAcceleration + " hardware acceleration"); encoderHandler.removeHardwareEncoding(hardwareAcceleration); encoderHandler.save(); convert(file); return; } } throw new IllegalArgumentException("Conversion failed. Please check the preceding output."); } /** * Gets the available methods for hardware encoding * * @return

Available hardware encoding methods

*/ protected List getAvailableHardwareEncodingMethods() { try { if (encoderHandler == null) { encoderHandler = AvailableHardwareEncoderHandler.load(); if (encoderHandler.availableHardwareEncodings().isEmpty()) { List hardwareEncoding = new ArrayList<>(FFMpegHelper.getHWAcceleration(ffmpegPath)); hardwareEncoding.remove(0); encoderHandler = new AvailableHardwareEncoderHandler(hardwareEncoding); encoderHandler.save(); } } return encoderHandler.availableHardwareEncodings(); } catch (ConfigurationException | IOException exception) { OutputUtil.println("Unable to get available hardware encoders: " + exception.getMessage()); return new ArrayList<>(); } } }