Injects ProcessRunner to simplify testing, tests conda
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package com.github.gtache.autosubtitle.setup.conda;
|
||||
|
||||
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.SetupStatus;
|
||||
import com.github.gtache.autosubtitle.setup.impl.AbstractSetupManager;
|
||||
@@ -31,8 +32,8 @@ public class CondaSetupManager extends AbstractSetupManager {
|
||||
private final CondaSetupConfiguration configuration;
|
||||
|
||||
@Inject
|
||||
CondaSetupManager(final CondaSetupConfiguration configuration, final HttpClient httpClient) {
|
||||
super(httpClient);
|
||||
CondaSetupManager(final CondaSetupConfiguration configuration, final ProcessRunner processRunner, final HttpClient httpClient) {
|
||||
super(processRunner, httpClient);
|
||||
this.configuration = requireNonNull(configuration);
|
||||
}
|
||||
|
||||
@@ -71,31 +72,28 @@ public class CondaSetupManager extends AbstractSetupManager {
|
||||
switch (configuration.os()) {
|
||||
case OS.WINDOWS -> installWindows();
|
||||
case OS.MAC, OS.LINUX -> installLinux();
|
||||
default -> throw new SetupException("Unsupported OS: " + configuration.os());
|
||||
}
|
||||
}
|
||||
|
||||
private void installLinux() throws SetupException {
|
||||
try {
|
||||
final var installerPath = configuration.condaInstallerPath();
|
||||
final var rootPath = configuration.condaRootPath();
|
||||
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);
|
||||
}
|
||||
installArgs(List.of("bash", installerPath.toString(), "-b", "-p", rootPath.toString()));
|
||||
}
|
||||
|
||||
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 {
|
||||
final var installerPath = configuration.condaInstallerPath();
|
||||
final var rootPath = configuration.condaRootPath();
|
||||
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) {
|
||||
logger.info("Installed conda to {}", rootPath);
|
||||
} else {
|
||||
@@ -111,6 +109,7 @@ public class CondaSetupManager extends AbstractSetupManager {
|
||||
case OS.WINDOWS -> downloadCondaWindows();
|
||||
case OS.MAC -> downloadCondaMac();
|
||||
case OS.LINUX -> downloadCondaLinux();
|
||||
default -> throw new SetupException("Unsupported OS: " + configuration.os());
|
||||
}
|
||||
logger.info("Downloaded conda to {}", configuration.condaInstallerPath());
|
||||
}
|
||||
@@ -154,7 +153,7 @@ public class CondaSetupManager extends AbstractSetupManager {
|
||||
@Override
|
||||
public void update() throws SetupException {
|
||||
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) {
|
||||
logger.info("Conda updated");
|
||||
} 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();
|
||||
try {
|
||||
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) {
|
||||
logger.info("Created venv {}", path);
|
||||
} 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"))) {
|
||||
final var fileList = files.toList();
|
||||
final var targetFolder = path.resolve("DLLs");
|
||||
Files.createDirectories(targetFolder);
|
||||
for (final var s : fileList) {
|
||||
Files.copy(s, targetFolder.resolve(s.getFileName()));
|
||||
}
|
||||
@@ -202,7 +202,7 @@ public class CondaSetupManager extends AbstractSetupManager {
|
||||
|
||||
private boolean isSystemCondaInstalled() throws SetupException {
|
||||
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) {
|
||||
final var output = result.output().getFirst();
|
||||
final var versionString = output.substring(output.indexOf(' ') + 1);
|
||||
@@ -212,7 +212,7 @@ public class CondaSetupManager extends AbstractSetupManager {
|
||||
final var minor = Integer.parseInt(version[1]);
|
||||
return major >= configuration.condaMinimumMajorVersion() && minor >= configuration.condaMinimumMinorVersion();
|
||||
} else {
|
||||
throw new SetupException("Unexpected python version: " + versionString);
|
||||
throw new SetupException("Unexpected conda version: " + versionString);
|
||||
}
|
||||
} else {
|
||||
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.setup.impl.SetupModule;
|
||||
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.Provides;
|
||||
|
||||
@@ -22,6 +25,9 @@ public abstract class CoreModule {
|
||||
|
||||
}
|
||||
|
||||
@Binds
|
||||
abstract ProcessRunner bindsProcessRunner(final ProcessRunnerImpl processRunner);
|
||||
|
||||
@Provides
|
||||
static OS providesOS() {
|
||||
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.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
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
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
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.SetupEvent;
|
||||
import com.github.gtache.autosubtitle.setup.SetupException;
|
||||
@@ -28,27 +28,23 @@ import static java.util.Objects.requireNonNull;
|
||||
/**
|
||||
* 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 final Set<SetupListener> listeners;
|
||||
private final HttpClient httpClient;
|
||||
private final ProcessRunner processRunner;
|
||||
|
||||
/**
|
||||
* Instantiates the manager with a default client
|
||||
*/
|
||||
protected AbstractSetupManager() {
|
||||
this(HttpClient.newHttpClient());
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates the manager with the given client
|
||||
* Instantiates the manager with the given runner and client
|
||||
*
|
||||
* @param processRunner The process runner
|
||||
* @param httpClient The HTTP client to use
|
||||
*/
|
||||
protected AbstractSetupManager(final HttpClient httpClient) {
|
||||
this.listeners = new HashSet<>();
|
||||
protected AbstractSetupManager(final ProcessRunner processRunner, final HttpClient httpClient) {
|
||||
this.processRunner = requireNonNull(processRunner);
|
||||
this.httpClient = requireNonNull(httpClient);
|
||||
this.listeners = new HashSet<>();
|
||||
}
|
||||
|
||||
@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
|
||||
* @throws SetupException if an error occurred
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.github.gtache.autosubtitle.process.impl;
|
||||
|
||||
import com.github.gtache.autosubtitle.impl.OS;
|
||||
import com.github.gtache.autosubtitle.process.ProcessRunner;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
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.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 final DummyProcessRunner dummyProcessRunner;
|
||||
private final ProcessRunner runner;
|
||||
|
||||
TestAbstractProcessRunner() {
|
||||
this.dummyProcessRunner = new DummyProcessRunner();
|
||||
TestProcessRunnerImpl() {
|
||||
this.runner = new ProcessRunnerImpl();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRun() throws IOException {
|
||||
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);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testStart() throws IOException, InterruptedException {
|
||||
final var process = dummyProcessRunner.start(ARGS);
|
||||
final var process = runner.start(ARGS);
|
||||
process.waitFor();
|
||||
assertEquals(0, process.exitValue());
|
||||
final var read = new String(process.getInputStream().readAllBytes());
|
||||
@@ -38,7 +39,7 @@ class TestAbstractProcessRunner {
|
||||
|
||||
@Test
|
||||
void testStartListen() throws IOException {
|
||||
final var listener = dummyProcessRunner.startListen(ARGS);
|
||||
final var listener = runner.startListen(ARGS);
|
||||
assertEquals("1", listener.readLine());
|
||||
assertEquals("2", listener.readLine());
|
||||
assertEquals("3", listener.readLine());
|
||||
@@ -47,15 +48,4 @@ class TestAbstractProcessRunner {
|
||||
assertEquals(0, result.exitCode());
|
||||
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;
|
||||
|
||||
import com.github.gtache.autosubtitle.process.ProcessRunner;
|
||||
import com.github.gtache.autosubtitle.setup.SetupAction;
|
||||
import com.github.gtache.autosubtitle.setup.SetupEvent;
|
||||
import com.github.gtache.autosubtitle.setup.SetupException;
|
||||
@@ -29,18 +30,21 @@ class TestAbstractSetupManager {
|
||||
private final SetupListener listener2;
|
||||
private final SetupEvent event;
|
||||
private final SetupAction setupAction;
|
||||
private final ProcessRunner processRunner;
|
||||
private final HttpClient httpClient;
|
||||
|
||||
TestAbstractSetupManager(@Mock final SetupListener listener1,
|
||||
@Mock final SetupListener listener2,
|
||||
@Mock final SetupEvent event,
|
||||
@Mock final SetupAction setupAction,
|
||||
@Mock final ProcessRunner processRunner,
|
||||
@Mock final HttpClient httpClient) {
|
||||
this.manager = spy(new DummySetupManager(httpClient));
|
||||
this.manager = spy(new DummySetupManager(processRunner, httpClient));
|
||||
this.listener1 = requireNonNull(listener1);
|
||||
this.listener2 = requireNonNull(listener2);
|
||||
this.event = requireNonNull(event);
|
||||
this.setupAction = requireNonNull(setupAction);
|
||||
this.processRunner = requireNonNull(processRunner);
|
||||
this.httpClient = requireNonNull(httpClient);
|
||||
}
|
||||
|
||||
@@ -223,13 +227,13 @@ class TestAbstractSetupManager {
|
||||
|
||||
@Test
|
||||
void testIllegal() {
|
||||
assertThrows(NullPointerException.class, () -> new DummySetupManager(null));
|
||||
assertThrows(NullPointerException.class, () -> new DummySetupManager(null, null));
|
||||
}
|
||||
|
||||
private static final class DummySetupManager extends AbstractSetupManager {
|
||||
|
||||
private DummySetupManager(final HttpClient httpClient) {
|
||||
super(httpClient);
|
||||
private DummySetupManager(final ProcessRunner processRunner, final HttpClient httpClient) {
|
||||
super(processRunner, httpClient);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
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.SetupManager;
|
||||
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 javax.inject.Inject;
|
||||
import java.net.http.HttpClient;
|
||||
import java.util.Objects;
|
||||
import java.util.prefs.BackingStoreException;
|
||||
import java.util.prefs.Preferences;
|
||||
@@ -22,7 +24,9 @@ public class DeepLSetupManager extends AbstractSetupManager {
|
||||
private final Preferences preferences;
|
||||
|
||||
@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.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.modules.setup.ffmpeg.FFmpegBundledPath;
|
||||
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.converter.SubtitleConverter;
|
||||
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 java.io.IOException;
|
||||
@@ -34,21 +36,24 @@ import static java.util.Objects.requireNonNull;
|
||||
/**
|
||||
* 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 final Path bundledPath;
|
||||
private final Path systemPath;
|
||||
private final SubtitleConverterProvider converterProvider;
|
||||
private final Preferences preferences;
|
||||
private final ProcessRunner processRunner;
|
||||
|
||||
@Inject
|
||||
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.systemPath = requireNonNull(systemPath);
|
||||
this.converterProvider = requireNonNull(converterProvider);
|
||||
this.preferences = requireNonNull(preferences);
|
||||
this.processRunner = requireNonNull(processRunner);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -197,4 +202,15 @@ public class FFmpegVideoConverter extends AbstractProcessRunner implements Video
|
||||
private String getFFmpegPath() {
|
||||
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.modules.setup.ffmpeg.FFprobeBundledPath;
|
||||
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 java.io.IOException;
|
||||
@@ -20,20 +20,23 @@ import static java.util.Objects.requireNonNull;
|
||||
/**
|
||||
* FFprobe implementation of {@link VideoLoader}
|
||||
*/
|
||||
public class FFprobeVideoLoader extends AbstractProcessRunner implements VideoLoader {
|
||||
public class FFprobeVideoLoader implements VideoLoader {
|
||||
|
||||
private final Path bundledPath;
|
||||
private final Path systemPath;
|
||||
private final ProcessRunner processRunner;
|
||||
|
||||
@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.systemPath = requireNonNull(systemPath);
|
||||
this.processRunner = requireNonNull(processRunner);
|
||||
}
|
||||
|
||||
@Override
|
||||
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 split = resolution.split(",");
|
||||
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.impl.Architecture;
|
||||
import com.github.gtache.autosubtitle.process.ProcessRunner;
|
||||
import com.github.gtache.autosubtitle.setup.SetupException;
|
||||
import com.github.gtache.autosubtitle.setup.SetupManager;
|
||||
import com.github.gtache.autosubtitle.setup.SetupStatus;
|
||||
@@ -32,8 +33,8 @@ public class FFmpegSetupManager extends AbstractSetupManager {
|
||||
|
||||
@Inject
|
||||
FFmpegSetupManager(final FFmpegSetupConfiguration configuration, final ArchiverProvider archiverProvider,
|
||||
final HttpClient httpClient) {
|
||||
super(httpClient);
|
||||
final ProcessRunner processRunner, final HttpClient httpClient) {
|
||||
super(processRunner, httpClient);
|
||||
this.configuration = requireNonNull(configuration);
|
||||
this.archiverProvider = requireNonNull(archiverProvider);
|
||||
}
|
||||
@@ -190,7 +191,7 @@ public class FFmpegSetupManager extends AbstractSetupManager {
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.github.gtache.autosubtitle.ffmpeg;
|
||||
import com.github.gtache.autosubtitle.Video;
|
||||
import com.github.gtache.autosubtitle.VideoInfo;
|
||||
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.converter.SubtitleConverter;
|
||||
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverterProvider;
|
||||
@@ -24,6 +25,7 @@ import static org.mockito.Mockito.when;
|
||||
class TestFFmpegVideoConverter {
|
||||
|
||||
private final FFmpegVideoConverter converter;
|
||||
private final ProcessRunner runner;
|
||||
private final SubtitleConverter subtitleConverter;
|
||||
private final SubtitleConverterProvider subtitleConverterProvider;
|
||||
private final Video video;
|
||||
@@ -33,11 +35,12 @@ class TestFFmpegVideoConverter {
|
||||
private final SubtitleCollection<?> collection;
|
||||
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 {
|
||||
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";
|
||||
this.video = Objects.requireNonNull(video);
|
||||
this.runner = Objects.requireNonNull(runner);
|
||||
this.videoInfo = Objects.requireNonNull(videoInfo);
|
||||
when(video.info()).thenReturn(videoInfo);
|
||||
this.tmpFile = Files.createTempFile("fake-ffmpeg", resource.substring(resource.lastIndexOf('.')));
|
||||
@@ -48,7 +51,7 @@ class TestFFmpegVideoConverter {
|
||||
this.subtitleConverter = Objects.requireNonNull(subtitleConverter);
|
||||
this.subtitleConverterProvider = Objects.requireNonNull(subtitleConverterProvider);
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,25 +3,32 @@ package com.github.gtache.autosubtitle.ffmpeg;
|
||||
import com.github.gtache.autosubtitle.impl.FileVideoImpl;
|
||||
import com.github.gtache.autosubtitle.impl.OS;
|
||||
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.Disabled;
|
||||
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.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.Objects;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class TestFFmpegVideoLoader {
|
||||
|
||||
private final FFprobeVideoLoader loader;
|
||||
private final ProcessRunner runner;
|
||||
private final Path tmpFile;
|
||||
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 resource = OS.getOS() == OS.WINDOWS ? "fake-ffprobe.exe" : "fake-ffprobe.sh";
|
||||
this.tmpFile = Files.createTempFile("fake-ffprobe", resource.substring(resource.lastIndexOf('.')));
|
||||
@@ -29,7 +36,8 @@ class TestFFmpegVideoLoader {
|
||||
Files.copy(in, tmpFile, StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
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
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
subtitles.add.prompt.label=Enter text here...
|
||||
subtitles.button.load.label=Load subtitles...
|
||||
subtitles.button.reset.label=Reset 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.reset.label=R\u00E9initialiser 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.scene.control.Button;
|
||||
import javafx.scene.control.ComboBox;
|
||||
import javafx.scene.control.SelectionMode;
|
||||
import javafx.scene.control.Tab;
|
||||
import javafx.scene.control.TabPane;
|
||||
import javafx.scene.control.TableColumn;
|
||||
@@ -165,6 +166,8 @@ public class FXSubtitlesController extends AbstractFXController implements Subti
|
||||
|
||||
private void bindTable() {
|
||||
subtitlesTable.setItems(model.selectedSubtitles());
|
||||
subtitlesTable.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
|
||||
|
||||
subtitlesTable.setOnKeyPressed(e -> {
|
||||
if (e.getCode().isLetterKey() || e.getCode().isDigitKey()) {
|
||||
editFocusedCell();
|
||||
@@ -180,6 +183,9 @@ public class FXSubtitlesController extends AbstractFXController implements Subti
|
||||
e.consume();
|
||||
}
|
||||
});
|
||||
subtitlesTable.setOnContextMenuRequested(e -> {
|
||||
//TODO menu with copy, delete
|
||||
});
|
||||
startColumn.setCellFactory(TextFieldTableCell.forTableColumn(new TimeStringConverter(timeFormatter)));
|
||||
startColumn.setCellValueFactory(param -> new SimpleObjectProperty<>(param.getValue() == null ? null : param.getValue().start()));
|
||||
startColumn.setOnEditCommit(e -> {
|
||||
@@ -242,7 +248,7 @@ public class FXSubtitlesController extends AbstractFXController implements Subti
|
||||
|
||||
@FXML
|
||||
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
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
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.conda.CondaSetupManager;
|
||||
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.Singleton;
|
||||
import java.io.IOException;
|
||||
import java.net.http.HttpClient;
|
||||
import java.nio.file.Files;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
@@ -23,8 +25,9 @@ public class WhisperSetupManager extends AbstractWhisperSetupManager {
|
||||
private static final Logger logger = LogManager.getLogger(WhisperSetupManager.class);
|
||||
|
||||
@Inject
|
||||
WhisperSetupManager(final CondaSetupManager condaSetupManager, final WhisperSetupConfiguration configuration) {
|
||||
super(condaSetupManager, configuration);
|
||||
WhisperSetupManager(final CondaSetupManager condaSetupManager, final WhisperSetupConfiguration configuration,
|
||||
final ProcessRunner processRunner, final HttpClient httpClient) {
|
||||
super(condaSetupManager, configuration, processRunner, httpClient);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -37,7 +40,7 @@ public class WhisperSetupManager extends AbstractWhisperSetupManager {
|
||||
final var path = getPythonPath();
|
||||
try {
|
||||
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) {
|
||||
logger.info("Whisper installed");
|
||||
} else {
|
||||
@@ -53,7 +56,7 @@ public class WhisperSetupManager extends AbstractWhisperSetupManager {
|
||||
final var path = getPythonPath();
|
||||
if (Files.exists(path)) {
|
||||
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;
|
||||
} catch (final IOException 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.impl.OS;
|
||||
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.extractor.ExtractionModel;
|
||||
import com.github.gtache.autosubtitle.subtitle.extractor.SubtitleExtractor;
|
||||
@@ -23,8 +24,9 @@ public class WhisperSubtitleExtractor extends AbstractWhisperSubtitleExtractor {
|
||||
|
||||
|
||||
@Inject
|
||||
WhisperSubtitleExtractor(@WhisperVenvPath final Path venvPath, final SubtitleConverterProvider converterProvider, final OS os) {
|
||||
super(venvPath, converterProvider, os);
|
||||
WhisperSubtitleExtractor(@WhisperVenvPath final Path venvPath, final SubtitleConverterProvider converterProvider,
|
||||
final ProcessRunner processRunner, final OS os) {
|
||||
super(venvPath, converterProvider, processRunner, os);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.github.gtache.autosubtitle.setup.whisper;
|
||||
|
||||
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.SetupException;
|
||||
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.Logger;
|
||||
|
||||
import java.net.http.HttpClient;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
@@ -26,7 +28,9 @@ public abstract class AbstractWhisperSetupManager extends AbstractSetupManager {
|
||||
private final CondaSetupManager condaSetupManager;
|
||||
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.configuration = requireNonNull(configuration);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import com.github.gtache.autosubtitle.File;
|
||||
import com.github.gtache.autosubtitle.Language;
|
||||
import com.github.gtache.autosubtitle.Video;
|
||||
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.SubtitleCollection;
|
||||
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
|
||||
*/
|
||||
public abstract class AbstractWhisperSubtitleExtractor extends AbstractProcessRunner implements SubtitleExtractor {
|
||||
public abstract class AbstractWhisperSubtitleExtractor implements SubtitleExtractor<Subtitle> {
|
||||
|
||||
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 TQDM_PROGRESS_PATTERN = Pattern.compile("^(?<progress>\\d{1,3})%\\|.+");
|
||||
private final Path venvPath;
|
||||
private final ProcessRunner processRunner;
|
||||
private final SubtitleConverter converter;
|
||||
private final OS os;
|
||||
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.converter = requireNonNull(converterProvider.getConverter("json"));
|
||||
this.processRunner = requireNonNull(processRunner);
|
||||
this.os = requireNonNull(os);
|
||||
this.listeners = new HashSet<>();
|
||||
}
|
||||
@@ -115,7 +118,7 @@ public abstract class AbstractWhisperSubtitleExtractor extends AbstractProcessRu
|
||||
try {
|
||||
final var outputDir = Files.createTempDirectory(AUTOSUBTITLE);
|
||||
final var args = createArgs(path, language, model, outputDir);
|
||||
final var processListener = startListen(args);
|
||||
final var processListener = processRunner.startListen(args);
|
||||
var oldProgress = -1.0;
|
||||
var line = processListener.readLine();
|
||||
while (line != null) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
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.conda.CondaSetupManager;
|
||||
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.Singleton;
|
||||
import java.io.IOException;
|
||||
import java.net.http.HttpClient;
|
||||
import java.nio.file.Files;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
@@ -23,8 +25,9 @@ public class WhisperXSetupManager extends AbstractWhisperSetupManager {
|
||||
private static final Logger logger = LogManager.getLogger(WhisperXSetupManager.class);
|
||||
|
||||
@Inject
|
||||
WhisperXSetupManager(final CondaSetupManager condaSetupManager, final WhisperSetupConfiguration configuration) {
|
||||
super(condaSetupManager, configuration);
|
||||
WhisperXSetupManager(final CondaSetupManager condaSetupManager, final WhisperSetupConfiguration configuration,
|
||||
final ProcessRunner processRunner, final HttpClient httpClient) {
|
||||
super(condaSetupManager, configuration, processRunner, httpClient);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -37,7 +40,7 @@ public class WhisperXSetupManager extends AbstractWhisperSetupManager {
|
||||
final var path = getPythonPath();
|
||||
try {
|
||||
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) {
|
||||
logger.info("Whisper installed");
|
||||
} else {
|
||||
@@ -53,7 +56,7 @@ public class WhisperXSetupManager extends AbstractWhisperSetupManager {
|
||||
final var path = getPythonPath();
|
||||
if (Files.exists(path)) {
|
||||
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;
|
||||
} catch (final IOException 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.impl.OS;
|
||||
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.extractor.ExtractionModel;
|
||||
import com.github.gtache.autosubtitle.subtitle.extractor.SubtitleExtractor;
|
||||
@@ -22,8 +23,8 @@ import java.util.List;
|
||||
public class WhisperXSubtitleExtractor extends AbstractWhisperSubtitleExtractor {
|
||||
|
||||
@Inject
|
||||
WhisperXSubtitleExtractor(@WhisperVenvPath final Path venvPath, final SubtitleConverterProvider converterProvider, final OS os) {
|
||||
super(venvPath, converterProvider, os);
|
||||
WhisperXSubtitleExtractor(@WhisperVenvPath final Path venvPath, final SubtitleConverterProvider converterProvider, final ProcessRunner processRunner, final OS os) {
|
||||
super(venvPath, converterProvider, processRunner, os);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user