Extraction works

This commit is contained in:
Guillaume Tâche
2024-08-04 21:55:30 +02:00
parent 8002fc6719
commit 5efdaa6f63
121 changed files with 3360 additions and 400 deletions

View File

@@ -4,7 +4,17 @@ package com.github.gtache.autosubtitle.impl;
* The list of possible operating systems
*/
public enum Architecture {
I386, I486, I586, I686, PPC, POWERPC, X86, X86_32, X86_64, AMD64, ARM, ARM32, ARM64, AARCH64, UNKNOWN;
//32 bit
I386, I486, I586, I686, X86_32,
//PowerPC
PPC, POWERPC,
//64 bit
X86, X86_64, AMD64,
//ARM 32 bit
ARM32, ARM, ARMV1, ARMV2, ARMV3, ARMV4, ARMV5, ARMV6, ARMV7, AARCH32,
//ARM 64 bit
ARM64, ARMV8, ARMV9, AARCH64,
UNKNOWN;
public static Architecture getArchitecture(final String name) {
try {
@@ -13,4 +23,12 @@ public enum Architecture {
return UNKNOWN;
}
}
public boolean isAMD64() {
return this == X86 || this == X86_64 || this == AMD64;
}
public boolean isARM64() {
return this == ARM64 || this == ARMV8 || this == ARMV9 || this == AARCH64;
}
}

View File

@@ -7,14 +7,12 @@ import java.util.Objects;
/**
* Implementation of {@link AudioInfo}
*/
public record AudioInfoImpl(String audioFormat) implements AudioInfo {
public record AudioInfoImpl(String audioFormat, long duration) implements AudioInfo {
public AudioInfoImpl {
Objects.requireNonNull(audioFormat);
}
@Override
public String videoFormat() {
return audioFormat;
if (duration < 0) {
throw new IllegalArgumentException("Duration must be positive");
}
}
}

View File

@@ -1,4 +1,4 @@
package com.github.gtache.autosubtitle.setup.modules.impl;
package com.github.gtache.autosubtitle.modules.setup.impl;
import javax.inject.Qualifier;
import java.lang.annotation.Documented;

View File

@@ -1,4 +1,4 @@
package com.github.gtache.autosubtitle.setup.modules.impl;
package com.github.gtache.autosubtitle.modules.setup.impl;
import javax.inject.Qualifier;
import java.lang.annotation.Documented;

View File

@@ -1,4 +1,4 @@
package com.github.gtache.autosubtitle.setup.modules.impl;
package com.github.gtache.autosubtitle.modules.setup.impl;
import javax.inject.Qualifier;
import java.lang.annotation.Documented;

View File

@@ -1,12 +1,10 @@
package com.github.gtache.autosubtitle.subtitle.modules.impl;
package com.github.gtache.autosubtitle.modules.subtitles.impl;
import com.github.gtache.autosubtitle.subtitle.SubtitleConverter;
import com.github.gtache.autosubtitle.subtitle.impl.SRTSubtitleConverter;
import dagger.Binds;
import dagger.Module;
import javax.inject.Singleton;
/**
* Dagger module for subtitle converter
*/
@@ -14,6 +12,5 @@ import javax.inject.Singleton;
public interface ConverterModule {
@Binds
@Singleton
SubtitleConverter bindsSubtitleConverter(final SRTSubtitleConverter converter);
}

View File

@@ -1,19 +1,15 @@
package com.github.gtache.autosubtitle.process.impl;
import com.github.gtache.autosubtitle.process.ProcessListener;
import com.github.gtache.autosubtitle.process.ProcessResult;
import com.github.gtache.autosubtitle.process.ProcessRunner;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.time.Duration;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
/**
* Base implementation of {@link ProcessRunner}
@@ -24,29 +20,30 @@ public abstract class AbstractProcessRunner implements ProcessRunner {
@Override
public ProcessResult run(final List<String> args) throws IOException {
final var builder = new ProcessBuilder(args);
builder.redirectErrorStream(true);
final var process = builder.start();
final var readFuture = CompletableFuture.supplyAsync(() -> {
final var output = new ArrayList<String>();
try (final var in = new BufferedReader(new InputStreamReader(new BufferedInputStream(process.getInputStream()), StandardCharsets.UTF_8))) {
var line = in.readLine();
final var listener = startListen(args);
CompletableFuture.runAsync(() -> {
try {
var line = listener.readLine();
while (line != null) {
output.add(line);
line = in.readLine();
line = listener.readLine();
}
} catch (final IOException e) {
logger.error("Error listening to process output of {}", args, e);
}
return output;
});
try {
process.waitFor(1, TimeUnit.HOURS);
} catch (final InterruptedException e) {
Thread.currentThread().interrupt();
process.destroy();
}
final var output = readFuture.join();
return new ProcessResultImpl(process.exitValue(), output);
return listener.join(Duration.ofHours(1));
}
@Override
public Process start(final List<String> args) throws IOException {
final var builder = new ProcessBuilder(args);
builder.redirectErrorStream(true);
return builder.start();
}
@Override
public ProcessListener startListen(final List<String> args) throws IOException {
final var process = start(args);
return new ProcessListenerImpl(process);
}
}

View File

@@ -0,0 +1,65 @@
package com.github.gtache.autosubtitle.process.impl;
import com.github.gtache.autosubtitle.process.ProcessListener;
import com.github.gtache.autosubtitle.process.ProcessResult;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
/**
* Implementation of {@link ProcessListener}
*/
public class ProcessListenerImpl implements ProcessListener {
private final Process process;
private final BufferedReader reader;
private final List<String> output;
/**
* Instantiates the listener
*
* @param process The process to listen to
*/
public ProcessListenerImpl(final Process process) {
this.process = Objects.requireNonNull(process);
this.reader = new BufferedReader(new InputStreamReader(new BufferedInputStream(process.getInputStream()), StandardCharsets.UTF_8));
this.output = new ArrayList<>();
}
@Override
public Process process() {
return process;
}
@Override
public String readLine() throws IOException {
final var line = reader.readLine();
if (line != null) {
output.add(line);
}
return line;
}
@Override
public ProcessResult join(final Duration duration) throws IOException {
try {
process.waitFor(duration.getSeconds(), TimeUnit.SECONDS);
} catch (final InterruptedException e) {
Thread.currentThread().interrupt();
process.destroy();
}
if (process.isAlive()) {
process.destroyForcibly();
}
reader.close();
return new ProcessResultImpl(process.exitValue(), output);
}
}

View File

@@ -0,0 +1,166 @@
package com.github.gtache.autosubtitle.setup.impl;
import com.github.gtache.autosubtitle.process.impl.AbstractProcessRunner;
import com.github.gtache.autosubtitle.setup.SetupAction;
import com.github.gtache.autosubtitle.setup.SetupEvent;
import com.github.gtache.autosubtitle.setup.SetupException;
import com.github.gtache.autosubtitle.setup.SetupListener;
import com.github.gtache.autosubtitle.setup.SetupManager;
import com.github.gtache.autosubtitle.setup.SetupStatus;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Base class for all {@link SetupManager} implementations
*/
public abstract class AbstractSetupManager extends AbstractProcessRunner implements SetupManager {
private static final Logger logger = LogManager.getLogger(AbstractSetupManager.class);
private final Set<SetupListener> listeners;
/**
* Instantiates the manager
*/
protected AbstractSetupManager() {
this.listeners = new HashSet<>();
}
@Override
public void addListener(final SetupListener listener) {
listeners.add(listener);
}
@Override
public void removeListener(final SetupListener listener) {
listeners.remove(listener);
}
@Override
public void removeListeners() {
listeners.clear();
}
@Override
public void reinstall() throws SetupException {
sendStartEvent(SetupAction.UNINSTALL, name(), 0);
uninstall();
sendEndEvent(SetupAction.UNINSTALL, name(), -1);
sendStartEvent(SetupAction.INSTALL, name(), -1);
install();
sendEndEvent(SetupAction.INSTALL, name(), 1);
}
@Override
public boolean isInstalled() {
return status() == SetupStatus.SYSTEM_INSTALLED || status() == SetupStatus.BUNDLE_INSTALLED || status() == SetupStatus.UPDATE_AVAILABLE;
}
@Override
public boolean isUpdateAvailable() {
return status() == SetupStatus.UPDATE_AVAILABLE;
}
@Override
public SetupStatus status() {
sendStartEvent(SetupAction.CHECK, name(), 0);
try {
final var status = getStatus();
sendEndEvent(SetupAction.CHECK, name(), 1);
return status;
} catch (final SetupException e) {
logger.error("Error getting status of {}", name(), e);
sendEndEvent(SetupAction.CHECK, name(), 1);
return SetupStatus.ERRORED;
}
}
/**
* @return Retrieves the setup status
* @throws SetupException if an error occurred
*/
protected abstract SetupStatus getStatus() throws SetupException;
/**
* Sends a start event
*
* @param action the action
* @param target the target
* @param progress the progress
*/
protected void sendStartEvent(final SetupAction action, final String target, final double progress) {
sendStartEvent(new SetupEventImpl(action, target, progress, this));
}
/**
* Sends an end event
*
* @param action the action
* @param target the target
* @param progress the progress
*/
protected void sendEndEvent(final SetupAction action, final String target, final double progress) {
sendEndEvent(new SetupEventImpl(action, target, progress, this));
}
/**
* Sends a start event
*
* @param event the event
*/
protected void sendStartEvent(final SetupEvent event) {
listeners.forEach(listener -> listener.onActionStart(event));
}
/**
* Sends an end event
*
* @param event the event
*/
protected void sendEndEvent(final SetupEvent event) {
listeners.forEach(listener -> listener.onActionEnd(event));
}
/**
* Deletes a folder
*
* @param path the path
* @throws SetupException if an error occurred
*/
protected void deleteFolder(final Path path) throws SetupException {
logger.info("Deleting {}", path);
final var index = new AtomicInteger(0);
try (final var files = Files.walk(path)) {
final var total = getFilesCount(path);
files.sorted(Comparator.reverseOrder())
.forEach(f -> {
try {
final var progress = index.get() / (double) total;
sendStartEvent(SetupAction.DELETE, f.toString(), progress);
Files.deleteIfExists(f);
final var newProgress = index.incrementAndGet() / (double) total;
sendEndEvent(SetupAction.DELETE, f.toString(), newProgress);
} catch (final IOException e) {
throw new UncheckedIOException(e);
}
});
} catch (final IOException | UncheckedIOException e) {
throw new SetupException(e);
}
logger.info("{} deleted", path);
}
private static long getFilesCount(final Path path) throws IOException {
try (final var stream = Files.walk(path)) {
return stream.count();
}
}
}

View File

@@ -0,0 +1,20 @@
package com.github.gtache.autosubtitle.setup.impl;
import com.github.gtache.autosubtitle.setup.SetupAction;
import com.github.gtache.autosubtitle.setup.SetupEvent;
import com.github.gtache.autosubtitle.setup.SetupManager;
import java.util.Objects;
/**
* Implementation of {@link SetupEvent}
*/
public record SetupEventImpl(SetupAction action, String target, double progress,
SetupManager setupManager) implements SetupEvent {
public SetupEventImpl {
Objects.requireNonNull(action);
Objects.requireNonNull(target);
Objects.requireNonNull(setupManager);
}
}

View File

@@ -0,0 +1,47 @@
package com.github.gtache.autosubtitle.subtitle.impl;
import com.github.gtache.autosubtitle.subtitle.ExtractEvent;
import com.github.gtache.autosubtitle.subtitle.SubtitleExtractor;
import com.github.gtache.autosubtitle.subtitle.SubtitleExtractorListener;
import java.util.HashSet;
import java.util.Set;
/**
* Base implementation of {@link SubtitleExtractor}
*/
public abstract class AbstractSubtitleExtractor implements SubtitleExtractor {
private final Set<SubtitleExtractorListener> listeners;
/**
* Instantiates the extractor
*/
protected AbstractSubtitleExtractor() {
this.listeners = new HashSet<>();
}
@Override
public void addListener(final SubtitleExtractorListener listener) {
listeners.add(listener);
}
@Override
public void removeListener(final SubtitleExtractorListener listener) {
listeners.remove(listener);
}
@Override
public void removeListeners() {
listeners.clear();
}
/**
* Notifies all listeners
*
* @param event The event
*/
protected void notifyListeners(final ExtractEvent event) {
listeners.forEach(listener -> listener.listen(event));
}
}

View File

@@ -0,0 +1,10 @@
package com.github.gtache.autosubtitle.subtitle.impl;
import com.github.gtache.autosubtitle.subtitle.ExtractEvent;
/**
* Implementation of {@link ExtractEvent}
*/
public record ExtractEventImpl(String message, double progress) implements ExtractEvent {
}

View File

@@ -1,10 +1,14 @@
package com.github.gtache.autosubtitle.subtitle.impl;
import com.github.gtache.autosubtitle.subtitle.Subtitle;
import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
import com.github.gtache.autosubtitle.subtitle.SubtitleConverter;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.Comparator;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
/**
* Converts subtitles to SRT format
@@ -17,7 +21,13 @@ public class SRTSubtitleConverter implements SubtitleConverter {
}
public String convert(final SubtitleCollection collection) {
throw new UnsupportedOperationException("TODO");
final var subtitles = collection.subtitles().stream().sorted(Comparator.comparing(Subtitle::start).thenComparing(Subtitle::end)).toList();
return IntStream.range(0, subtitles.size()).mapToObj(i -> {
final var subtitle = subtitles.get(i);
return (i + 1) + "\n" +
subtitle.start() + " --> " + subtitle.end() + "\n" +
subtitle.content();
}).collect(Collectors.joining("\n\n"));
}
@Override

View File

@@ -1,22 +1,24 @@
package com.github.gtache.autosubtitle.subtitle.impl;
import com.github.gtache.autosubtitle.Language;
import com.github.gtache.autosubtitle.subtitle.Subtitle;
import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import static java.util.Objects.requireNonNull;
/**
* Implementation of {@link SubtitleCollection}
*/
public record SubtitleCollectionImpl(Collection<? extends Subtitle> subtitles,
Locale locale) implements SubtitleCollection {
public record SubtitleCollectionImpl(String text, Collection<? extends Subtitle> subtitles,
Language language) implements SubtitleCollection {
public SubtitleCollectionImpl {
Objects.requireNonNull(text);
subtitles = List.copyOf(subtitles);
requireNonNull(locale);
requireNonNull(language);
}
}

View File

@@ -0,0 +1,25 @@
package com.github.gtache.autosubtitle.subtitle.impl;
import com.github.gtache.autosubtitle.subtitle.Bounds;
import com.github.gtache.autosubtitle.subtitle.Font;
import com.github.gtache.autosubtitle.subtitle.Subtitle;
import java.util.Objects;
/**
* Implementation of {@link Subtitle}
*/
public record SubtitleImpl(String content, long start, long end, Font font, Bounds bounds) implements Subtitle {
public SubtitleImpl {
Objects.requireNonNull(content);
if (start < 0) {
throw new IllegalArgumentException("start must be >= 0 : " + start);
}
if (end < 0) {
throw new IllegalArgumentException("end must be >= 0 : " + end);
}
if (start > end) {
throw new IllegalArgumentException("start must be <= end : " + start + " > " + end);
}
}
}

View File

@@ -8,9 +8,11 @@ module com.github.gtache.autosubtitle.core {
requires org.apache.logging.log4j;
exports com.github.gtache.autosubtitle.impl;
exports com.github.gtache.autosubtitle.modules.impl;
exports com.github.gtache.autosubtitle.process.impl;
exports com.github.gtache.autosubtitle.setup.impl;
exports com.github.gtache.autosubtitle.subtitle.impl;
exports com.github.gtache.autosubtitle.setup.modules.impl;
exports com.github.gtache.autosubtitle.subtitle.modules.impl;
exports com.github.gtache.autosubtitle.modules.impl;
exports com.github.gtache.autosubtitle.modules.setup.impl;
exports com.github.gtache.autosubtitle.modules.subtitles.impl;
}