Injects ProcessRunner to simplify testing, tests conda
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
package com.github.gtache.autosubtitle.setup.conda;
|
package com.github.gtache.autosubtitle.setup.conda;
|
||||||
|
|
||||||
import com.github.gtache.autosubtitle.impl.OS;
|
import com.github.gtache.autosubtitle.impl.OS;
|
||||||
|
import com.github.gtache.autosubtitle.process.ProcessRunner;
|
||||||
import com.github.gtache.autosubtitle.setup.SetupException;
|
import com.github.gtache.autosubtitle.setup.SetupException;
|
||||||
import com.github.gtache.autosubtitle.setup.SetupStatus;
|
import com.github.gtache.autosubtitle.setup.SetupStatus;
|
||||||
import com.github.gtache.autosubtitle.setup.impl.AbstractSetupManager;
|
import com.github.gtache.autosubtitle.setup.impl.AbstractSetupManager;
|
||||||
@@ -31,8 +32,8 @@ public class CondaSetupManager extends AbstractSetupManager {
|
|||||||
private final CondaSetupConfiguration configuration;
|
private final CondaSetupConfiguration configuration;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
CondaSetupManager(final CondaSetupConfiguration configuration, final HttpClient httpClient) {
|
CondaSetupManager(final CondaSetupConfiguration configuration, final ProcessRunner processRunner, final HttpClient httpClient) {
|
||||||
super(httpClient);
|
super(processRunner, httpClient);
|
||||||
this.configuration = requireNonNull(configuration);
|
this.configuration = requireNonNull(configuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,31 +72,28 @@ public class CondaSetupManager extends AbstractSetupManager {
|
|||||||
switch (configuration.os()) {
|
switch (configuration.os()) {
|
||||||
case OS.WINDOWS -> installWindows();
|
case OS.WINDOWS -> installWindows();
|
||||||
case OS.MAC, OS.LINUX -> installLinux();
|
case OS.MAC, OS.LINUX -> installLinux();
|
||||||
|
default -> throw new SetupException("Unsupported OS: " + configuration.os());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void installLinux() throws SetupException {
|
private void installLinux() throws SetupException {
|
||||||
try {
|
final var installerPath = configuration.condaInstallerPath();
|
||||||
final var installerPath = configuration.condaInstallerPath();
|
final var rootPath = configuration.condaRootPath();
|
||||||
final var rootPath = configuration.condaRootPath();
|
installArgs(List.of("bash", installerPath.toString(), "-b", "-p", rootPath.toString()));
|
||||||
logger.info("Installing conda using {}", installerPath);
|
|
||||||
final var result = run(List.of("bash", installerPath.toString(), "-b", "-p", rootPath.toString()), Duration.ofMinutes(15));
|
|
||||||
if (result.exitCode() == 0) {
|
|
||||||
logger.info("Installed conda to {}", rootPath);
|
|
||||||
} else {
|
|
||||||
throw new SetupException("Error installing conda: " + result);
|
|
||||||
}
|
|
||||||
} catch (final IOException e) {
|
|
||||||
throw new SetupException(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void installWindows() throws SetupException {
|
private void installWindows() throws SetupException {
|
||||||
|
final var installerPath = configuration.condaInstallerPath();
|
||||||
|
final var rootPath = configuration.condaRootPath();
|
||||||
|
installArgs(List.of(installerPath.toString(), "/InstallationType=JustMe", "/RegisterPython=0", "/S", "/D=" + rootPath));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void installArgs(final List<String> args) throws SetupException {
|
||||||
try {
|
try {
|
||||||
final var installerPath = configuration.condaInstallerPath();
|
final var installerPath = configuration.condaInstallerPath();
|
||||||
final var rootPath = configuration.condaRootPath();
|
final var rootPath = configuration.condaRootPath();
|
||||||
logger.info("Installing conda using {}", installerPath);
|
logger.info("Installing conda using {}", installerPath);
|
||||||
final var result = run(List.of(installerPath.toString(), "/InstallationType=JustMe", "/RegisterPython=0", "/S", "/D=" + rootPath.toString()), Duration.ofMinutes(15));
|
final var result = processRunner().run(args, Duration.ofMinutes(15));
|
||||||
if (result.exitCode() == 0) {
|
if (result.exitCode() == 0) {
|
||||||
logger.info("Installed conda to {}", rootPath);
|
logger.info("Installed conda to {}", rootPath);
|
||||||
} else {
|
} else {
|
||||||
@@ -111,6 +109,7 @@ public class CondaSetupManager extends AbstractSetupManager {
|
|||||||
case OS.WINDOWS -> downloadCondaWindows();
|
case OS.WINDOWS -> downloadCondaWindows();
|
||||||
case OS.MAC -> downloadCondaMac();
|
case OS.MAC -> downloadCondaMac();
|
||||||
case OS.LINUX -> downloadCondaLinux();
|
case OS.LINUX -> downloadCondaLinux();
|
||||||
|
default -> throw new SetupException("Unsupported OS: " + configuration.os());
|
||||||
}
|
}
|
||||||
logger.info("Downloaded conda to {}", configuration.condaInstallerPath());
|
logger.info("Downloaded conda to {}", configuration.condaInstallerPath());
|
||||||
}
|
}
|
||||||
@@ -154,7 +153,7 @@ public class CondaSetupManager extends AbstractSetupManager {
|
|||||||
@Override
|
@Override
|
||||||
public void update() throws SetupException {
|
public void update() throws SetupException {
|
||||||
try {
|
try {
|
||||||
final var result = run(List.of(getCondaPath().toString(), "update", "-y", "conda"), Duration.ofMinutes(15));
|
final var result = processRunner().run(List.of(getCondaPath().toString(), "update", "-y", "conda"), Duration.ofMinutes(15));
|
||||||
if (result.exitCode() == 0) {
|
if (result.exitCode() == 0) {
|
||||||
logger.info("Conda updated");
|
logger.info("Conda updated");
|
||||||
} else {
|
} else {
|
||||||
@@ -169,7 +168,7 @@ public class CondaSetupManager extends AbstractSetupManager {
|
|||||||
final var args = Stream.concat(Stream.of(getCondaPath().toString(), "create", "-y", "-p", path.toString(), "python=" + pythonVersion), Arrays.stream(packages)).toList();
|
final var args = Stream.concat(Stream.of(getCondaPath().toString(), "create", "-y", "-p", path.toString(), "python=" + pythonVersion), Arrays.stream(packages)).toList();
|
||||||
try {
|
try {
|
||||||
logger.info("Creating venv {}", path);
|
logger.info("Creating venv {}", path);
|
||||||
final var result = run(args, Duration.ofMinutes(15));
|
final var result = processRunner().run(args, Duration.ofMinutes(15));
|
||||||
if (result.exitCode() == 0) {
|
if (result.exitCode() == 0) {
|
||||||
logger.info("Created venv {}", path);
|
logger.info("Created venv {}", path);
|
||||||
} else {
|
} else {
|
||||||
@@ -181,6 +180,7 @@ public class CondaSetupManager extends AbstractSetupManager {
|
|||||||
try (final var files = Files.find(sourceFolder, 1, (p, a) -> p.getFileName().toString().contains("libcrypto") || p.getFileName().toString().contains("libssl"))) {
|
try (final var files = Files.find(sourceFolder, 1, (p, a) -> p.getFileName().toString().contains("libcrypto") || p.getFileName().toString().contains("libssl"))) {
|
||||||
final var fileList = files.toList();
|
final var fileList = files.toList();
|
||||||
final var targetFolder = path.resolve("DLLs");
|
final var targetFolder = path.resolve("DLLs");
|
||||||
|
Files.createDirectories(targetFolder);
|
||||||
for (final var s : fileList) {
|
for (final var s : fileList) {
|
||||||
Files.copy(s, targetFolder.resolve(s.getFileName()));
|
Files.copy(s, targetFolder.resolve(s.getFileName()));
|
||||||
}
|
}
|
||||||
@@ -202,7 +202,7 @@ public class CondaSetupManager extends AbstractSetupManager {
|
|||||||
|
|
||||||
private boolean isSystemCondaInstalled() throws SetupException {
|
private boolean isSystemCondaInstalled() throws SetupException {
|
||||||
try {
|
try {
|
||||||
final var result = run(List.of(configuration.condaSystemPath().toString(), "--version"), Duration.ofSeconds(5));
|
final var result = processRunner().run(List.of(configuration.condaSystemPath().toString(), "--version"), Duration.ofSeconds(5));
|
||||||
if (result.exitCode() == 0) {
|
if (result.exitCode() == 0) {
|
||||||
final var output = result.output().getFirst();
|
final var output = result.output().getFirst();
|
||||||
final var versionString = output.substring(output.indexOf(' ') + 1);
|
final var versionString = output.substring(output.indexOf(' ') + 1);
|
||||||
@@ -212,7 +212,7 @@ public class CondaSetupManager extends AbstractSetupManager {
|
|||||||
final var minor = Integer.parseInt(version[1]);
|
final var minor = Integer.parseInt(version[1]);
|
||||||
return major >= configuration.condaMinimumMajorVersion() && minor >= configuration.condaMinimumMinorVersion();
|
return major >= configuration.condaMinimumMajorVersion() && minor >= configuration.condaMinimumMinorVersion();
|
||||||
} else {
|
} else {
|
||||||
throw new SetupException("Unexpected python version: " + versionString);
|
throw new SetupException("Unexpected conda version: " + versionString);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
package com.github.gtache.autosubtitle.modules.setup.conda;
|
||||||
|
|
||||||
|
import com.github.gtache.autosubtitle.impl.Architecture;
|
||||||
|
import com.github.gtache.autosubtitle.impl.OS;
|
||||||
|
import com.github.gtache.autosubtitle.setup.conda.CondaSetupConfiguration;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
class TestCondaSetupModule {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCondaSetupConfiguration() {
|
||||||
|
final var rootPath = Paths.get("root");
|
||||||
|
final var systemPath = Paths.get("system");
|
||||||
|
final var bundledPath = Paths.get("bundled");
|
||||||
|
final var condaMinimumMajorVersion = 24;
|
||||||
|
final var condaMinimumMinorVersion = 5;
|
||||||
|
final var condaInstallerPath = Paths.get("installer");
|
||||||
|
final var os = OS.WINDOWS;
|
||||||
|
final var architecture = Architecture.X86_64;
|
||||||
|
final var expected = new CondaSetupConfiguration(rootPath, systemPath, bundledPath, condaMinimumMajorVersion, condaMinimumMinorVersion, condaInstallerPath, os, architecture);
|
||||||
|
assertEquals(expected, CondaSetupModule.providesCondaSetupConfiguration(systemPath, bundledPath, condaMinimumMajorVersion, condaMinimumMinorVersion, condaInstallerPath, rootPath, os, architecture));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testProvidesCondaSystemPath() {
|
||||||
|
assertEquals(Paths.get("conda.bat"), CondaSetupModule.providesCondaSystemPath(OS.WINDOWS));
|
||||||
|
assertEquals(Paths.get("conda"), CondaSetupModule.providesCondaSystemPath(OS.LINUX));
|
||||||
|
assertEquals(Paths.get("conda"), CondaSetupModule.providesCondaSystemPath(OS.MAC));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCondaBundledPath() {
|
||||||
|
final var root = Paths.get("root");
|
||||||
|
final var expectedWindows = root.resolve("condabin").resolve(Paths.get("conda.bat"));
|
||||||
|
final var expectedOther = root.resolve("condabin").resolve(Paths.get("conda"));
|
||||||
|
assertEquals(expectedWindows, CondaSetupModule.providesCondaBundledPath(root, OS.WINDOWS));
|
||||||
|
assertEquals(expectedOther, CondaSetupModule.providesCondaBundledPath(root, OS.MAC));
|
||||||
|
assertEquals(expectedOther, CondaSetupModule.providesCondaBundledPath(root, OS.LINUX));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCondaMinimumMajorVersion() {
|
||||||
|
assertEquals(24, CondaSetupModule.providesCondaMinimumMajorVersion());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCondaMinimumMinorVersion() {
|
||||||
|
assertEquals(5, CondaSetupModule.providesCondaMinimumMinorVersion());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCondaRootPath() {
|
||||||
|
final var root = Paths.get("root");
|
||||||
|
final var expected = root.resolve("miniconda3");
|
||||||
|
assertEquals(expected, CondaSetupModule.providesCondaRootPath(root));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCondaInstallerPath() {
|
||||||
|
final var root = Paths.get("root");
|
||||||
|
final var expectedWindows = root.resolve("conda-install.exe");
|
||||||
|
final var expectedOther = root.resolve("conda-install.sh");
|
||||||
|
assertEquals(expectedWindows, CondaSetupModule.providesCondaInstallerPath(root, OS.WINDOWS));
|
||||||
|
assertEquals(expectedOther, CondaSetupModule.providesCondaInstallerPath(root, OS.MAC));
|
||||||
|
assertEquals(expectedOther, CondaSetupModule.providesCondaInstallerPath(root, OS.LINUX));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
package com.github.gtache.autosubtitle.setup.conda;
|
||||||
|
|
||||||
|
import com.github.gtache.autosubtitle.impl.Architecture;
|
||||||
|
import com.github.gtache.autosubtitle.impl.OS;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class TestCondaSetupConfiguration {
|
||||||
|
|
||||||
|
private final Path condaRootPath;
|
||||||
|
private final Path condaSystemPath;
|
||||||
|
private final Path condaBundledPath;
|
||||||
|
private final int condaMinimumMajorVersion;
|
||||||
|
private final int condaMinimumMinorVersion;
|
||||||
|
private final Path condaInstallerPath;
|
||||||
|
private final OS os;
|
||||||
|
private final Architecture architecture;
|
||||||
|
|
||||||
|
TestCondaSetupConfiguration(@Mock final Path condaRootPath, @Mock final Path condaSystemPath,
|
||||||
|
@Mock final Path condaBundledPath, @Mock final Path condaInstallerPath,
|
||||||
|
@Mock final OS os, @Mock final Architecture architecture) {
|
||||||
|
this.condaRootPath = Objects.requireNonNull(condaRootPath);
|
||||||
|
this.condaSystemPath = Objects.requireNonNull(condaSystemPath);
|
||||||
|
this.condaBundledPath = Objects.requireNonNull(condaBundledPath);
|
||||||
|
this.condaMinimumMajorVersion = 3;
|
||||||
|
this.condaMinimumMinorVersion = 4;
|
||||||
|
this.condaInstallerPath = Objects.requireNonNull(condaInstallerPath);
|
||||||
|
this.os = Objects.requireNonNull(os);
|
||||||
|
this.architecture = Objects.requireNonNull(architecture);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCondaSetupConfiguration() {
|
||||||
|
final var configuration = new CondaSetupConfiguration(condaRootPath, condaSystemPath, condaBundledPath, condaMinimumMajorVersion, condaMinimumMinorVersion, condaInstallerPath, os, architecture);
|
||||||
|
assertEquals(condaRootPath, configuration.condaRootPath());
|
||||||
|
assertEquals(condaSystemPath, configuration.condaSystemPath());
|
||||||
|
assertEquals(condaBundledPath, configuration.condaBundledPath());
|
||||||
|
assertEquals(condaMinimumMajorVersion, configuration.condaMinimumMajorVersion());
|
||||||
|
assertEquals(condaMinimumMinorVersion, configuration.condaMinimumMinorVersion());
|
||||||
|
assertEquals(condaInstallerPath, configuration.condaInstallerPath());
|
||||||
|
assertEquals(os, configuration.os());
|
||||||
|
assertEquals(architecture, configuration.architecture());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testIllegal() {
|
||||||
|
assertThrows(NullPointerException.class, () -> new CondaSetupConfiguration(null, condaSystemPath, condaBundledPath, condaMinimumMajorVersion, condaMinimumMinorVersion, condaInstallerPath, os, architecture));
|
||||||
|
assertThrows(NullPointerException.class, () -> new CondaSetupConfiguration(condaRootPath, null, condaBundledPath, condaMinimumMajorVersion, condaMinimumMinorVersion, condaInstallerPath, os, architecture));
|
||||||
|
assertThrows(NullPointerException.class, () -> new CondaSetupConfiguration(condaRootPath, condaSystemPath, null, condaMinimumMajorVersion, condaMinimumMinorVersion, condaInstallerPath, os, architecture));
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> new CondaSetupConfiguration(condaRootPath, condaSystemPath, condaBundledPath, 0, condaMinimumMinorVersion, condaInstallerPath, os, architecture));
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> new CondaSetupConfiguration(condaRootPath, condaSystemPath, condaBundledPath, condaMinimumMajorVersion, -1, condaInstallerPath, os, architecture));
|
||||||
|
assertThrows(NullPointerException.class, () -> new CondaSetupConfiguration(condaRootPath, condaSystemPath, condaBundledPath, condaMinimumMajorVersion, condaMinimumMinorVersion, null, os, architecture));
|
||||||
|
assertThrows(NullPointerException.class, () -> new CondaSetupConfiguration(condaRootPath, condaSystemPath, condaBundledPath, condaMinimumMajorVersion, condaMinimumMinorVersion, condaInstallerPath, null, architecture));
|
||||||
|
assertThrows(NullPointerException.class, () -> new CondaSetupConfiguration(condaRootPath, condaSystemPath, condaBundledPath, condaMinimumMajorVersion, condaMinimumMinorVersion, condaInstallerPath, os, null));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,513 @@
|
|||||||
|
package com.github.gtache.autosubtitle.setup.conda;
|
||||||
|
|
||||||
|
import com.github.gtache.autosubtitle.impl.Architecture;
|
||||||
|
import com.github.gtache.autosubtitle.impl.OS;
|
||||||
|
import com.github.gtache.autosubtitle.process.ProcessResult;
|
||||||
|
import com.github.gtache.autosubtitle.process.ProcessRunner;
|
||||||
|
import com.github.gtache.autosubtitle.setup.SetupException;
|
||||||
|
import com.github.gtache.autosubtitle.setup.SetupStatus;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.junit.jupiter.api.io.TempDir;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.http.HttpClient;
|
||||||
|
import java.net.http.HttpRequest;
|
||||||
|
import java.net.http.HttpResponse;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static java.util.Objects.requireNonNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class TestCondaSetupManager {
|
||||||
|
private static final String PYTHON_VERSION = "3.3";
|
||||||
|
private static final String[] PACKAGES = {"p1", "p2"};
|
||||||
|
|
||||||
|
|
||||||
|
private final CondaSetupConfiguration configuration;
|
||||||
|
private final ProcessRunner processRunner;
|
||||||
|
private final HttpClient httpClient;
|
||||||
|
private final CondaSetupManager condaSetupManager;
|
||||||
|
private final ProcessResult systemProcessResult;
|
||||||
|
private final Path systemPath;
|
||||||
|
private final HttpResponse<Path> response;
|
||||||
|
|
||||||
|
TestCondaSetupManager(@Mock final CondaSetupConfiguration configuration, @Mock final ProcessRunner processRunner,
|
||||||
|
@Mock final HttpClient httpClient, @Mock final ProcessResult systemProcessResult, @Mock final HttpResponse<Path> response) throws IOException, InterruptedException {
|
||||||
|
this.configuration = requireNonNull(configuration);
|
||||||
|
this.processRunner = requireNonNull(processRunner);
|
||||||
|
this.httpClient = requireNonNull(httpClient);
|
||||||
|
this.condaSetupManager = new CondaSetupManager(configuration, processRunner, httpClient);
|
||||||
|
this.systemProcessResult = requireNonNull(systemProcessResult);
|
||||||
|
this.systemPath = Paths.get("system");
|
||||||
|
this.response = requireNonNull(response);
|
||||||
|
when(response.statusCode()).thenReturn(200);
|
||||||
|
when(configuration.condaSystemPath()).thenReturn(systemPath);
|
||||||
|
when(systemProcessResult.output()).thenReturn(List.of("conda 99.99.99"));
|
||||||
|
when(systemProcessResult.exitCode()).thenReturn(0);
|
||||||
|
when(processRunner.run(List.of(systemPath.toString(), "--version"), Duration.ofSeconds(5))).thenReturn(systemProcessResult);
|
||||||
|
when(httpClient.send(any(HttpRequest.class), any(HttpResponse.BodyHandler.class))).thenReturn(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testName() {
|
||||||
|
assertEquals("conda", condaSetupManager.name());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGetStatusIsSystemInstalled() throws SetupException {
|
||||||
|
assertEquals(SetupStatus.SYSTEM_INSTALLED, condaSetupManager.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGetStatusIsBundledInstalled(@TempDir final Path tempDir) throws SetupException, IOException {
|
||||||
|
final var path = tempDir.resolve("conda");
|
||||||
|
Files.createFile(path);
|
||||||
|
when(configuration.condaBundledPath()).thenReturn(path);
|
||||||
|
when(systemProcessResult.exitCode()).thenReturn(1);
|
||||||
|
|
||||||
|
assertEquals(SetupStatus.BUNDLE_INSTALLED, condaSetupManager.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGetStatusNotInstalled() throws SetupException {
|
||||||
|
final var path = Paths.get("conda");
|
||||||
|
when(configuration.condaBundledPath()).thenReturn(path);
|
||||||
|
when(systemProcessResult.exitCode()).thenReturn(1);
|
||||||
|
assertEquals(SetupStatus.NOT_INSTALLED, condaSetupManager.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testInstallIsInstalledSystem() {
|
||||||
|
assertDoesNotThrow(condaSetupManager::install);
|
||||||
|
verifyNoInteractions(httpClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testInstallIsInstalledBundled(@TempDir final Path tempDir) throws IOException {
|
||||||
|
final var path = tempDir.resolve("conda");
|
||||||
|
Files.createFile(path);
|
||||||
|
when(configuration.condaBundledPath()).thenReturn(path);
|
||||||
|
when(systemProcessResult.exitCode()).thenReturn(1);
|
||||||
|
assertDoesNotThrow(condaSetupManager::install);
|
||||||
|
verifyNoInteractions(httpClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testInstallDownloadWindows(@TempDir final Path tempDir) throws IOException, InterruptedException {
|
||||||
|
final var installerPath = tempDir.resolve("conda");
|
||||||
|
when(configuration.condaInstallerPath()).thenReturn(installerPath);
|
||||||
|
when(configuration.condaBundledPath()).thenReturn(tempDir);
|
||||||
|
when(configuration.os()).thenReturn(OS.WINDOWS);
|
||||||
|
when(systemProcessResult.exitCode()).thenReturn(1);
|
||||||
|
when(configuration.condaRootPath()).thenReturn(tempDir);
|
||||||
|
|
||||||
|
final var args = List.of(installerPath.toString(), "/InstallationType=JustMe", "/RegisterPython=0", "/S", "/D=" + tempDir);
|
||||||
|
final var result = mock(ProcessResult.class);
|
||||||
|
when(result.exitCode()).thenReturn(0);
|
||||||
|
when(processRunner.run(args, Duration.ofMinutes(15))).thenReturn(result);
|
||||||
|
|
||||||
|
assertDoesNotThrow(condaSetupManager::install);
|
||||||
|
final var requestCapture = ArgumentCaptor.forClass(HttpRequest.class);
|
||||||
|
verify(httpClient).send(requestCapture.capture(), any(HttpResponse.BodyHandler.class));
|
||||||
|
final var request = requestCapture.getValue();
|
||||||
|
assertEquals("https://repo.anaconda.com/miniconda/Miniconda3-latest-Windows-x86_64.exe", request.uri().toString());
|
||||||
|
verify(processRunner).run(args, Duration.ofMinutes(15));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testInstallDownloadLinuxAMD64(@TempDir final Path tempDir) throws IOException, InterruptedException {
|
||||||
|
final var installerPath = tempDir.resolve("conda");
|
||||||
|
when(configuration.condaInstallerPath()).thenReturn(installerPath);
|
||||||
|
when(configuration.condaBundledPath()).thenReturn(tempDir);
|
||||||
|
when(configuration.os()).thenReturn(OS.LINUX);
|
||||||
|
when(configuration.architecture()).thenReturn(Architecture.AMD64);
|
||||||
|
when(systemProcessResult.exitCode()).thenReturn(1);
|
||||||
|
when(configuration.condaRootPath()).thenReturn(tempDir);
|
||||||
|
|
||||||
|
final var args = List.of("bash", installerPath.toString(), "-b", "-p", tempDir.toString());
|
||||||
|
final var result = mock(ProcessResult.class);
|
||||||
|
when(result.exitCode()).thenReturn(0);
|
||||||
|
when(processRunner.run(args, Duration.ofMinutes(15))).thenReturn(result);
|
||||||
|
|
||||||
|
assertDoesNotThrow(condaSetupManager::install);
|
||||||
|
final var requestCapture = ArgumentCaptor.forClass(HttpRequest.class);
|
||||||
|
verify(httpClient).send(requestCapture.capture(), any(HttpResponse.BodyHandler.class));
|
||||||
|
final var request = requestCapture.getValue();
|
||||||
|
assertEquals("https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh", request.uri().toString());
|
||||||
|
verify(processRunner).run(args, Duration.ofMinutes(15));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testInstallDownloadLinuxARM64(@TempDir final Path tempDir) throws IOException, InterruptedException {
|
||||||
|
final var installerPath = tempDir.resolve("conda");
|
||||||
|
when(configuration.condaInstallerPath()).thenReturn(installerPath);
|
||||||
|
when(configuration.condaBundledPath()).thenReturn(tempDir);
|
||||||
|
when(configuration.os()).thenReturn(OS.LINUX);
|
||||||
|
when(configuration.architecture()).thenReturn(Architecture.ARM64);
|
||||||
|
when(systemProcessResult.exitCode()).thenReturn(1);
|
||||||
|
when(configuration.condaRootPath()).thenReturn(tempDir);
|
||||||
|
|
||||||
|
final var args = List.of("bash", installerPath.toString(), "-b", "-p", tempDir.toString());
|
||||||
|
final var result = mock(ProcessResult.class);
|
||||||
|
when(result.exitCode()).thenReturn(0);
|
||||||
|
when(processRunner.run(args, Duration.ofMinutes(15))).thenReturn(result);
|
||||||
|
|
||||||
|
assertDoesNotThrow(condaSetupManager::install);
|
||||||
|
final var requestCapture = ArgumentCaptor.forClass(HttpRequest.class);
|
||||||
|
verify(httpClient).send(requestCapture.capture(), any(HttpResponse.BodyHandler.class));
|
||||||
|
final var request = requestCapture.getValue();
|
||||||
|
assertEquals("https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-aarch64.sh", request.uri().toString());
|
||||||
|
verify(processRunner).run(args, Duration.ofMinutes(15));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testInstallDownloadLinuxUnsupported(@TempDir final Path tempDir) throws IOException {
|
||||||
|
final var installerPath = tempDir.resolve("conda");
|
||||||
|
when(configuration.condaInstallerPath()).thenReturn(installerPath);
|
||||||
|
when(configuration.condaBundledPath()).thenReturn(tempDir);
|
||||||
|
when(configuration.os()).thenReturn(OS.LINUX);
|
||||||
|
when(configuration.architecture()).thenReturn(Architecture.ARM32);
|
||||||
|
when(systemProcessResult.exitCode()).thenReturn(1);
|
||||||
|
when(configuration.condaRootPath()).thenReturn(tempDir);
|
||||||
|
|
||||||
|
assertThrows(SetupException.class, condaSetupManager::install);
|
||||||
|
verifyNoInteractions(httpClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testInstallDownloadMacAMD64(@TempDir final Path tempDir) throws IOException, InterruptedException {
|
||||||
|
final var installerPath = tempDir.resolve("conda");
|
||||||
|
when(configuration.condaInstallerPath()).thenReturn(installerPath);
|
||||||
|
when(configuration.condaBundledPath()).thenReturn(tempDir);
|
||||||
|
when(configuration.os()).thenReturn(OS.MAC);
|
||||||
|
when(configuration.architecture()).thenReturn(Architecture.AMD64);
|
||||||
|
when(systemProcessResult.exitCode()).thenReturn(1);
|
||||||
|
when(configuration.condaRootPath()).thenReturn(tempDir);
|
||||||
|
|
||||||
|
final var args = List.of("bash", installerPath.toString(), "-b", "-p", tempDir.toString());
|
||||||
|
final var result = mock(ProcessResult.class);
|
||||||
|
when(result.exitCode()).thenReturn(0);
|
||||||
|
when(processRunner.run(args, Duration.ofMinutes(15))).thenReturn(result);
|
||||||
|
|
||||||
|
assertDoesNotThrow(condaSetupManager::install);
|
||||||
|
final var requestCapture = ArgumentCaptor.forClass(HttpRequest.class);
|
||||||
|
verify(httpClient).send(requestCapture.capture(), any(HttpResponse.BodyHandler.class));
|
||||||
|
final var request = requestCapture.getValue();
|
||||||
|
assertEquals("https://repo.anaconda.com/miniconda/Miniconda3-latest-MacOSX-x86_64.sh", request.uri().toString());
|
||||||
|
verify(processRunner).run(args, Duration.ofMinutes(15));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testInstallDownloadMacARM64(@TempDir final Path tempDir) throws IOException, InterruptedException {
|
||||||
|
final var installerPath = tempDir.resolve("conda");
|
||||||
|
when(configuration.condaInstallerPath()).thenReturn(installerPath);
|
||||||
|
when(configuration.condaBundledPath()).thenReturn(tempDir);
|
||||||
|
when(configuration.os()).thenReturn(OS.MAC);
|
||||||
|
when(configuration.architecture()).thenReturn(Architecture.ARM64);
|
||||||
|
when(systemProcessResult.exitCode()).thenReturn(1);
|
||||||
|
when(configuration.condaRootPath()).thenReturn(tempDir);
|
||||||
|
|
||||||
|
final var args = List.of("bash", installerPath.toString(), "-b", "-p", tempDir.toString());
|
||||||
|
final var result = mock(ProcessResult.class);
|
||||||
|
when(result.exitCode()).thenReturn(0);
|
||||||
|
when(processRunner.run(args, Duration.ofMinutes(15))).thenReturn(result);
|
||||||
|
|
||||||
|
assertDoesNotThrow(condaSetupManager::install);
|
||||||
|
final var requestCapture = ArgumentCaptor.forClass(HttpRequest.class);
|
||||||
|
verify(httpClient).send(requestCapture.capture(), any(HttpResponse.BodyHandler.class));
|
||||||
|
final var request = requestCapture.getValue();
|
||||||
|
assertEquals("https://repo.anaconda.com/miniconda/Miniconda3-latest-MacOSX-arm64.sh", request.uri().toString());
|
||||||
|
verify(processRunner).run(args, Duration.ofMinutes(15));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testInstallDownloadMacUnsupported(@TempDir final Path tempDir) throws IOException {
|
||||||
|
final var installerPath = tempDir.resolve("conda");
|
||||||
|
when(configuration.condaInstallerPath()).thenReturn(installerPath);
|
||||||
|
when(configuration.condaBundledPath()).thenReturn(tempDir);
|
||||||
|
when(configuration.os()).thenReturn(OS.MAC);
|
||||||
|
when(configuration.architecture()).thenReturn(Architecture.ARM32);
|
||||||
|
when(systemProcessResult.exitCode()).thenReturn(1);
|
||||||
|
when(configuration.condaRootPath()).thenReturn(tempDir);
|
||||||
|
|
||||||
|
assertThrows(SetupException.class, condaSetupManager::install);
|
||||||
|
verifyNoInteractions(httpClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testInstallDownloadUnsupported(@TempDir final Path tempDir) throws IOException {
|
||||||
|
final var installerPath = tempDir.resolve("conda");
|
||||||
|
when(configuration.condaInstallerPath()).thenReturn(installerPath);
|
||||||
|
when(configuration.condaBundledPath()).thenReturn(tempDir);
|
||||||
|
when(configuration.os()).thenReturn(OS.UNKNOWN);
|
||||||
|
when(systemProcessResult.exitCode()).thenReturn(1);
|
||||||
|
when(configuration.condaRootPath()).thenReturn(tempDir);
|
||||||
|
|
||||||
|
assertThrows(SetupException.class, condaSetupManager::install);
|
||||||
|
verifyNoInteractions(httpClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testInstallInstallerWindows(@TempDir final Path tempDir) throws IOException {
|
||||||
|
final var installerPath = tempDir.resolve("conda");
|
||||||
|
Files.createFile(installerPath);
|
||||||
|
when(configuration.condaInstallerPath()).thenReturn(installerPath);
|
||||||
|
when(configuration.condaBundledPath()).thenReturn(tempDir);
|
||||||
|
when(configuration.os()).thenReturn(OS.WINDOWS);
|
||||||
|
when(systemProcessResult.exitCode()).thenReturn(1);
|
||||||
|
when(configuration.condaRootPath()).thenReturn(tempDir);
|
||||||
|
|
||||||
|
final var args = List.of(installerPath.toString(), "/InstallationType=JustMe", "/RegisterPython=0", "/S", "/D=" + tempDir.toString());
|
||||||
|
final var result = mock(ProcessResult.class);
|
||||||
|
when(result.exitCode()).thenReturn(0);
|
||||||
|
when(processRunner.run(args, Duration.ofMinutes(15))).thenReturn(result);
|
||||||
|
|
||||||
|
assertDoesNotThrow(condaSetupManager::install);
|
||||||
|
verify(processRunner).run(args, Duration.ofMinutes(15));
|
||||||
|
verifyNoInteractions(httpClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testInstallInstallerLinux(@TempDir final Path tempDir) throws IOException {
|
||||||
|
final var installerPath = tempDir.resolve("conda");
|
||||||
|
Files.createFile(installerPath);
|
||||||
|
when(configuration.condaInstallerPath()).thenReturn(installerPath);
|
||||||
|
when(configuration.condaBundledPath()).thenReturn(tempDir);
|
||||||
|
when(configuration.os()).thenReturn(OS.LINUX);
|
||||||
|
when(systemProcessResult.exitCode()).thenReturn(1);
|
||||||
|
when(configuration.condaRootPath()).thenReturn(tempDir);
|
||||||
|
|
||||||
|
final var args = List.of("bash", installerPath.toString(), "-b", "-p", tempDir.toString());
|
||||||
|
final var result = mock(ProcessResult.class);
|
||||||
|
when(result.exitCode()).thenReturn(0);
|
||||||
|
when(processRunner.run(args, Duration.ofMinutes(15))).thenReturn(result);
|
||||||
|
|
||||||
|
assertDoesNotThrow(condaSetupManager::install);
|
||||||
|
verify(processRunner).run(args, Duration.ofMinutes(15));
|
||||||
|
verifyNoInteractions(httpClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testInstallInstallerLinuxIOException(@TempDir final Path tempDir) throws IOException {
|
||||||
|
final var installerPath = tempDir.resolve("conda");
|
||||||
|
Files.createFile(installerPath);
|
||||||
|
when(configuration.condaInstallerPath()).thenReturn(installerPath);
|
||||||
|
when(configuration.condaBundledPath()).thenReturn(tempDir);
|
||||||
|
when(configuration.os()).thenReturn(OS.LINUX);
|
||||||
|
when(systemProcessResult.exitCode()).thenReturn(1);
|
||||||
|
when(configuration.condaRootPath()).thenReturn(tempDir);
|
||||||
|
|
||||||
|
final var args = List.of("bash", installerPath.toString(), "-b", "-p", tempDir.toString());
|
||||||
|
when(processRunner.run(args, Duration.ofMinutes(15))).thenThrow(IOException.class);
|
||||||
|
|
||||||
|
assertThrows(SetupException.class, condaSetupManager::install);
|
||||||
|
verify(processRunner).run(args, Duration.ofMinutes(15));
|
||||||
|
verifyNoInteractions(httpClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testInstallInstallerLinuxBadExitCode(@TempDir final Path tempDir) throws IOException {
|
||||||
|
final var installerPath = tempDir.resolve("conda");
|
||||||
|
Files.createFile(installerPath);
|
||||||
|
when(configuration.condaInstallerPath()).thenReturn(installerPath);
|
||||||
|
when(configuration.condaBundledPath()).thenReturn(tempDir);
|
||||||
|
when(configuration.os()).thenReturn(OS.LINUX);
|
||||||
|
when(systemProcessResult.exitCode()).thenReturn(1);
|
||||||
|
when(configuration.condaRootPath()).thenReturn(tempDir);
|
||||||
|
|
||||||
|
final var args = List.of("bash", installerPath.toString(), "-b", "-p", tempDir.toString());
|
||||||
|
final var result = mock(ProcessResult.class);
|
||||||
|
when(result.exitCode()).thenReturn(1);
|
||||||
|
when(processRunner.run(args, Duration.ofMinutes(15))).thenReturn(result);
|
||||||
|
|
||||||
|
assertThrows(SetupException.class, condaSetupManager::install);
|
||||||
|
verify(processRunner).run(args, Duration.ofMinutes(15));
|
||||||
|
verifyNoInteractions(httpClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testInstallInstallerUnknown(@TempDir final Path tempDir) throws IOException {
|
||||||
|
final var installerPath = tempDir.resolve("conda");
|
||||||
|
Files.createFile(installerPath);
|
||||||
|
when(configuration.condaInstallerPath()).thenReturn(installerPath);
|
||||||
|
when(configuration.condaBundledPath()).thenReturn(tempDir);
|
||||||
|
when(configuration.os()).thenReturn(OS.UNKNOWN);
|
||||||
|
when(systemProcessResult.exitCode()).thenReturn(1);
|
||||||
|
|
||||||
|
assertThrows(SetupException.class, condaSetupManager::install);
|
||||||
|
verifyNoInteractions(httpClient);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testUninstall(@TempDir final Path tempDir) throws IOException {
|
||||||
|
when(configuration.condaRootPath()).thenReturn(tempDir);
|
||||||
|
Files.createDirectories(tempDir.resolve("sub"));
|
||||||
|
Files.createFile(tempDir.resolve("sub").resolve("conda"));
|
||||||
|
Files.createFile(tempDir.resolve("python"));
|
||||||
|
assertDoesNotThrow(condaSetupManager::uninstall);
|
||||||
|
assertFalse(Files.isDirectory(tempDir));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testUpdateBundled() throws IOException {
|
||||||
|
final var path = Path.of("test");
|
||||||
|
final var args = List.of(path.toString(), "update", "-y", "conda");
|
||||||
|
final var duration = Duration.ofMinutes(15);
|
||||||
|
when(configuration.condaBundledPath()).thenReturn(path);
|
||||||
|
when(systemProcessResult.exitCode()).thenReturn(1);
|
||||||
|
final var processResult = mock(ProcessResult.class);
|
||||||
|
when(processResult.exitCode()).thenReturn(0);
|
||||||
|
when(processRunner.run(args, duration)).thenReturn(processResult);
|
||||||
|
assertDoesNotThrow(condaSetupManager::update);
|
||||||
|
verify(processRunner).run(args, duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testUpdateSystemInstalled() throws IOException {
|
||||||
|
final var args = List.of(systemPath.toString(), "update", "-y", "conda");
|
||||||
|
final var duration = Duration.ofMinutes(15);
|
||||||
|
final var processResult = mock(ProcessResult.class);
|
||||||
|
when(processResult.exitCode()).thenReturn(0);
|
||||||
|
when(processRunner.run(args, duration)).thenReturn(processResult);
|
||||||
|
assertDoesNotThrow(condaSetupManager::update);
|
||||||
|
verify(processRunner).run(args, duration);
|
||||||
|
verify(processRunner).run(List.of(systemPath.toString(), "--version"), Duration.ofSeconds(5));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testUpdateSystemIOException() throws IOException {
|
||||||
|
when(processRunner.run(List.of(systemPath.toString(), "--version"), Duration.ofSeconds(5))).thenThrow(IOException.class);
|
||||||
|
assertThrows(SetupException.class, condaSetupManager::update);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testUpdateSystemBadVersion() {
|
||||||
|
final var path = Path.of("test");
|
||||||
|
when(systemProcessResult.output()).thenReturn(List.of("conda 9"));
|
||||||
|
when(configuration.condaBundledPath()).thenReturn(path);
|
||||||
|
when(configuration.condaSystemPath()).thenReturn(systemPath);
|
||||||
|
assertThrows(SetupException.class, condaSetupManager::update);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testUpdateBundleBadResultCode() throws IOException {
|
||||||
|
final var path = Path.of("test");
|
||||||
|
final var args = List.of(path.toString(), "update", "-y", "conda");
|
||||||
|
final var duration = Duration.ofMinutes(15);
|
||||||
|
when(systemProcessResult.exitCode()).thenReturn(1);
|
||||||
|
when(configuration.condaBundledPath()).thenReturn(path);
|
||||||
|
when(processRunner.run(args, duration)).thenReturn(systemProcessResult);
|
||||||
|
when(systemProcessResult.exitCode()).thenReturn(1);
|
||||||
|
assertThrows(SetupException.class, condaSetupManager::update);
|
||||||
|
verify(processRunner).run(args, duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testUpdateBundleIOException() throws IOException {
|
||||||
|
final var path = Path.of("test");
|
||||||
|
final var args = List.of(path.toString(), "update", "-y", "conda");
|
||||||
|
final var duration = Duration.ofMinutes(15);
|
||||||
|
when(systemProcessResult.exitCode()).thenReturn(1);
|
||||||
|
when(configuration.condaBundledPath()).thenReturn(path);
|
||||||
|
when(processRunner.run(args, duration)).thenThrow(IOException.class);
|
||||||
|
assertThrows(SetupException.class, condaSetupManager::update);
|
||||||
|
verify(processRunner).run(args, duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCreateVenv() throws IOException {
|
||||||
|
final var path = Paths.get("test");
|
||||||
|
final var args = List.of(systemPath.toString(), "create", "-y", "-p", path.toString(), "python=" + PYTHON_VERSION, "p1", "p2");
|
||||||
|
final var result = mock(ProcessResult.class);
|
||||||
|
when(result.exitCode()).thenReturn(0);
|
||||||
|
when(configuration.os()).thenReturn(OS.LINUX);
|
||||||
|
when(processRunner.run(args, Duration.ofMinutes(15))).thenReturn(result);
|
||||||
|
assertDoesNotThrow(() -> condaSetupManager.createVenv(path, PYTHON_VERSION, PACKAGES));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCreateVenvWindows(@TempDir final Path tempDir) throws IOException {
|
||||||
|
final var path = tempDir.resolve("test");
|
||||||
|
final var bin = path.resolve("Library").resolve("bin");
|
||||||
|
Files.createDirectories(bin);
|
||||||
|
final var crypto1 = bin.resolve("libcrypto-1.1.dll");
|
||||||
|
final var crypto2 = bin.resolve("libcrypto-2.sdf");
|
||||||
|
final var ssl1 = bin.resolve("libssl-1.1.dll");
|
||||||
|
final var ssl2 = bin.resolve("libssl-2.sdf");
|
||||||
|
final var other = bin.resolve("other.dll");
|
||||||
|
final var dlls = path.resolve("DLLs");
|
||||||
|
for (final var path1 : List.of(crypto1, crypto2, ssl1, ssl2, other)) {
|
||||||
|
Files.createFile(path1);
|
||||||
|
}
|
||||||
|
final var args = List.of(systemPath.toString(), "create", "-y", "-p", path.toString(), "python=" + PYTHON_VERSION, "p1", "p2");
|
||||||
|
final var result = mock(ProcessResult.class);
|
||||||
|
when(result.exitCode()).thenReturn(0);
|
||||||
|
when(configuration.os()).thenReturn(OS.WINDOWS);
|
||||||
|
when(processRunner.run(args, Duration.ofMinutes(15))).thenReturn(result);
|
||||||
|
assertDoesNotThrow(() -> condaSetupManager.createVenv(path, PYTHON_VERSION, PACKAGES));
|
||||||
|
for (final var path1 : List.of(crypto1, crypto2, ssl1, ssl2)) {
|
||||||
|
assertTrue(Files.exists(dlls.resolve(path1.getFileName())));
|
||||||
|
}
|
||||||
|
assertFalse(Files.exists(dlls.resolve(other.getFileName())));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCreateVenvIOException() throws IOException {
|
||||||
|
final var path = Paths.get("test");
|
||||||
|
final var args = List.of(systemPath.toString(), "create", "-y", "-p", path.toString(), "python=" + PYTHON_VERSION, "p1", "p2");
|
||||||
|
when(processRunner.run(args, Duration.ofMinutes(15))).thenThrow(IOException.class);
|
||||||
|
assertThrows(SetupException.class, () -> condaSetupManager.createVenv(path, PYTHON_VERSION, PACKAGES));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCreateVenvBadResultCode() throws IOException {
|
||||||
|
final var path = Paths.get("test");
|
||||||
|
final var args = List.of(systemPath.toString(), "create", "-y", "-p", path.toString(), "python=" + PYTHON_VERSION, "p1", "p2");
|
||||||
|
final var result = mock(ProcessResult.class);
|
||||||
|
when(result.exitCode()).thenReturn(1);
|
||||||
|
when(processRunner.run(args, Duration.ofMinutes(15))).thenReturn(result);
|
||||||
|
assertThrows(SetupException.class, () -> condaSetupManager.createVenv(path, PYTHON_VERSION, PACKAGES));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testVenvExists(@TempDir final Path tempDirectory) throws IOException {
|
||||||
|
assertFalse(condaSetupManager.venvExists(tempDirectory));
|
||||||
|
Files.createDirectories(tempDirectory.resolve("bin"));
|
||||||
|
|
||||||
|
when(configuration.os()).thenReturn(OS.WINDOWS);
|
||||||
|
Files.createFile(tempDirectory.resolve("bin").resolve("python.exe"));
|
||||||
|
assertTrue(condaSetupManager.venvExists(tempDirectory));
|
||||||
|
|
||||||
|
when(configuration.os()).thenReturn(OS.LINUX);
|
||||||
|
assertFalse(condaSetupManager.venvExists(tempDirectory));
|
||||||
|
|
||||||
|
Files.createFile(tempDirectory.resolve("bin").resolve("python"));
|
||||||
|
assertTrue(condaSetupManager.venvExists(tempDirectory));
|
||||||
|
|
||||||
|
when(configuration.os()).thenReturn(OS.MAC);
|
||||||
|
assertTrue(condaSetupManager.venvExists(tempDirectory));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testIllegal() {
|
||||||
|
assertThrows(NullPointerException.class, () -> new CondaSetupManager(null, processRunner, httpClient));
|
||||||
|
assertThrows(NullPointerException.class, () -> new CondaSetupManager(configuration, null, httpClient));
|
||||||
|
assertThrows(NullPointerException.class, () -> new CondaSetupManager(configuration, processRunner, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -6,6 +6,9 @@ import com.github.gtache.autosubtitle.impl.OS;
|
|||||||
import com.github.gtache.autosubtitle.modules.archive.impl.ArchiveModule;
|
import com.github.gtache.autosubtitle.modules.archive.impl.ArchiveModule;
|
||||||
import com.github.gtache.autosubtitle.modules.setup.impl.SetupModule;
|
import com.github.gtache.autosubtitle.modules.setup.impl.SetupModule;
|
||||||
import com.github.gtache.autosubtitle.modules.subtitle.impl.SubtitleModule;
|
import com.github.gtache.autosubtitle.modules.subtitle.impl.SubtitleModule;
|
||||||
|
import com.github.gtache.autosubtitle.process.ProcessRunner;
|
||||||
|
import com.github.gtache.autosubtitle.process.impl.ProcessRunnerImpl;
|
||||||
|
import dagger.Binds;
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
|
|
||||||
@@ -22,6 +25,9 @@ public abstract class CoreModule {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
abstract ProcessRunner bindsProcessRunner(final ProcessRunnerImpl processRunner);
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
static OS providesOS() {
|
static OS providesOS() {
|
||||||
final var os = OS.getOS();
|
final var os = OS.getOS();
|
||||||
|
|||||||
@@ -6,16 +6,22 @@ import com.github.gtache.autosubtitle.process.ProcessRunner;
|
|||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base implementation of {@link ProcessRunner}
|
* Implementation of {@link ProcessRunner}
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractProcessRunner implements ProcessRunner {
|
public class ProcessRunnerImpl implements ProcessRunner {
|
||||||
|
|
||||||
private static final Logger logger = LogManager.getLogger(AbstractProcessRunner.class);
|
private static final Logger logger = LogManager.getLogger(ProcessRunnerImpl.class);
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
ProcessRunnerImpl() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ProcessResult run(final List<String> args, final Duration duration) throws IOException {
|
public ProcessResult run(final List<String> args, final Duration duration) throws IOException {
|
||||||
@@ -37,22 +43,5 @@ public abstract class AbstractProcessRunner implements ProcessRunner {
|
|||||||
return new ProcessListenerImpl(process);
|
return new ProcessListenerImpl(process);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Runs a process and writes the output to the log
|
|
||||||
*
|
|
||||||
* @param args the command
|
|
||||||
* @param duration The maximum duration to wait for
|
|
||||||
* @return the result
|
|
||||||
* @throws IOException if an error occurs
|
|
||||||
*/
|
|
||||||
protected ProcessResult runListen(final List<String> args, final Duration duration) throws IOException {
|
|
||||||
final var listener = startListen(args);
|
|
||||||
var line = listener.readLine();
|
|
||||||
final var processName = args.getFirst();
|
|
||||||
while (line != null) {
|
|
||||||
logger.info("[{}]: {}", processName, line);
|
|
||||||
line = listener.readLine();
|
|
||||||
}
|
|
||||||
return listener.join(duration);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package com.github.gtache.autosubtitle.setup.impl;
|
package com.github.gtache.autosubtitle.setup.impl;
|
||||||
|
|
||||||
import com.github.gtache.autosubtitle.process.impl.AbstractProcessRunner;
|
import com.github.gtache.autosubtitle.process.ProcessRunner;
|
||||||
import com.github.gtache.autosubtitle.setup.SetupAction;
|
import com.github.gtache.autosubtitle.setup.SetupAction;
|
||||||
import com.github.gtache.autosubtitle.setup.SetupEvent;
|
import com.github.gtache.autosubtitle.setup.SetupEvent;
|
||||||
import com.github.gtache.autosubtitle.setup.SetupException;
|
import com.github.gtache.autosubtitle.setup.SetupException;
|
||||||
@@ -28,27 +28,23 @@ import static java.util.Objects.requireNonNull;
|
|||||||
/**
|
/**
|
||||||
* Base class for all {@link SetupManager} implementations
|
* Base class for all {@link SetupManager} implementations
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractSetupManager extends AbstractProcessRunner implements SetupManager {
|
public abstract class AbstractSetupManager implements SetupManager {
|
||||||
|
|
||||||
private static final Logger logger = LogManager.getLogger(AbstractSetupManager.class);
|
private static final Logger logger = LogManager.getLogger(AbstractSetupManager.class);
|
||||||
private final Set<SetupListener> listeners;
|
private final Set<SetupListener> listeners;
|
||||||
private final HttpClient httpClient;
|
private final HttpClient httpClient;
|
||||||
|
private final ProcessRunner processRunner;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instantiates the manager with a default client
|
* Instantiates the manager with the given runner and client
|
||||||
*/
|
|
||||||
protected AbstractSetupManager() {
|
|
||||||
this(HttpClient.newHttpClient());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instantiates the manager with the given client
|
|
||||||
*
|
*
|
||||||
* @param httpClient The HTTP client to use
|
* @param processRunner The process runner
|
||||||
|
* @param httpClient The HTTP client to use
|
||||||
*/
|
*/
|
||||||
protected AbstractSetupManager(final HttpClient httpClient) {
|
protected AbstractSetupManager(final ProcessRunner processRunner, final HttpClient httpClient) {
|
||||||
this.listeners = new HashSet<>();
|
this.processRunner = requireNonNull(processRunner);
|
||||||
this.httpClient = requireNonNull(httpClient);
|
this.httpClient = requireNonNull(httpClient);
|
||||||
|
this.listeners = new HashSet<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -100,6 +96,13 @@ public abstract class AbstractSetupManager extends AbstractProcessRunner impleme
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The process runner used by this manager
|
||||||
|
*/
|
||||||
|
protected ProcessRunner processRunner() {
|
||||||
|
return processRunner;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Retrieves the setup status
|
* @return Retrieves the setup status
|
||||||
* @throws SetupException if an error occurred
|
* @throws SetupException if an error occurred
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.github.gtache.autosubtitle.process.impl;
|
package com.github.gtache.autosubtitle.process.impl;
|
||||||
|
|
||||||
import com.github.gtache.autosubtitle.impl.OS;
|
import com.github.gtache.autosubtitle.impl.OS;
|
||||||
|
import com.github.gtache.autosubtitle.process.ProcessRunner;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -10,25 +11,25 @@ import java.util.List;
|
|||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
|
|
||||||
class TestAbstractProcessRunner {
|
class TestProcessRunnerImpl {
|
||||||
|
|
||||||
private static final List<String> ARGS = OS.getOS() == OS.WINDOWS ? List.of("powershell.exe", "-Command", "\"echo '1\n2\n3'\"") : List.of("echo", "1\n2\n3");
|
private static final List<String> ARGS = OS.getOS() == OS.WINDOWS ? List.of("powershell.exe", "-Command", "\"echo '1\n2\n3'\"") : List.of("echo", "1\n2\n3");
|
||||||
private final DummyProcessRunner dummyProcessRunner;
|
private final ProcessRunner runner;
|
||||||
|
|
||||||
TestAbstractProcessRunner() {
|
TestProcessRunnerImpl() {
|
||||||
this.dummyProcessRunner = new DummyProcessRunner();
|
this.runner = new ProcessRunnerImpl();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testRun() throws IOException {
|
void testRun() throws IOException {
|
||||||
final var expected = new ProcessResultImpl(0, List.of("1", "2", "3"));
|
final var expected = new ProcessResultImpl(0, List.of("1", "2", "3"));
|
||||||
final var actual = dummyProcessRunner.run(ARGS, Duration.ofSeconds(5));
|
final var actual = runner.run(ARGS, Duration.ofSeconds(5));
|
||||||
assertEquals(expected, actual);
|
assertEquals(expected, actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testStart() throws IOException, InterruptedException {
|
void testStart() throws IOException, InterruptedException {
|
||||||
final var process = dummyProcessRunner.start(ARGS);
|
final var process = runner.start(ARGS);
|
||||||
process.waitFor();
|
process.waitFor();
|
||||||
assertEquals(0, process.exitValue());
|
assertEquals(0, process.exitValue());
|
||||||
final var read = new String(process.getInputStream().readAllBytes());
|
final var read = new String(process.getInputStream().readAllBytes());
|
||||||
@@ -38,7 +39,7 @@ class TestAbstractProcessRunner {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testStartListen() throws IOException {
|
void testStartListen() throws IOException {
|
||||||
final var listener = dummyProcessRunner.startListen(ARGS);
|
final var listener = runner.startListen(ARGS);
|
||||||
assertEquals("1", listener.readLine());
|
assertEquals("1", listener.readLine());
|
||||||
assertEquals("2", listener.readLine());
|
assertEquals("2", listener.readLine());
|
||||||
assertEquals("3", listener.readLine());
|
assertEquals("3", listener.readLine());
|
||||||
@@ -47,15 +48,4 @@ class TestAbstractProcessRunner {
|
|||||||
assertEquals(0, result.exitCode());
|
assertEquals(0, result.exitCode());
|
||||||
assertEquals(List.of("1", "2", "3"), result.output());
|
assertEquals(List.of("1", "2", "3"), result.output());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
void testRunListen() throws IOException {
|
|
||||||
final var result = dummyProcessRunner.runListen(ARGS, Duration.ofSeconds(5));
|
|
||||||
assertEquals(0, result.exitCode());
|
|
||||||
assertEquals(List.of("1", "2", "3"), result.output());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final class DummyProcessRunner extends AbstractProcessRunner {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.github.gtache.autosubtitle.setup.impl;
|
package com.github.gtache.autosubtitle.setup.impl;
|
||||||
|
|
||||||
|
import com.github.gtache.autosubtitle.process.ProcessRunner;
|
||||||
import com.github.gtache.autosubtitle.setup.SetupAction;
|
import com.github.gtache.autosubtitle.setup.SetupAction;
|
||||||
import com.github.gtache.autosubtitle.setup.SetupEvent;
|
import com.github.gtache.autosubtitle.setup.SetupEvent;
|
||||||
import com.github.gtache.autosubtitle.setup.SetupException;
|
import com.github.gtache.autosubtitle.setup.SetupException;
|
||||||
@@ -29,18 +30,21 @@ class TestAbstractSetupManager {
|
|||||||
private final SetupListener listener2;
|
private final SetupListener listener2;
|
||||||
private final SetupEvent event;
|
private final SetupEvent event;
|
||||||
private final SetupAction setupAction;
|
private final SetupAction setupAction;
|
||||||
|
private final ProcessRunner processRunner;
|
||||||
private final HttpClient httpClient;
|
private final HttpClient httpClient;
|
||||||
|
|
||||||
TestAbstractSetupManager(@Mock final SetupListener listener1,
|
TestAbstractSetupManager(@Mock final SetupListener listener1,
|
||||||
@Mock final SetupListener listener2,
|
@Mock final SetupListener listener2,
|
||||||
@Mock final SetupEvent event,
|
@Mock final SetupEvent event,
|
||||||
@Mock final SetupAction setupAction,
|
@Mock final SetupAction setupAction,
|
||||||
|
@Mock final ProcessRunner processRunner,
|
||||||
@Mock final HttpClient httpClient) {
|
@Mock final HttpClient httpClient) {
|
||||||
this.manager = spy(new DummySetupManager(httpClient));
|
this.manager = spy(new DummySetupManager(processRunner, httpClient));
|
||||||
this.listener1 = requireNonNull(listener1);
|
this.listener1 = requireNonNull(listener1);
|
||||||
this.listener2 = requireNonNull(listener2);
|
this.listener2 = requireNonNull(listener2);
|
||||||
this.event = requireNonNull(event);
|
this.event = requireNonNull(event);
|
||||||
this.setupAction = requireNonNull(setupAction);
|
this.setupAction = requireNonNull(setupAction);
|
||||||
|
this.processRunner = requireNonNull(processRunner);
|
||||||
this.httpClient = requireNonNull(httpClient);
|
this.httpClient = requireNonNull(httpClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,13 +227,13 @@ class TestAbstractSetupManager {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testIllegal() {
|
void testIllegal() {
|
||||||
assertThrows(NullPointerException.class, () -> new DummySetupManager(null));
|
assertThrows(NullPointerException.class, () -> new DummySetupManager(null, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class DummySetupManager extends AbstractSetupManager {
|
private static final class DummySetupManager extends AbstractSetupManager {
|
||||||
|
|
||||||
private DummySetupManager(final HttpClient httpClient) {
|
private DummySetupManager(final ProcessRunner processRunner, final HttpClient httpClient) {
|
||||||
super(httpClient);
|
super(processRunner, httpClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.github.gtache.autosubtitle.setup.deepl;
|
package com.github.gtache.autosubtitle.setup.deepl;
|
||||||
|
|
||||||
|
import com.github.gtache.autosubtitle.process.ProcessRunner;
|
||||||
import com.github.gtache.autosubtitle.setup.SetupException;
|
import com.github.gtache.autosubtitle.setup.SetupException;
|
||||||
import com.github.gtache.autosubtitle.setup.SetupManager;
|
import com.github.gtache.autosubtitle.setup.SetupManager;
|
||||||
import com.github.gtache.autosubtitle.setup.SetupStatus;
|
import com.github.gtache.autosubtitle.setup.SetupStatus;
|
||||||
@@ -7,6 +8,7 @@ import com.github.gtache.autosubtitle.setup.SetupUserBridge;
|
|||||||
import com.github.gtache.autosubtitle.setup.impl.AbstractSetupManager;
|
import com.github.gtache.autosubtitle.setup.impl.AbstractSetupManager;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
import java.net.http.HttpClient;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.prefs.BackingStoreException;
|
import java.util.prefs.BackingStoreException;
|
||||||
import java.util.prefs.Preferences;
|
import java.util.prefs.Preferences;
|
||||||
@@ -22,7 +24,9 @@ public class DeepLSetupManager extends AbstractSetupManager {
|
|||||||
private final Preferences preferences;
|
private final Preferences preferences;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
DeepLSetupManager(final SetupUserBridge userBridge, final Preferences preferences) {
|
DeepLSetupManager(final SetupUserBridge userBridge, final Preferences preferences, final ProcessRunner processRunner,
|
||||||
|
final HttpClient httpClient) {
|
||||||
|
super(processRunner, httpClient);
|
||||||
this.userBridge = Objects.requireNonNull(userBridge);
|
this.userBridge = Objects.requireNonNull(userBridge);
|
||||||
this.preferences = Objects.requireNonNull(preferences);
|
this.preferences = Objects.requireNonNull(preferences);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,10 +11,12 @@ import com.github.gtache.autosubtitle.impl.FileVideoImpl;
|
|||||||
import com.github.gtache.autosubtitle.impl.VideoInfoImpl;
|
import com.github.gtache.autosubtitle.impl.VideoInfoImpl;
|
||||||
import com.github.gtache.autosubtitle.modules.setup.ffmpeg.FFmpegBundledPath;
|
import com.github.gtache.autosubtitle.modules.setup.ffmpeg.FFmpegBundledPath;
|
||||||
import com.github.gtache.autosubtitle.modules.setup.ffmpeg.FFmpegSystemPath;
|
import com.github.gtache.autosubtitle.modules.setup.ffmpeg.FFmpegSystemPath;
|
||||||
import com.github.gtache.autosubtitle.process.impl.AbstractProcessRunner;
|
import com.github.gtache.autosubtitle.process.ProcessRunner;
|
||||||
import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
|
import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
|
||||||
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverter;
|
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverter;
|
||||||
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverterProvider;
|
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverterProvider;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -34,21 +36,24 @@ import static java.util.Objects.requireNonNull;
|
|||||||
/**
|
/**
|
||||||
* FFmpeg implementation of {@link VideoConverter}
|
* FFmpeg implementation of {@link VideoConverter}
|
||||||
*/
|
*/
|
||||||
public class FFmpegVideoConverter extends AbstractProcessRunner implements VideoConverter {
|
public class FFmpegVideoConverter implements VideoConverter {
|
||||||
|
private static final Logger logger = LogManager.getLogger(FFmpegVideoConverter.class);
|
||||||
private static final String TEMP_FILE_PREFIX = "autosubtitle";
|
private static final String TEMP_FILE_PREFIX = "autosubtitle";
|
||||||
private final Path bundledPath;
|
private final Path bundledPath;
|
||||||
private final Path systemPath;
|
private final Path systemPath;
|
||||||
private final SubtitleConverterProvider converterProvider;
|
private final SubtitleConverterProvider converterProvider;
|
||||||
private final Preferences preferences;
|
private final Preferences preferences;
|
||||||
|
private final ProcessRunner processRunner;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
FFmpegVideoConverter(@FFmpegBundledPath final Path bundledPath, @FFmpegSystemPath final Path systemPath,
|
FFmpegVideoConverter(@FFmpegBundledPath final Path bundledPath, @FFmpegSystemPath final Path systemPath,
|
||||||
final SubtitleConverterProvider converterProvider, final Preferences preferences) {
|
final SubtitleConverterProvider converterProvider, final Preferences preferences,
|
||||||
|
final ProcessRunner processRunner) {
|
||||||
this.bundledPath = requireNonNull(bundledPath);
|
this.bundledPath = requireNonNull(bundledPath);
|
||||||
this.systemPath = requireNonNull(systemPath);
|
this.systemPath = requireNonNull(systemPath);
|
||||||
this.converterProvider = requireNonNull(converterProvider);
|
this.converterProvider = requireNonNull(converterProvider);
|
||||||
this.preferences = requireNonNull(preferences);
|
this.preferences = requireNonNull(preferences);
|
||||||
|
this.processRunner = requireNonNull(processRunner);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -197,4 +202,15 @@ public class FFmpegVideoConverter extends AbstractProcessRunner implements Video
|
|||||||
private String getFFmpegPath() {
|
private String getFFmpegPath() {
|
||||||
return Files.isRegularFile(bundledPath) ? bundledPath.toString() : systemPath.toString();
|
return Files.isRegularFile(bundledPath) ? bundledPath.toString() : systemPath.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void runListen(final List<String> args, final Duration duration) throws IOException {
|
||||||
|
final var listener = processRunner.startListen(args);
|
||||||
|
var line = listener.readLine();
|
||||||
|
final var processName = args.getFirst();
|
||||||
|
while (line != null) {
|
||||||
|
logger.info("[{}]: {}", processName, line);
|
||||||
|
line = listener.readLine();
|
||||||
|
}
|
||||||
|
listener.join(duration);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import com.github.gtache.autosubtitle.impl.FileVideoImpl;
|
|||||||
import com.github.gtache.autosubtitle.impl.VideoInfoImpl;
|
import com.github.gtache.autosubtitle.impl.VideoInfoImpl;
|
||||||
import com.github.gtache.autosubtitle.modules.setup.ffmpeg.FFprobeBundledPath;
|
import com.github.gtache.autosubtitle.modules.setup.ffmpeg.FFprobeBundledPath;
|
||||||
import com.github.gtache.autosubtitle.modules.setup.ffmpeg.FFprobeSystemPath;
|
import com.github.gtache.autosubtitle.modules.setup.ffmpeg.FFprobeSystemPath;
|
||||||
import com.github.gtache.autosubtitle.process.impl.AbstractProcessRunner;
|
import com.github.gtache.autosubtitle.process.ProcessRunner;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -20,20 +20,23 @@ import static java.util.Objects.requireNonNull;
|
|||||||
/**
|
/**
|
||||||
* FFprobe implementation of {@link VideoLoader}
|
* FFprobe implementation of {@link VideoLoader}
|
||||||
*/
|
*/
|
||||||
public class FFprobeVideoLoader extends AbstractProcessRunner implements VideoLoader {
|
public class FFprobeVideoLoader implements VideoLoader {
|
||||||
|
|
||||||
private final Path bundledPath;
|
private final Path bundledPath;
|
||||||
private final Path systemPath;
|
private final Path systemPath;
|
||||||
|
private final ProcessRunner processRunner;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
FFprobeVideoLoader(@FFprobeBundledPath final Path bundledPath, @FFprobeSystemPath final Path systemPath) {
|
FFprobeVideoLoader(@FFprobeBundledPath final Path bundledPath, @FFprobeSystemPath final Path systemPath,
|
||||||
|
final ProcessRunner processRunner) {
|
||||||
this.bundledPath = requireNonNull(bundledPath);
|
this.bundledPath = requireNonNull(bundledPath);
|
||||||
this.systemPath = requireNonNull(systemPath);
|
this.systemPath = requireNonNull(systemPath);
|
||||||
|
this.processRunner = requireNonNull(processRunner);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Video loadVideo(final Path path) throws IOException {
|
public Video loadVideo(final Path path) throws IOException {
|
||||||
final var result = run(List.of(getFFprobePath(), "-v", "error", "-select_streams", "v", "-show_entries", "stream=width,height,duration", "-of", "csv=p=0", path.toString()), Duration.ofSeconds(5));
|
final var result = processRunner.run(List.of(getFFprobePath(), "-v", "error", "-select_streams", "v", "-show_entries", "stream=width,height,duration", "-of", "csv=p=0", path.toString()), Duration.ofSeconds(5));
|
||||||
final var resolution = result.output().getLast();
|
final var resolution = result.output().getLast();
|
||||||
final var split = resolution.split(",");
|
final var split = resolution.split(",");
|
||||||
final var width = Integer.parseInt(split[0]);
|
final var width = Integer.parseInt(split[0]);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.github.gtache.autosubtitle.setup.ffmpeg;
|
|||||||
|
|
||||||
import com.github.gtache.autosubtitle.archive.ArchiverProvider;
|
import com.github.gtache.autosubtitle.archive.ArchiverProvider;
|
||||||
import com.github.gtache.autosubtitle.impl.Architecture;
|
import com.github.gtache.autosubtitle.impl.Architecture;
|
||||||
|
import com.github.gtache.autosubtitle.process.ProcessRunner;
|
||||||
import com.github.gtache.autosubtitle.setup.SetupException;
|
import com.github.gtache.autosubtitle.setup.SetupException;
|
||||||
import com.github.gtache.autosubtitle.setup.SetupManager;
|
import com.github.gtache.autosubtitle.setup.SetupManager;
|
||||||
import com.github.gtache.autosubtitle.setup.SetupStatus;
|
import com.github.gtache.autosubtitle.setup.SetupStatus;
|
||||||
@@ -32,8 +33,8 @@ public class FFmpegSetupManager extends AbstractSetupManager {
|
|||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
FFmpegSetupManager(final FFmpegSetupConfiguration configuration, final ArchiverProvider archiverProvider,
|
FFmpegSetupManager(final FFmpegSetupConfiguration configuration, final ArchiverProvider archiverProvider,
|
||||||
final HttpClient httpClient) {
|
final ProcessRunner processRunner, final HttpClient httpClient) {
|
||||||
super(httpClient);
|
super(processRunner, httpClient);
|
||||||
this.configuration = requireNonNull(configuration);
|
this.configuration = requireNonNull(configuration);
|
||||||
this.archiverProvider = requireNonNull(archiverProvider);
|
this.archiverProvider = requireNonNull(archiverProvider);
|
||||||
}
|
}
|
||||||
@@ -190,7 +191,7 @@ public class FFmpegSetupManager extends AbstractSetupManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkSystemFFmpeg() throws IOException {
|
private boolean checkSystemFFmpeg() throws IOException {
|
||||||
final var result = run(List.of(configuration.systemFFmpegPath().toString(), "-version"), Duration.ofSeconds(5));
|
final var result = processRunner().run(List.of(configuration.systemFFmpegPath().toString(), "-version"), Duration.ofSeconds(5));
|
||||||
return result.exitCode() == 0;
|
return result.exitCode() == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.github.gtache.autosubtitle.ffmpeg;
|
|||||||
import com.github.gtache.autosubtitle.Video;
|
import com.github.gtache.autosubtitle.Video;
|
||||||
import com.github.gtache.autosubtitle.VideoInfo;
|
import com.github.gtache.autosubtitle.VideoInfo;
|
||||||
import com.github.gtache.autosubtitle.impl.OS;
|
import com.github.gtache.autosubtitle.impl.OS;
|
||||||
|
import com.github.gtache.autosubtitle.process.ProcessRunner;
|
||||||
import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
|
import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
|
||||||
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverter;
|
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverter;
|
||||||
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverterProvider;
|
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverterProvider;
|
||||||
@@ -24,6 +25,7 @@ import static org.mockito.Mockito.when;
|
|||||||
class TestFFmpegVideoConverter {
|
class TestFFmpegVideoConverter {
|
||||||
|
|
||||||
private final FFmpegVideoConverter converter;
|
private final FFmpegVideoConverter converter;
|
||||||
|
private final ProcessRunner runner;
|
||||||
private final SubtitleConverter subtitleConverter;
|
private final SubtitleConverter subtitleConverter;
|
||||||
private final SubtitleConverterProvider subtitleConverterProvider;
|
private final SubtitleConverterProvider subtitleConverterProvider;
|
||||||
private final Video video;
|
private final Video video;
|
||||||
@@ -33,11 +35,12 @@ class TestFFmpegVideoConverter {
|
|||||||
private final SubtitleCollection<?> collection;
|
private final SubtitleCollection<?> collection;
|
||||||
private final Preferences preferences;
|
private final Preferences preferences;
|
||||||
|
|
||||||
TestFFmpegVideoConverter(@Mock final SubtitleConverter subtitleConverter, @Mock final SubtitleConverterProvider subtitleConverterProvider, @Mock final Video video,
|
TestFFmpegVideoConverter(@Mock final SubtitleConverter subtitleConverter, @Mock final ProcessRunner runner, @Mock final SubtitleConverterProvider subtitleConverterProvider, @Mock final Video video,
|
||||||
@Mock final VideoInfo videoInfo, @Mock final SubtitleCollection<?> collection, @Mock final Preferences preferences) throws IOException {
|
@Mock final VideoInfo videoInfo, @Mock final SubtitleCollection<?> collection, @Mock final Preferences preferences) throws IOException {
|
||||||
final var output = (OS.getOS() == OS.WINDOWS ? System.getProperty("java.io.tmpdir") : "/tmp");
|
final var output = (OS.getOS() == OS.WINDOWS ? System.getProperty("java.io.tmpdir") : "/tmp");
|
||||||
final var resource = OS.getOS() == OS.WINDOWS ? "fake-ffmpeg.exe" : "fake-ffmpeg.sh";
|
final var resource = OS.getOS() == OS.WINDOWS ? "fake-ffmpeg.exe" : "fake-ffmpeg.sh";
|
||||||
this.video = Objects.requireNonNull(video);
|
this.video = Objects.requireNonNull(video);
|
||||||
|
this.runner = Objects.requireNonNull(runner);
|
||||||
this.videoInfo = Objects.requireNonNull(videoInfo);
|
this.videoInfo = Objects.requireNonNull(videoInfo);
|
||||||
when(video.info()).thenReturn(videoInfo);
|
when(video.info()).thenReturn(videoInfo);
|
||||||
this.tmpFile = Files.createTempFile("fake-ffmpeg", resource.substring(resource.lastIndexOf('.')));
|
this.tmpFile = Files.createTempFile("fake-ffmpeg", resource.substring(resource.lastIndexOf('.')));
|
||||||
@@ -48,7 +51,7 @@ class TestFFmpegVideoConverter {
|
|||||||
this.subtitleConverter = Objects.requireNonNull(subtitleConverter);
|
this.subtitleConverter = Objects.requireNonNull(subtitleConverter);
|
||||||
this.subtitleConverterProvider = Objects.requireNonNull(subtitleConverterProvider);
|
this.subtitleConverterProvider = Objects.requireNonNull(subtitleConverterProvider);
|
||||||
this.preferences = Objects.requireNonNull(preferences);
|
this.preferences = Objects.requireNonNull(preferences);
|
||||||
this.converter = new FFmpegVideoConverter(tmpFile, tmpFile, subtitleConverterProvider, preferences);
|
this.converter = new FFmpegVideoConverter(tmpFile, tmpFile, subtitleConverterProvider, preferences, runner);
|
||||||
this.collection = Objects.requireNonNull(collection);
|
this.collection = Objects.requireNonNull(collection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,25 +3,32 @@ package com.github.gtache.autosubtitle.ffmpeg;
|
|||||||
import com.github.gtache.autosubtitle.impl.FileVideoImpl;
|
import com.github.gtache.autosubtitle.impl.FileVideoImpl;
|
||||||
import com.github.gtache.autosubtitle.impl.OS;
|
import com.github.gtache.autosubtitle.impl.OS;
|
||||||
import com.github.gtache.autosubtitle.impl.VideoInfoImpl;
|
import com.github.gtache.autosubtitle.impl.VideoInfoImpl;
|
||||||
|
import com.github.gtache.autosubtitle.process.ProcessRunner;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.Disabled;
|
import org.junit.jupiter.api.Disabled;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.nio.file.StandardCopyOption;
|
import java.nio.file.StandardCopyOption;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
class TestFFmpegVideoLoader {
|
class TestFFmpegVideoLoader {
|
||||||
|
|
||||||
private final FFprobeVideoLoader loader;
|
private final FFprobeVideoLoader loader;
|
||||||
|
private final ProcessRunner runner;
|
||||||
private final Path tmpFile;
|
private final Path tmpFile;
|
||||||
private final Path outputPath;
|
private final Path outputPath;
|
||||||
|
|
||||||
TestFFmpegVideoLoader() throws IOException {
|
TestFFmpegVideoLoader(@Mock final ProcessRunner runner) throws IOException {
|
||||||
final var output = (OS.getOS() == OS.WINDOWS ? System.getProperty("java.io.tmpdir") : "/tmp");
|
final var output = (OS.getOS() == OS.WINDOWS ? System.getProperty("java.io.tmpdir") : "/tmp");
|
||||||
final var resource = OS.getOS() == OS.WINDOWS ? "fake-ffprobe.exe" : "fake-ffprobe.sh";
|
final var resource = OS.getOS() == OS.WINDOWS ? "fake-ffprobe.exe" : "fake-ffprobe.sh";
|
||||||
this.tmpFile = Files.createTempFile("fake-ffprobe", resource.substring(resource.lastIndexOf('.')));
|
this.tmpFile = Files.createTempFile("fake-ffprobe", resource.substring(resource.lastIndexOf('.')));
|
||||||
@@ -29,7 +36,8 @@ class TestFFmpegVideoLoader {
|
|||||||
Files.copy(in, tmpFile, StandardCopyOption.REPLACE_EXISTING);
|
Files.copy(in, tmpFile, StandardCopyOption.REPLACE_EXISTING);
|
||||||
}
|
}
|
||||||
this.outputPath = Path.of(output, "test-ffprobe-output.txt");
|
this.outputPath = Path.of(output, "test-ffprobe-output.txt");
|
||||||
this.loader = new FFprobeVideoLoader(tmpFile, tmpFile);
|
this.runner = Objects.requireNonNull(runner);
|
||||||
|
this.loader = new FFprobeVideoLoader(tmpFile, tmpFile, runner);
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterEach
|
@AfterEach
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
subtitles.add.prompt.label=Enter text here...
|
||||||
subtitles.button.load.label=Load subtitles...
|
subtitles.button.load.label=Load subtitles...
|
||||||
subtitles.button.reset.label=Reset subtitles
|
subtitles.button.reset.label=Reset subtitles
|
||||||
subtitles.button.subtitles.save.label=Save subtitles...
|
subtitles.button.subtitles.save.label=Save subtitles...
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
subtitles.add.prompt.label=Entrez le texte ici...
|
||||||
subtitles.button.load.label=Charger des sous-titres...
|
subtitles.button.load.label=Charger des sous-titres...
|
||||||
subtitles.button.reset.label=R\u00E9initialiser les sous-titres
|
subtitles.button.reset.label=R\u00E9initialiser les sous-titres
|
||||||
subtitles.button.subtitles.save.label=Sauvegarder les sous-titres...
|
subtitles.button.subtitles.save.label=Sauvegarder les sous-titres...
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import javafx.collections.MapChangeListener;
|
|||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Button;
|
||||||
import javafx.scene.control.ComboBox;
|
import javafx.scene.control.ComboBox;
|
||||||
|
import javafx.scene.control.SelectionMode;
|
||||||
import javafx.scene.control.Tab;
|
import javafx.scene.control.Tab;
|
||||||
import javafx.scene.control.TabPane;
|
import javafx.scene.control.TabPane;
|
||||||
import javafx.scene.control.TableColumn;
|
import javafx.scene.control.TableColumn;
|
||||||
@@ -165,6 +166,8 @@ public class FXSubtitlesController extends AbstractFXController implements Subti
|
|||||||
|
|
||||||
private void bindTable() {
|
private void bindTable() {
|
||||||
subtitlesTable.setItems(model.selectedSubtitles());
|
subtitlesTable.setItems(model.selectedSubtitles());
|
||||||
|
subtitlesTable.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
|
||||||
|
|
||||||
subtitlesTable.setOnKeyPressed(e -> {
|
subtitlesTable.setOnKeyPressed(e -> {
|
||||||
if (e.getCode().isLetterKey() || e.getCode().isDigitKey()) {
|
if (e.getCode().isLetterKey() || e.getCode().isDigitKey()) {
|
||||||
editFocusedCell();
|
editFocusedCell();
|
||||||
@@ -180,6 +183,9 @@ public class FXSubtitlesController extends AbstractFXController implements Subti
|
|||||||
e.consume();
|
e.consume();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
subtitlesTable.setOnContextMenuRequested(e -> {
|
||||||
|
//TODO menu with copy, delete
|
||||||
|
});
|
||||||
startColumn.setCellFactory(TextFieldTableCell.forTableColumn(new TimeStringConverter(timeFormatter)));
|
startColumn.setCellFactory(TextFieldTableCell.forTableColumn(new TimeStringConverter(timeFormatter)));
|
||||||
startColumn.setCellValueFactory(param -> new SimpleObjectProperty<>(param.getValue() == null ? null : param.getValue().start()));
|
startColumn.setCellValueFactory(param -> new SimpleObjectProperty<>(param.getValue() == null ? null : param.getValue().start()));
|
||||||
startColumn.setOnEditCommit(e -> {
|
startColumn.setOnEditCommit(e -> {
|
||||||
@@ -242,7 +248,7 @@ public class FXSubtitlesController extends AbstractFXController implements Subti
|
|||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private void addPressed() {
|
private void addPressed() {
|
||||||
model.selectedCollection().subtitles().add(new ObservableSubtitleImpl("Enter text here..."));
|
model.selectedCollection().subtitles().add(new ObservableSubtitleImpl(resources.getString("subtitles.add.prompt.label")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.github.gtache.autosubtitle.setup.whisper.base;
|
package com.github.gtache.autosubtitle.setup.whisper.base;
|
||||||
|
|
||||||
|
import com.github.gtache.autosubtitle.process.ProcessRunner;
|
||||||
import com.github.gtache.autosubtitle.setup.SetupException;
|
import com.github.gtache.autosubtitle.setup.SetupException;
|
||||||
import com.github.gtache.autosubtitle.setup.conda.CondaSetupManager;
|
import com.github.gtache.autosubtitle.setup.conda.CondaSetupManager;
|
||||||
import com.github.gtache.autosubtitle.setup.whisper.AbstractWhisperSetupManager;
|
import com.github.gtache.autosubtitle.setup.whisper.AbstractWhisperSetupManager;
|
||||||
@@ -10,6 +11,7 @@ import org.apache.logging.log4j.Logger;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.http.HttpClient;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -23,8 +25,9 @@ public class WhisperSetupManager extends AbstractWhisperSetupManager {
|
|||||||
private static final Logger logger = LogManager.getLogger(WhisperSetupManager.class);
|
private static final Logger logger = LogManager.getLogger(WhisperSetupManager.class);
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
WhisperSetupManager(final CondaSetupManager condaSetupManager, final WhisperSetupConfiguration configuration) {
|
WhisperSetupManager(final CondaSetupManager condaSetupManager, final WhisperSetupConfiguration configuration,
|
||||||
super(condaSetupManager, configuration);
|
final ProcessRunner processRunner, final HttpClient httpClient) {
|
||||||
|
super(condaSetupManager, configuration, processRunner, httpClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -37,7 +40,7 @@ public class WhisperSetupManager extends AbstractWhisperSetupManager {
|
|||||||
final var path = getPythonPath();
|
final var path = getPythonPath();
|
||||||
try {
|
try {
|
||||||
logger.info("Installing whisper");
|
logger.info("Installing whisper");
|
||||||
final var result = run(List.of(path.toString(), "-m", "pip", "install", "-U", "openai-whisper", "numpy<2"), Duration.ofMinutes(15));
|
final var result = processRunner().run(List.of(path.toString(), "-m", "pip", "install", "-U", "openai-whisper", "numpy<2"), Duration.ofMinutes(15));
|
||||||
if (result.exitCode() == 0) {
|
if (result.exitCode() == 0) {
|
||||||
logger.info("Whisper installed");
|
logger.info("Whisper installed");
|
||||||
} else {
|
} else {
|
||||||
@@ -53,7 +56,7 @@ public class WhisperSetupManager extends AbstractWhisperSetupManager {
|
|||||||
final var path = getPythonPath();
|
final var path = getPythonPath();
|
||||||
if (Files.exists(path)) {
|
if (Files.exists(path)) {
|
||||||
try {
|
try {
|
||||||
final var result = run(List.of(path.toString(), "-m", "pip", "show", "openai-whisper"), Duration.ofSeconds(5));
|
final var result = processRunner().run(List.of(path.toString(), "-m", "pip", "show", "openai-whisper"), Duration.ofSeconds(5));
|
||||||
return result.exitCode() == 0;
|
return result.exitCode() == 0;
|
||||||
} catch (final IOException e) {
|
} catch (final IOException e) {
|
||||||
throw new SetupException(e);
|
throw new SetupException(e);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.github.gtache.autosubtitle.subtitle.extractor.whisper.base;
|
|||||||
import com.github.gtache.autosubtitle.Language;
|
import com.github.gtache.autosubtitle.Language;
|
||||||
import com.github.gtache.autosubtitle.impl.OS;
|
import com.github.gtache.autosubtitle.impl.OS;
|
||||||
import com.github.gtache.autosubtitle.modules.setup.whisper.WhisperVenvPath;
|
import com.github.gtache.autosubtitle.modules.setup.whisper.WhisperVenvPath;
|
||||||
|
import com.github.gtache.autosubtitle.process.ProcessRunner;
|
||||||
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverterProvider;
|
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverterProvider;
|
||||||
import com.github.gtache.autosubtitle.subtitle.extractor.ExtractionModel;
|
import com.github.gtache.autosubtitle.subtitle.extractor.ExtractionModel;
|
||||||
import com.github.gtache.autosubtitle.subtitle.extractor.SubtitleExtractor;
|
import com.github.gtache.autosubtitle.subtitle.extractor.SubtitleExtractor;
|
||||||
@@ -23,8 +24,9 @@ public class WhisperSubtitleExtractor extends AbstractWhisperSubtitleExtractor {
|
|||||||
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
WhisperSubtitleExtractor(@WhisperVenvPath final Path venvPath, final SubtitleConverterProvider converterProvider, final OS os) {
|
WhisperSubtitleExtractor(@WhisperVenvPath final Path venvPath, final SubtitleConverterProvider converterProvider,
|
||||||
super(venvPath, converterProvider, os);
|
final ProcessRunner processRunner, final OS os) {
|
||||||
|
super(venvPath, converterProvider, processRunner, os);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.github.gtache.autosubtitle.setup.whisper;
|
package com.github.gtache.autosubtitle.setup.whisper;
|
||||||
|
|
||||||
import com.github.gtache.autosubtitle.impl.OS;
|
import com.github.gtache.autosubtitle.impl.OS;
|
||||||
|
import com.github.gtache.autosubtitle.process.ProcessRunner;
|
||||||
import com.github.gtache.autosubtitle.setup.SetupAction;
|
import com.github.gtache.autosubtitle.setup.SetupAction;
|
||||||
import com.github.gtache.autosubtitle.setup.SetupException;
|
import com.github.gtache.autosubtitle.setup.SetupException;
|
||||||
import com.github.gtache.autosubtitle.setup.SetupManager;
|
import com.github.gtache.autosubtitle.setup.SetupManager;
|
||||||
@@ -10,6 +11,7 @@ import com.github.gtache.autosubtitle.setup.impl.AbstractSetupManager;
|
|||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.net.http.HttpClient;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
|
||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
@@ -26,7 +28,9 @@ public abstract class AbstractWhisperSetupManager extends AbstractSetupManager {
|
|||||||
private final CondaSetupManager condaSetupManager;
|
private final CondaSetupManager condaSetupManager;
|
||||||
private final WhisperSetupConfiguration configuration;
|
private final WhisperSetupConfiguration configuration;
|
||||||
|
|
||||||
protected AbstractWhisperSetupManager(final CondaSetupManager condaSetupManager, final WhisperSetupConfiguration configuration) {
|
protected AbstractWhisperSetupManager(final CondaSetupManager condaSetupManager, final WhisperSetupConfiguration configuration,
|
||||||
|
final ProcessRunner processRunner, final HttpClient httpClient) {
|
||||||
|
super(processRunner, httpClient);
|
||||||
this.condaSetupManager = requireNonNull(condaSetupManager);
|
this.condaSetupManager = requireNonNull(condaSetupManager);
|
||||||
this.configuration = requireNonNull(configuration);
|
this.configuration = requireNonNull(configuration);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import com.github.gtache.autosubtitle.File;
|
|||||||
import com.github.gtache.autosubtitle.Language;
|
import com.github.gtache.autosubtitle.Language;
|
||||||
import com.github.gtache.autosubtitle.Video;
|
import com.github.gtache.autosubtitle.Video;
|
||||||
import com.github.gtache.autosubtitle.impl.OS;
|
import com.github.gtache.autosubtitle.impl.OS;
|
||||||
import com.github.gtache.autosubtitle.process.impl.AbstractProcessRunner;
|
import com.github.gtache.autosubtitle.process.ProcessRunner;
|
||||||
import com.github.gtache.autosubtitle.subtitle.Subtitle;
|
import com.github.gtache.autosubtitle.subtitle.Subtitle;
|
||||||
import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
|
import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
|
||||||
import com.github.gtache.autosubtitle.subtitle.converter.ParseException;
|
import com.github.gtache.autosubtitle.subtitle.converter.ParseException;
|
||||||
@@ -34,7 +34,7 @@ import static java.util.Objects.requireNonNull;
|
|||||||
/**
|
/**
|
||||||
* Base implementation of {@link SubtitleExtractor} for Whisper
|
* Base implementation of {@link SubtitleExtractor} for Whisper
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractWhisperSubtitleExtractor extends AbstractProcessRunner implements SubtitleExtractor {
|
public abstract class AbstractWhisperSubtitleExtractor implements SubtitleExtractor<Subtitle> {
|
||||||
|
|
||||||
private static final Logger logger = LogManager.getLogger(AbstractWhisperSubtitleExtractor.class);
|
private static final Logger logger = LogManager.getLogger(AbstractWhisperSubtitleExtractor.class);
|
||||||
|
|
||||||
@@ -43,13 +43,16 @@ public abstract class AbstractWhisperSubtitleExtractor extends AbstractProcessRu
|
|||||||
private static final Pattern LINE_PROGRESS_PATTERN = Pattern.compile("^\\[\\d{2}:\\d{2}\\.\\d{3} --> (?<minutes>\\d{2}):(?<seconds>\\d{2})\\.(?<millis>\\d{3})].+");
|
private static final Pattern LINE_PROGRESS_PATTERN = Pattern.compile("^\\[\\d{2}:\\d{2}\\.\\d{3} --> (?<minutes>\\d{2}):(?<seconds>\\d{2})\\.(?<millis>\\d{3})].+");
|
||||||
private static final Pattern TQDM_PROGRESS_PATTERN = Pattern.compile("^(?<progress>\\d{1,3})%\\|.+");
|
private static final Pattern TQDM_PROGRESS_PATTERN = Pattern.compile("^(?<progress>\\d{1,3})%\\|.+");
|
||||||
private final Path venvPath;
|
private final Path venvPath;
|
||||||
|
private final ProcessRunner processRunner;
|
||||||
private final SubtitleConverter converter;
|
private final SubtitleConverter converter;
|
||||||
private final OS os;
|
private final OS os;
|
||||||
private final Set<SubtitleExtractorListener> listeners;
|
private final Set<SubtitleExtractorListener> listeners;
|
||||||
|
|
||||||
protected AbstractWhisperSubtitleExtractor(final Path venvPath, final SubtitleConverterProvider converterProvider, final OS os) {
|
protected AbstractWhisperSubtitleExtractor(final Path venvPath, final SubtitleConverterProvider converterProvider,
|
||||||
|
final ProcessRunner processRunner, final OS os) {
|
||||||
this.venvPath = requireNonNull(venvPath);
|
this.venvPath = requireNonNull(venvPath);
|
||||||
this.converter = requireNonNull(converterProvider.getConverter("json"));
|
this.converter = requireNonNull(converterProvider.getConverter("json"));
|
||||||
|
this.processRunner = requireNonNull(processRunner);
|
||||||
this.os = requireNonNull(os);
|
this.os = requireNonNull(os);
|
||||||
this.listeners = new HashSet<>();
|
this.listeners = new HashSet<>();
|
||||||
}
|
}
|
||||||
@@ -115,7 +118,7 @@ public abstract class AbstractWhisperSubtitleExtractor extends AbstractProcessRu
|
|||||||
try {
|
try {
|
||||||
final var outputDir = Files.createTempDirectory(AUTOSUBTITLE);
|
final var outputDir = Files.createTempDirectory(AUTOSUBTITLE);
|
||||||
final var args = createArgs(path, language, model, outputDir);
|
final var args = createArgs(path, language, model, outputDir);
|
||||||
final var processListener = startListen(args);
|
final var processListener = processRunner.startListen(args);
|
||||||
var oldProgress = -1.0;
|
var oldProgress = -1.0;
|
||||||
var line = processListener.readLine();
|
var line = processListener.readLine();
|
||||||
while (line != null) {
|
while (line != null) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.github.gtache.autosubtitle.setup.whisperx;
|
package com.github.gtache.autosubtitle.setup.whisperx;
|
||||||
|
|
||||||
|
import com.github.gtache.autosubtitle.process.ProcessRunner;
|
||||||
import com.github.gtache.autosubtitle.setup.SetupException;
|
import com.github.gtache.autosubtitle.setup.SetupException;
|
||||||
import com.github.gtache.autosubtitle.setup.conda.CondaSetupManager;
|
import com.github.gtache.autosubtitle.setup.conda.CondaSetupManager;
|
||||||
import com.github.gtache.autosubtitle.setup.whisper.AbstractWhisperSetupManager;
|
import com.github.gtache.autosubtitle.setup.whisper.AbstractWhisperSetupManager;
|
||||||
@@ -10,6 +11,7 @@ import org.apache.logging.log4j.Logger;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.http.HttpClient;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -23,8 +25,9 @@ public class WhisperXSetupManager extends AbstractWhisperSetupManager {
|
|||||||
private static final Logger logger = LogManager.getLogger(WhisperXSetupManager.class);
|
private static final Logger logger = LogManager.getLogger(WhisperXSetupManager.class);
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
WhisperXSetupManager(final CondaSetupManager condaSetupManager, final WhisperSetupConfiguration configuration) {
|
WhisperXSetupManager(final CondaSetupManager condaSetupManager, final WhisperSetupConfiguration configuration,
|
||||||
super(condaSetupManager, configuration);
|
final ProcessRunner processRunner, final HttpClient httpClient) {
|
||||||
|
super(condaSetupManager, configuration, processRunner, httpClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -37,7 +40,7 @@ public class WhisperXSetupManager extends AbstractWhisperSetupManager {
|
|||||||
final var path = getPythonPath();
|
final var path = getPythonPath();
|
||||||
try {
|
try {
|
||||||
logger.info("Installing whisper");
|
logger.info("Installing whisper");
|
||||||
final var result = run(List.of(path.toString(), "-m", "pip", "install", "-U", "git+https://github.com/m-bain/whisperx.git", "numpy<2"), Duration.ofMinutes(15));
|
final var result = processRunner().run(List.of(path.toString(), "-m", "pip", "install", "-U", "git+https://github.com/m-bain/whisperx.git", "numpy<2"), Duration.ofMinutes(15));
|
||||||
if (result.exitCode() == 0) {
|
if (result.exitCode() == 0) {
|
||||||
logger.info("Whisper installed");
|
logger.info("Whisper installed");
|
||||||
} else {
|
} else {
|
||||||
@@ -53,7 +56,7 @@ public class WhisperXSetupManager extends AbstractWhisperSetupManager {
|
|||||||
final var path = getPythonPath();
|
final var path = getPythonPath();
|
||||||
if (Files.exists(path)) {
|
if (Files.exists(path)) {
|
||||||
try {
|
try {
|
||||||
final var result = run(List.of(path.toString(), "-m", "pip", "show", "whisperx"), Duration.ofSeconds(5));
|
final var result = processRunner().run(List.of(path.toString(), "-m", "pip", "show", "whisperx"), Duration.ofSeconds(5));
|
||||||
return result.exitCode() == 0;
|
return result.exitCode() == 0;
|
||||||
} catch (final IOException e) {
|
} catch (final IOException e) {
|
||||||
throw new SetupException(e);
|
throw new SetupException(e);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.github.gtache.autosubtitle.subtitle.extractor.whisperx;
|
|||||||
import com.github.gtache.autosubtitle.Language;
|
import com.github.gtache.autosubtitle.Language;
|
||||||
import com.github.gtache.autosubtitle.impl.OS;
|
import com.github.gtache.autosubtitle.impl.OS;
|
||||||
import com.github.gtache.autosubtitle.modules.setup.whisper.WhisperVenvPath;
|
import com.github.gtache.autosubtitle.modules.setup.whisper.WhisperVenvPath;
|
||||||
|
import com.github.gtache.autosubtitle.process.ProcessRunner;
|
||||||
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverterProvider;
|
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverterProvider;
|
||||||
import com.github.gtache.autosubtitle.subtitle.extractor.ExtractionModel;
|
import com.github.gtache.autosubtitle.subtitle.extractor.ExtractionModel;
|
||||||
import com.github.gtache.autosubtitle.subtitle.extractor.SubtitleExtractor;
|
import com.github.gtache.autosubtitle.subtitle.extractor.SubtitleExtractor;
|
||||||
@@ -22,8 +23,8 @@ import java.util.List;
|
|||||||
public class WhisperXSubtitleExtractor extends AbstractWhisperSubtitleExtractor {
|
public class WhisperXSubtitleExtractor extends AbstractWhisperSubtitleExtractor {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
WhisperXSubtitleExtractor(@WhisperVenvPath final Path venvPath, final SubtitleConverterProvider converterProvider, final OS os) {
|
WhisperXSubtitleExtractor(@WhisperVenvPath final Path venvPath, final SubtitleConverterProvider converterProvider, final ProcessRunner processRunner, final OS os) {
|
||||||
super(venvPath, converterProvider, os);
|
super(venvPath, converterProvider, processRunner, os);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
Reference in New Issue
Block a user