Pipeline working, implements FFmpegSetupManager
This commit is contained in:
@@ -16,6 +16,14 @@
|
||||
<groupId>com.github.gtache.autosubtitle</groupId>
|
||||
<artifactId>autosubtitle-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-compress</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.tukaani</groupId>
|
||||
<artifactId>xz</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -49,19 +49,24 @@ public class FFmpegVideoConverter extends AbstractProcessRunner implements Video
|
||||
|
||||
@Override
|
||||
public Video addSoftSubtitles(final Video video, final Collection<SubtitleCollection> subtitles) throws IOException {
|
||||
final var out = getTempFile("mkv"); //Soft ass subtitles are only supported by mkv apparently
|
||||
addSoftSubtitles(video, subtitles, out);
|
||||
return new FileVideoImpl(out, new VideoInfoImpl("mkv", video.info().width(), video.info().height(), video.info().duration()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSoftSubtitles(final Video video, final Collection<SubtitleCollection> subtitles, final Path path) throws IOException {
|
||||
final var videoPath = getPath(video);
|
||||
final var collectionMap = dumpCollections(subtitles);
|
||||
final var out = getTempFile("mkv"); //Soft subtitles are only supported by mkv apparently
|
||||
final var args = new ArrayList<String>();
|
||||
args.add(getFFmpegPath());
|
||||
args.add("-y");
|
||||
args.add("-i");
|
||||
args.add(videoPath.toString());
|
||||
collectionMap.forEach((c, p) -> {
|
||||
args.add("-i");
|
||||
args.add(p.toString());
|
||||
});
|
||||
args.add("-c");
|
||||
args.add("copy");
|
||||
args.add("-map");
|
||||
args.add("0:v");
|
||||
args.add("-map");
|
||||
@@ -71,30 +76,56 @@ public class FFmpegVideoConverter extends AbstractProcessRunner implements Video
|
||||
final var n = i.getAndIncrement();
|
||||
args.add("-map");
|
||||
args.add(String.valueOf(n));
|
||||
});
|
||||
args.add("-c:v");
|
||||
args.add("copy");
|
||||
args.add("-c:a");
|
||||
args.add("copy");
|
||||
final var extension = path.toString().substring(path.toString().lastIndexOf('.') + 1);
|
||||
if (extension.equals("mp4")) {
|
||||
args.add("-c:s");
|
||||
args.add("mov_text");
|
||||
} else {
|
||||
args.add("-c:s");
|
||||
args.add(subtitleConverter.formatName());
|
||||
}
|
||||
final var j = new AtomicInteger(0);
|
||||
collectionMap.forEach((c, p) -> {
|
||||
final var n = j.getAndIncrement();
|
||||
args.add("-metadata:s:s:" + n);
|
||||
args.add("language=" + c.language().iso3());
|
||||
});
|
||||
args.add(out.toString());
|
||||
run(args);
|
||||
return new FileVideoImpl(out, new VideoInfoImpl("mkv", video.info().width(), video.info().height(), video.info().duration()));
|
||||
args.add(path.toString());
|
||||
runListen(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Video addHardSubtitles(final Video video, final SubtitleCollection subtitles) throws IOException {
|
||||
final var out = getTempFile(video.info().videoFormat());
|
||||
addHardSubtitles(video, subtitles, out);
|
||||
return new FileVideoImpl(out, video.info());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addHardSubtitles(final Video video, final SubtitleCollection subtitles, final Path path) throws IOException {
|
||||
final var videoPath = getPath(video);
|
||||
final var subtitlesPath = dumpSubtitles(subtitles);
|
||||
final var out = getTempFile(video.info().videoFormat());
|
||||
final var subtitleArg = subtitleConverter.formatName().equalsIgnoreCase("ass") ? "ass=" + subtitlesPath : "subtitles=" + subtitlesPath;
|
||||
final var escapedPath = escapeVF(subtitlesPath.toString());
|
||||
final var subtitleArg = subtitleConverter.formatName().equalsIgnoreCase("ass") ? "ass='" + escapedPath + "'" : "subtitles='" + escapedPath + "'";
|
||||
final var args = List.of(
|
||||
getFFmpegPath(),
|
||||
"-i",
|
||||
videoPath.toString(),
|
||||
"-vf",
|
||||
subtitleArg,
|
||||
out.toString()
|
||||
path.toString()
|
||||
);
|
||||
run(args);
|
||||
return new FileVideoImpl(out, video.info());
|
||||
runListen(args);
|
||||
}
|
||||
|
||||
private static String escapeVF(final String path) {
|
||||
return path.replace("\\", "\\\\").replace(":", "\\:").replace("'", "'\\''")
|
||||
.replace("%", "\\%");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -104,6 +135,7 @@ public class FFmpegVideoConverter extends AbstractProcessRunner implements Video
|
||||
final var dumpVideoPath = getTempFile("." + video.info().videoFormat());
|
||||
final var args = List.of(
|
||||
getFFmpegPath(),
|
||||
"-y",
|
||||
"-i",
|
||||
videoPath.toString(),
|
||||
"-map",
|
||||
@@ -113,7 +145,7 @@ public class FFmpegVideoConverter extends AbstractProcessRunner implements Video
|
||||
"0:v",
|
||||
dumpVideoPath.toString()
|
||||
);
|
||||
run(args);
|
||||
runListen(args);
|
||||
Files.deleteIfExists(dumpVideoPath);
|
||||
return new FileAudioImpl(audioPath, new AudioInfoImpl("wav", video.info().duration()));
|
||||
}
|
||||
@@ -143,7 +175,7 @@ public class FFmpegVideoConverter extends AbstractProcessRunner implements Video
|
||||
}
|
||||
|
||||
private Path dumpSubtitles(final SubtitleCollection subtitles) throws IOException {
|
||||
final var path = getTempFile("ass");
|
||||
final var path = getTempFile("srt");
|
||||
Files.writeString(path, subtitleConverter.format(subtitles));
|
||||
return path;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.github.gtache.autosubtitle.modules.ffmpeg;
|
||||
|
||||
import javax.inject.Qualifier;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
@Qualifier
|
||||
@Documented
|
||||
@Retention(RUNTIME)
|
||||
@Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD})
|
||||
public @interface FFProbeInstallerPath {
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.github.gtache.autosubtitle.modules.ffmpeg;
|
||||
|
||||
import javax.inject.Qualifier;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
@Qualifier
|
||||
@Documented
|
||||
@Retention(RUNTIME)
|
||||
@Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD})
|
||||
public @interface FFmpegInstallerPath {
|
||||
}
|
||||
@@ -4,15 +4,23 @@ import com.github.gtache.autosubtitle.VideoConverter;
|
||||
import com.github.gtache.autosubtitle.VideoLoader;
|
||||
import com.github.gtache.autosubtitle.ffmpeg.FFmpegVideoConverter;
|
||||
import com.github.gtache.autosubtitle.ffmpeg.FFprobeVideoLoader;
|
||||
import com.github.gtache.autosubtitle.modules.setup.ffmpeg.FFmpegSetupModule;
|
||||
import dagger.Binds;
|
||||
import dagger.Module;
|
||||
|
||||
@Module
|
||||
public interface FFmpegModule {
|
||||
/**
|
||||
* Dagger module for FFmpeg
|
||||
*/
|
||||
@Module(includes = FFmpegSetupModule.class)
|
||||
public abstract class FFmpegModule {
|
||||
|
||||
private FFmpegModule() {
|
||||
|
||||
}
|
||||
|
||||
@Binds
|
||||
VideoConverter bindsVideoConverter(final FFmpegVideoConverter converter);
|
||||
abstract VideoConverter bindsVideoConverter(final FFmpegVideoConverter converter);
|
||||
|
||||
@Binds
|
||||
VideoLoader bindsVideoLoader(final FFprobeVideoLoader loader);
|
||||
abstract VideoLoader bindsVideoLoader(final FFprobeVideoLoader loader);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
package com.github.gtache.autosubtitle.modules.setup.ffmpeg;
|
||||
|
||||
import com.github.gtache.autosubtitle.impl.Architecture;
|
||||
import com.github.gtache.autosubtitle.impl.OS;
|
||||
import com.github.gtache.autosubtitle.modules.ffmpeg.FFBundledRoot;
|
||||
import com.github.gtache.autosubtitle.modules.ffmpeg.FFProbeInstallerPath;
|
||||
import com.github.gtache.autosubtitle.modules.ffmpeg.FFmpegBundledPath;
|
||||
import com.github.gtache.autosubtitle.modules.ffmpeg.FFmpegInstallerPath;
|
||||
import com.github.gtache.autosubtitle.modules.ffmpeg.FFmpegSystemPath;
|
||||
import com.github.gtache.autosubtitle.modules.ffmpeg.FFmpegVersion;
|
||||
import com.github.gtache.autosubtitle.modules.ffmpeg.FFprobeBundledPath;
|
||||
@@ -9,12 +13,18 @@ import com.github.gtache.autosubtitle.modules.ffmpeg.FFprobeSystemPath;
|
||||
import com.github.gtache.autosubtitle.modules.impl.ExecutableExtension;
|
||||
import com.github.gtache.autosubtitle.modules.setup.impl.VideoConverterSetup;
|
||||
import com.github.gtache.autosubtitle.setup.SetupManager;
|
||||
import com.github.gtache.autosubtitle.setup.ffmpeg.Decompresser;
|
||||
import com.github.gtache.autosubtitle.setup.ffmpeg.FFmpegSetupConfiguration;
|
||||
import com.github.gtache.autosubtitle.setup.ffmpeg.FFmpegSetupManager;
|
||||
import com.github.gtache.autosubtitle.setup.ffmpeg.TarDecompresser;
|
||||
import com.github.gtache.autosubtitle.setup.ffmpeg.XZDecompresser;
|
||||
import com.github.gtache.autosubtitle.setup.ffmpeg.ZipDecompresser;
|
||||
import dagger.Binds;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import dagger.multibindings.IntoMap;
|
||||
import dagger.multibindings.StringKey;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
@@ -27,47 +37,87 @@ public abstract class FFmpegSetupModule {
|
||||
private static final String FFMPEG = "ffmpeg";
|
||||
private static final String FFPROBE = "ffprobe";
|
||||
|
||||
private FFmpegSetupModule() {
|
||||
|
||||
}
|
||||
|
||||
@Binds
|
||||
@StringKey("zip")
|
||||
@IntoMap
|
||||
abstract Decompresser bindsZipDecompresser(final ZipDecompresser decompresser);
|
||||
|
||||
@Binds
|
||||
@StringKey("tar")
|
||||
@IntoMap
|
||||
abstract Decompresser bindsTarDecompresser(final TarDecompresser decompresser);
|
||||
|
||||
@Binds
|
||||
@StringKey("xz")
|
||||
@IntoMap
|
||||
abstract Decompresser bindsXzDecompresser(final XZDecompresser decompresser);
|
||||
|
||||
@Binds
|
||||
@VideoConverterSetup
|
||||
abstract SetupManager bindsFFmpegSetupManager(final FFmpegSetupManager manager);
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
static FFmpegSetupConfiguration providesFFmpegSetupConfiguration(@FFBundledRoot final Path root, @FFmpegBundledPath final Path bundledPath, @FFmpegSystemPath final Path systemPath,
|
||||
@FFmpegInstallerPath final Path ffmpegInstallerPath, @FFProbeInstallerPath final Path ffprobeInstallerPath,
|
||||
final OS os, final Architecture architecture) {
|
||||
return new FFmpegSetupConfiguration(root, bundledPath, systemPath, ffmpegInstallerPath, ffprobeInstallerPath, os, architecture);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FFmpegInstallerPath
|
||||
static Path providesFFmpegInstallerPath(@FFBundledRoot final Path root, final OS os) {
|
||||
return root.resolve("cache").resolve("ffmpeg-installer" + getInstallerExtension(os));
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FFProbeInstallerPath
|
||||
static Path providesFFProbeInstallerPath(@FFBundledRoot final Path root, final OS os) {
|
||||
return root.resolve("cache").resolve("ffprobe-installer" + getInstallerExtension(os));
|
||||
}
|
||||
|
||||
private static String getInstallerExtension(final OS os) {
|
||||
if (os == OS.LINUX) {
|
||||
return ".tar.gz";
|
||||
} else {
|
||||
return ".zip";
|
||||
}
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FFBundledRoot
|
||||
static Path providesFFBundledRoot() {
|
||||
return Paths.get("tools", FFMPEG);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@FFprobeBundledPath
|
||||
static Path providesFFProbeBundledPath(@FFBundledRoot final Path root, @ExecutableExtension final String extension) {
|
||||
return root.resolve(FFPROBE + extension);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@FFprobeSystemPath
|
||||
static Path providesFFProbeSystemPath(@ExecutableExtension final String extension) {
|
||||
return Paths.get(FFPROBE + extension);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@FFmpegBundledPath
|
||||
static Path providesFFmpegBundledPath(@FFBundledRoot final Path root, @ExecutableExtension final String extension) {
|
||||
return root.resolve(FFMPEG + extension);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@FFmpegSystemPath
|
||||
static Path providesFFmpegSystemPath(@ExecutableExtension final String extension) {
|
||||
return Paths.get(FFMPEG + extension);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@FFmpegVersion
|
||||
static String providesFFmpegVersion() {
|
||||
return "7.0.1";
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.github.gtache.autosubtitle.setup.ffmpeg;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
* Unzips files
|
||||
*/
|
||||
public interface Decompresser {
|
||||
|
||||
/**
|
||||
* Unzips an archive to the given destination
|
||||
*
|
||||
* @param archive The archive
|
||||
* @param destination The destination folder
|
||||
* @throws IOException if an error occurs
|
||||
*/
|
||||
void decompress(final Path archive, final Path destination) throws IOException;
|
||||
|
||||
/**
|
||||
* Checks whether the given file is supported by the decompresser
|
||||
*
|
||||
* @param path The file path
|
||||
* @return True if the file is supported
|
||||
*/
|
||||
boolean isPathSupported(final Path path);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.github.gtache.autosubtitle.setup.ffmpeg;
|
||||
|
||||
import com.github.gtache.autosubtitle.impl.Architecture;
|
||||
import com.github.gtache.autosubtitle.impl.OS;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Configuration for FFmpeg setup
|
||||
*/
|
||||
public record FFmpegSetupConfiguration(Path root, Path bundledFFmpegPath, Path systemFFmpegPath,
|
||||
Path ffmpegInstallerPath, Path ffprobeInstallerPath,
|
||||
OS os, Architecture architecture) {
|
||||
|
||||
public FFmpegSetupConfiguration {
|
||||
Objects.requireNonNull(root);
|
||||
Objects.requireNonNull(bundledFFmpegPath);
|
||||
Objects.requireNonNull(systemFFmpegPath);
|
||||
Objects.requireNonNull(ffmpegInstallerPath);
|
||||
Objects.requireNonNull(ffprobeInstallerPath);
|
||||
Objects.requireNonNull(os);
|
||||
Objects.requireNonNull(architecture);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,8 @@
|
||||
package com.github.gtache.autosubtitle.setup.ffmpeg;
|
||||
|
||||
import com.github.gtache.autosubtitle.impl.Architecture;
|
||||
import com.github.gtache.autosubtitle.impl.OS;
|
||||
import com.github.gtache.autosubtitle.modules.ffmpeg.FFmpegBundledPath;
|
||||
import com.github.gtache.autosubtitle.modules.ffmpeg.FFmpegSystemPath;
|
||||
import com.github.gtache.autosubtitle.modules.ffmpeg.FFmpegVersion;
|
||||
import com.github.gtache.autosubtitle.setup.SetupException;
|
||||
import com.github.gtache.autosubtitle.setup.SetupManager;
|
||||
import com.github.gtache.autosubtitle.setup.SetupStatus;
|
||||
import com.github.gtache.autosubtitle.setup.impl.AbstractSetupManager;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
@@ -13,30 +10,30 @@ import org.apache.logging.log4j.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.IOException;
|
||||
import java.net.http.HttpClient;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Objects;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.github.gtache.autosubtitle.impl.Architecture.ARMEL;
|
||||
import static com.github.gtache.autosubtitle.impl.Architecture.ARMHF;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/**
|
||||
* Manager managing the FFmpeg installation
|
||||
* {@link SetupManager} managing the FFmpeg installation
|
||||
*/
|
||||
//TODO add gpg/signature check
|
||||
public class FFmpegSetupManager extends AbstractSetupManager {
|
||||
private static final Logger logger = LogManager.getLogger(FFmpegSetupManager.class);
|
||||
private final Path bundledPath;
|
||||
private final Path systemPath;
|
||||
private final String version;
|
||||
private final OS os;
|
||||
private final Architecture architecture;
|
||||
private final String executableExtension;
|
||||
private final FFmpegSetupConfiguration configuration;
|
||||
private final Map<String, Decompresser> decompressers;
|
||||
|
||||
@Inject
|
||||
FFmpegSetupManager(@FFmpegBundledPath final Path bundledPath, @FFmpegSystemPath final Path systemPath, @FFmpegVersion final String version, final OS os, final Architecture architecture) {
|
||||
this.bundledPath = Objects.requireNonNull(bundledPath);
|
||||
this.systemPath = Objects.requireNonNull(systemPath);
|
||||
this.version = Objects.requireNonNull(version);
|
||||
this.os = Objects.requireNonNull(os);
|
||||
this.architecture = Objects.requireNonNull(architecture);
|
||||
this.executableExtension = os == OS.WINDOWS ? ".exe" : "";
|
||||
FFmpegSetupManager(final FFmpegSetupConfiguration configuration, final Map<String, Decompresser> decompressers,
|
||||
final HttpClient httpClient) {
|
||||
super(httpClient);
|
||||
this.configuration = requireNonNull(configuration);
|
||||
this.decompressers = Map.copyOf(decompressers);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -61,12 +58,128 @@ public class FFmpegSetupManager extends AbstractSetupManager {
|
||||
|
||||
@Override
|
||||
public void install() throws SetupException {
|
||||
|
||||
switch (configuration.os()) {
|
||||
case WINDOWS -> installWindows();
|
||||
case LINUX -> installLinux();
|
||||
case MAC -> installMac();
|
||||
}
|
||||
}
|
||||
|
||||
private void installWindows() throws SetupException {
|
||||
final var url = "https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-essentials.zip"; //.sha256
|
||||
downloadFFmpeg(url);
|
||||
decompressFFmpeg();
|
||||
}
|
||||
|
||||
private void downloadFFmpeg(final String url) throws SetupException {
|
||||
download(url, configuration.ffmpegInstallerPath());
|
||||
}
|
||||
|
||||
private void downloadFFProbe(final String url) throws SetupException {
|
||||
download(url, configuration.ffprobeInstallerPath());
|
||||
}
|
||||
|
||||
private void installLinux() throws SetupException {
|
||||
final var url = getLinuxUrl();
|
||||
downloadFFmpeg(url);
|
||||
decompressFFmpegLinux();
|
||||
}
|
||||
|
||||
private void decompressFFmpegLinux() throws SetupException {
|
||||
try {
|
||||
final var tmp = Files.createTempFile("ffmpeg", ".tar");
|
||||
decompress(configuration.ffmpegInstallerPath(), tmp);
|
||||
decompress(tmp, configuration.root());
|
||||
} catch (final IOException e) {
|
||||
throw new SetupException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private String getLinuxUrl() throws SetupException {
|
||||
final var architecture = configuration.architecture();
|
||||
if (architecture.isAMD64()) {
|
||||
return "https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz"; // .md5
|
||||
} else if (architecture.isARM64()) {
|
||||
return "https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-arm64-static.tar.xz";
|
||||
} else if (architecture == Architecture.I686) {
|
||||
return "https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-i686-static.tar.xz";
|
||||
} else if (architecture == ARMHF) {
|
||||
return "https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-armhf-static.tar.xz";
|
||||
} else if (architecture == ARMEL) {
|
||||
return "https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-armel-static.tar.xz";
|
||||
} else {
|
||||
throwUnsupportedOsArchitectureException();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void throwUnsupportedOsArchitectureException() throws SetupException {
|
||||
throw new SetupException("Unsupported os - architecture : " + configuration.os() + " - " + configuration.architecture());
|
||||
}
|
||||
|
||||
private void installMac() throws SetupException {
|
||||
installFFmpegMac();
|
||||
installFFprobeMac();
|
||||
}
|
||||
|
||||
private void installFFmpegMac() throws SetupException {
|
||||
final var url = getMacFFmpegUrl();
|
||||
downloadFFmpeg(url);
|
||||
decompress(configuration.ffmpegInstallerPath(), configuration.root());
|
||||
}
|
||||
|
||||
private void decompress(final Path from, final Path to) throws SetupException {
|
||||
try {
|
||||
final var filename = from.getFileName().toString();
|
||||
final var extension = filename.substring(filename.lastIndexOf('.') + 1);
|
||||
decompressers.get(extension).decompress(from, to);
|
||||
} catch (final IOException e) {
|
||||
throw new SetupException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void decompressFFmpeg() throws SetupException {
|
||||
decompress(configuration.ffmpegInstallerPath(), configuration.root());
|
||||
}
|
||||
|
||||
private void decompressFFProbe() throws SetupException {
|
||||
decompress(configuration.ffprobeInstallerPath(), configuration.root());
|
||||
}
|
||||
|
||||
private void installFFprobeMac() throws SetupException {
|
||||
final var url = getMacFFprobeUrl();
|
||||
downloadFFProbe(url);
|
||||
decompressFFProbe();
|
||||
}
|
||||
|
||||
private String getMacFFmpegUrl() throws SetupException {
|
||||
final var architecture = configuration.architecture();
|
||||
if (architecture.isAMD64()) {
|
||||
return "https://evermeet.cx/ffmpeg/getrelease/ffmpeg/zip"; // /sig
|
||||
} else if (architecture.isARM64()) {
|
||||
return "https://www.osxexperts.net/ffmpeg7arm.zip"; //no automatic sha?
|
||||
} else {
|
||||
throwUnsupportedOsArchitectureException();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String getMacFFprobeUrl() throws SetupException {
|
||||
final var architecture = configuration.architecture();
|
||||
if (architecture.isAMD64()) {
|
||||
return "https://evermeet.cx/ffmpeg/getrelease/ffprobe/zip";
|
||||
} else if (architecture.isARM64()) {
|
||||
return "https://www.osxexperts.net/ffprobe7arm.zip";
|
||||
} else {
|
||||
throwUnsupportedOsArchitectureException();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void uninstall() throws SetupException {
|
||||
|
||||
deleteFolder(configuration.root());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -75,11 +188,11 @@ public class FFmpegSetupManager extends AbstractSetupManager {
|
||||
}
|
||||
|
||||
private boolean checkSystemFFmpeg() throws IOException {
|
||||
final var result = run(systemPath.toString(), "-version");
|
||||
final var result = run(configuration.systemFFmpegPath().toString(), "-version");
|
||||
return result.exitCode() == 0;
|
||||
}
|
||||
|
||||
private boolean checkBundledFFmpeg() throws IOException {
|
||||
return Files.isRegularFile(bundledPath);
|
||||
return Files.isRegularFile(configuration.bundledFFmpegPath());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.github.gtache.autosubtitle.setup.ffmpeg;
|
||||
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
* Tar implementation of {@link Decompresser}
|
||||
*/
|
||||
public class TarDecompresser implements Decompresser {
|
||||
@Override
|
||||
public void decompress(final Path archive, final Path destination) throws IOException {
|
||||
if (!isPathSupported(archive)) {
|
||||
throw new IllegalArgumentException("Unsupported path : " + archive);
|
||||
}
|
||||
try (final var zis = new TarArchiveInputStream(Files.newInputStream(archive))) {
|
||||
var entry = zis.getNextEntry();
|
||||
while (entry != null) {
|
||||
final var newFile = newFile(destination, entry);
|
||||
if (entry.isDirectory()) {
|
||||
Files.createDirectories(newFile);
|
||||
} else {
|
||||
// fix for Windows-created archives
|
||||
final var parent = newFile.getParent();
|
||||
Files.createDirectories(parent);
|
||||
|
||||
// write file content
|
||||
try (final var fos = Files.newOutputStream(newFile)) {
|
||||
zis.transferTo(fos);
|
||||
}
|
||||
}
|
||||
entry = zis.getNextEntry();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Path newFile(final Path destinationDir, final TarArchiveEntry entry) throws IOException {
|
||||
final var destPath = destinationDir.resolve(entry.getName());
|
||||
|
||||
final var destDirPath = destinationDir.toAbsolutePath().toString();
|
||||
final var destFilePath = destPath.toAbsolutePath().toString();
|
||||
|
||||
if (!destFilePath.startsWith(destDirPath + File.separator)) {
|
||||
throw new IOException("Entry is outside of the target dir: " + entry.getName());
|
||||
}
|
||||
return destPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPathSupported(final Path path) {
|
||||
return path.getFileName().toString().endsWith(".tar");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.github.gtache.autosubtitle.setup.ffmpeg;
|
||||
|
||||
import org.tukaani.xz.XZInputStream;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
* XZ implementation of {@link Decompresser}
|
||||
*/
|
||||
public class XZDecompresser implements Decompresser {
|
||||
@Override
|
||||
public void decompress(final Path archive, final Path destination) throws IOException {
|
||||
if (!isPathSupported(archive)) {
|
||||
throw new IllegalArgumentException("Unsupported path : " + archive);
|
||||
}
|
||||
try (final var xzIn = new XZInputStream(Files.newInputStream(archive));
|
||||
final var out = Files.newOutputStream(destination)) {
|
||||
xzIn.transferTo(out);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPathSupported(final Path path) {
|
||||
return path.getFileName().toString().endsWith(".xz");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.github.gtache.autosubtitle.setup.ffmpeg;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
/**
|
||||
* Zip implementation of {@link Decompresser}
|
||||
*/
|
||||
public class ZipDecompresser implements Decompresser {
|
||||
@Override
|
||||
public void decompress(final Path archive, final Path destination) throws IOException {
|
||||
if (!isPathSupported(archive)) {
|
||||
throw new IllegalArgumentException("Unsupported path : " + archive);
|
||||
}
|
||||
try (final var zis = new ZipInputStream(Files.newInputStream(archive))) {
|
||||
var zipEntry = zis.getNextEntry();
|
||||
while (zipEntry != null) {
|
||||
final var newFile = newFile(destination, zipEntry);
|
||||
if (zipEntry.isDirectory()) {
|
||||
Files.createDirectories(newFile);
|
||||
} else {
|
||||
// fix for Windows-created archives
|
||||
final var parent = newFile.getParent();
|
||||
Files.createDirectories(parent);
|
||||
|
||||
// write file content
|
||||
try (final var fos = Files.newOutputStream(newFile)) {
|
||||
zis.transferTo(fos);
|
||||
}
|
||||
}
|
||||
zipEntry = zis.getNextEntry();
|
||||
}
|
||||
zis.closeEntry();
|
||||
}
|
||||
}
|
||||
|
||||
private static Path newFile(final Path destinationDir, final ZipEntry zipEntry) throws IOException {
|
||||
final var destPath = destinationDir.resolve(zipEntry.getName());
|
||||
|
||||
final var destDirPath = destinationDir.toAbsolutePath().toString();
|
||||
final var destFilePath = destPath.toAbsolutePath().toString();
|
||||
|
||||
if (!destFilePath.startsWith(destDirPath + File.separator)) {
|
||||
throw new IOException("Entry is outside of the target dir: " + zipEntry.getName());
|
||||
}
|
||||
return destPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPathSupported(final Path path) {
|
||||
return path.getFileName().toString().endsWith(".zip");
|
||||
}
|
||||
}
|
||||
@@ -5,11 +5,14 @@ module com.github.gtache.autosubtitle.ffmpeg {
|
||||
requires transitive com.github.gtache.autosubtitle.core;
|
||||
requires transitive dagger;
|
||||
requires transitive javax.inject;
|
||||
requires java.net.http;
|
||||
requires org.apache.logging.log4j;
|
||||
requires org.tukaani.xz;
|
||||
requires org.apache.commons.compress;
|
||||
|
||||
exports com.github.gtache.autosubtitle.ffmpeg;
|
||||
exports com.github.gtache.autosubtitle.setup.ffmpeg;
|
||||
|
||||
|
||||
exports com.github.gtache.autosubtitle.modules.ffmpeg;
|
||||
exports com.github.gtache.autosubtitle.modules.setup.ffmpeg;
|
||||
}
|
||||
Reference in New Issue
Block a user