Allows choosing and managing each tool
This commit is contained in:
@@ -1,11 +1,14 @@
|
||||
package com.github.gtache.autosubtitle.modules.setup.impl;
|
||||
|
||||
import com.github.gtache.autosubtitle.ToolType;
|
||||
import com.github.gtache.autosubtitle.setup.SetupManager;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
||||
import java.net.http.HttpClient;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Dagger core module for setup
|
||||
@@ -32,4 +35,22 @@ public final class SetupModule {
|
||||
static HttpClient providesHttpClient() {
|
||||
return HttpClient.newHttpClient();
|
||||
}
|
||||
|
||||
@Provides
|
||||
static Map<ToolType, Map<String, SetupManager>> providesSetupManagers(@VideoConverterSetup final Map<String, SetupManager> convertersManagers,
|
||||
@SubtitleExtractorSetup final Map<String, SetupManager> extractorsManagers,
|
||||
@TranslatorSetup final Map<String, SetupManager> translatorsManagers) {
|
||||
return Map.of(ToolType.VIDEO_CONVERTER, convertersManagers,
|
||||
ToolType.SUBTITLE_EXTRACTOR, extractorsManagers,
|
||||
ToolType.TRANSLATOR, translatorsManagers);
|
||||
}
|
||||
|
||||
@Provides
|
||||
static Map<ToolType, SetupManager> providesDefaultManagers(@VideoConverterSetup final SetupManager videoConverterManager,
|
||||
@SubtitleExtractorSetup final SetupManager subtitleExtractorManager,
|
||||
@TranslatorSetup final SetupManager translatorManager) {
|
||||
return Map.of(ToolType.VIDEO_CONVERTER, videoConverterManager,
|
||||
ToolType.SUBTITLE_EXTRACTOR, subtitleExtractorManager,
|
||||
ToolType.TRANSLATOR, translatorManager);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.github.gtache.autosubtitle.modules.subtitle.converter.impl;
|
||||
|
||||
import com.github.gtache.autosubtitle.modules.subtitle.converter.json.impl.JSONConverterModule;
|
||||
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverter;
|
||||
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverterProvider;
|
||||
import com.github.gtache.autosubtitle.subtitle.converter.impl.ASSSubtitleConverter;
|
||||
@@ -13,7 +14,7 @@ import dagger.multibindings.StringKey;
|
||||
/**
|
||||
* Dagger module for the subtitle converters
|
||||
*/
|
||||
@Module
|
||||
@Module(includes = JSONConverterModule.class)
|
||||
public abstract class SubtitleConverterModule {
|
||||
|
||||
private SubtitleConverterModule() {
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.github.gtache.autosubtitle.modules.subtitle.converter.json.impl;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverter;
|
||||
import com.github.gtache.autosubtitle.subtitle.converter.json.impl.JSONSubtitleConverter;
|
||||
import dagger.Binds;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import dagger.multibindings.IntoMap;
|
||||
import dagger.multibindings.StringKey;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
/**
|
||||
* Dagger module for the json subtitle converter
|
||||
*/
|
||||
@Module
|
||||
public abstract class JSONConverterModule {
|
||||
|
||||
private JSONConverterModule() {
|
||||
}
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@StringKey("json")
|
||||
abstract SubtitleConverter bindsJsonSubtitleConverter(final JSONSubtitleConverter converter);
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
static ObjectMapper providesObjectMapper() {
|
||||
return new ObjectMapper();
|
||||
}
|
||||
}
|
||||
@@ -23,8 +23,6 @@ import java.util.Objects;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/**
|
||||
* Converts subtitles to SRT format
|
||||
*/
|
||||
@@ -36,11 +34,11 @@ public class ASSSubtitleConverter implements SubtitleConverter<SubtitleImpl> {
|
||||
private static final String EVENTS_SECTION = "[Events]";
|
||||
private static final String STYLES_SECTION = "[V4+ Styles]";
|
||||
private static final Pattern NEWLINE_PATTERN = Pattern.compile("\\n");
|
||||
private final Translator<?> translator;
|
||||
private final Map<String, Translator<?>> translators;
|
||||
|
||||
@Inject
|
||||
ASSSubtitleConverter(final Translator translator) {
|
||||
this.translator = requireNonNull(translator);
|
||||
ASSSubtitleConverter(final Map<String, Translator<?>> translators) {
|
||||
this.translators = Map.copyOf(translators);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -99,7 +97,7 @@ public class ASSSubtitleConverter implements SubtitleConverter<SubtitleImpl> {
|
||||
final var fonts = parseFonts(content, options.defaultFont());
|
||||
final var subtitles = parseSubtitles(content, fonts);
|
||||
final var text = subtitles.stream().map(Subtitle::content).collect(Collectors.joining());
|
||||
final var language = translator.getLanguage(text);
|
||||
final var language = translators.values().iterator().next().getLanguage(text); //Use any translator for now
|
||||
return new SubtitleCollectionImpl<>(text, subtitles, language);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
package com.github.gtache.autosubtitle.subtitle.converter.json.impl;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.github.gtache.autosubtitle.subtitle.Subtitle;
|
||||
import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
|
||||
import com.github.gtache.autosubtitle.subtitle.converter.FormatException;
|
||||
import com.github.gtache.autosubtitle.subtitle.converter.FormatOptions;
|
||||
import com.github.gtache.autosubtitle.subtitle.converter.ParseException;
|
||||
import com.github.gtache.autosubtitle.subtitle.converter.ParseOptions;
|
||||
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverter;
|
||||
import com.github.gtache.autosubtitle.subtitle.impl.SubtitleCollectionImpl;
|
||||
import com.github.gtache.autosubtitle.subtitle.impl.SubtitleImpl;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Comparator;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* {@link SubtitleConverter} implementation for JSON files
|
||||
*/
|
||||
@Singleton
|
||||
public class JSONSubtitleConverter implements SubtitleConverter<SubtitleImpl> {
|
||||
|
||||
private final ObjectMapper mapper;
|
||||
|
||||
@Inject
|
||||
JSONSubtitleConverter(final ObjectMapper mapper) {
|
||||
this.mapper = Objects.requireNonNull(mapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String format(final SubtitleCollection<?> collection, final FormatOptions options) throws FormatException {
|
||||
final var segments = collection.subtitles().stream().map(s -> new JSONSubtitleSegment(s.start(),
|
||||
s.end(), s.content())).toList();
|
||||
final var subtitles = new JSONSubtitles(segments, collection.language());
|
||||
try {
|
||||
return mapper.writeValueAsString(subtitles);
|
||||
} catch (final JsonProcessingException e) {
|
||||
throw new FormatException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubtitleCollectionImpl<SubtitleImpl> parse(final String content, final ParseOptions options) throws ParseException {
|
||||
try {
|
||||
final var json = mapper.readValue(content, JSONSubtitles.class);
|
||||
final var subtitles = json.segments().stream().flatMap(s -> {
|
||||
final var start = s.start();
|
||||
final var end = s.end();
|
||||
return Stream.of(new SubtitleImpl(s.text(), start, end, null, null));
|
||||
}).sorted(Comparator.comparing(Subtitle::start).thenComparing(Subtitle::end)).toList();
|
||||
final var language = json.language();
|
||||
final var subtitlesText = subtitles.stream().map(s -> s.content().trim()).collect(Collectors.joining(" "));
|
||||
return new SubtitleCollectionImpl<>(subtitlesText, subtitles, language);
|
||||
} catch (final Exception e) {
|
||||
throw new ParseException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canParse(final Path file) {
|
||||
return file.getFileName().toString().endsWith(".json");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String formatName() {
|
||||
return "json";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.github.gtache.autosubtitle.subtitle.converter.json.impl;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/**
|
||||
* A subtitle segment as a JSON
|
||||
*
|
||||
* @param start The start in milliseconds
|
||||
* @param end The end in milliseconds
|
||||
* @param text The text
|
||||
*/
|
||||
public record JSONSubtitleSegment(long start, long end, String text) {
|
||||
|
||||
public JSONSubtitleSegment {
|
||||
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);
|
||||
}
|
||||
requireNonNull(text);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.github.gtache.autosubtitle.subtitle.converter.json.impl;
|
||||
|
||||
import com.github.gtache.autosubtitle.Language;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/**
|
||||
* Root json object for subtitles
|
||||
*
|
||||
* @param segments The list of subtitle segments
|
||||
* @param language The subtitles language
|
||||
*/
|
||||
public record JSONSubtitles(List<JSONSubtitleSegment> segments, Language language) {
|
||||
|
||||
public JSONSubtitles {
|
||||
segments = List.copyOf(segments);
|
||||
requireNonNull(language);
|
||||
}
|
||||
}
|
||||
@@ -6,18 +6,22 @@ module com.github.gtache.autosubtitle.core {
|
||||
requires transitive dagger;
|
||||
requires transitive java.net.http;
|
||||
requires transitive javax.inject;
|
||||
requires transitive java.prefs;
|
||||
requires transitive com.fasterxml.jackson.databind;
|
||||
requires org.apache.logging.log4j;
|
||||
requires java.prefs;
|
||||
|
||||
exports com.github.gtache.autosubtitle.impl;
|
||||
exports com.github.gtache.autosubtitle.archive.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.subtitle.converter.impl;
|
||||
exports com.github.gtache.autosubtitle.subtitle.converter.json.impl;
|
||||
exports com.github.gtache.autosubtitle.subtitle.extractor.impl;
|
||||
|
||||
exports com.github.gtache.autosubtitle.modules.impl;
|
||||
exports com.github.gtache.autosubtitle.modules.setup.impl;
|
||||
exports com.github.gtache.autosubtitle.subtitle.extractor.impl;
|
||||
exports com.github.gtache.autosubtitle.subtitle.converter.impl;
|
||||
exports com.github.gtache.autosubtitle.modules.subtitle.impl;
|
||||
exports com.github.gtache.autosubtitle.modules.subtitle.converter.impl;
|
||||
exports com.github.gtache.autosubtitle.modules.subtitle.converter.json.impl;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.github.gtache.autosubtitle.modules.subtitle.converter.json.impl;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
|
||||
|
||||
class TestJSONConverterModule {
|
||||
|
||||
@Test
|
||||
void testProvidesObjectMapper() {
|
||||
assertInstanceOf(ObjectMapper.class, JSONConverterModule.providesObjectMapper());
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.github.gtache.autosubtitle.setup.impl;
|
||||
|
||||
import com.github.gtache.autosubtitle.ToolType;
|
||||
import com.github.gtache.autosubtitle.process.ProcessRunner;
|
||||
import com.github.gtache.autosubtitle.setup.SetupAction;
|
||||
import com.github.gtache.autosubtitle.setup.SetupEvent;
|
||||
@@ -244,6 +245,11 @@ class TestAbstractSetupManager {
|
||||
return "dummy";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ToolType type() {
|
||||
return ToolType.VIDEO_CONVERTER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void install() {
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
@@ -44,7 +45,7 @@ class TestASSSubtitleConverter {
|
||||
this.formatOptions = requireNonNull(formatOptions);
|
||||
this.parseOptions = requireNonNull(parseOptions);
|
||||
this.videoInfo = requireNonNull(videoInfo);
|
||||
this.converter = new ASSSubtitleConverter(translator);
|
||||
this.converter = new ASSSubtitleConverter(Map.of("first", translator));
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
package com.github.gtache.autosubtitle.subtitle.converter.json.impl;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.github.gtache.autosubtitle.Language;
|
||||
import com.github.gtache.autosubtitle.subtitle.Subtitle;
|
||||
import com.github.gtache.autosubtitle.subtitle.converter.FormatException;
|
||||
import com.github.gtache.autosubtitle.subtitle.converter.FormatOptions;
|
||||
import com.github.gtache.autosubtitle.subtitle.converter.ParseException;
|
||||
import com.github.gtache.autosubtitle.subtitle.converter.ParseOptions;
|
||||
import com.github.gtache.autosubtitle.subtitle.impl.SubtitleCollectionImpl;
|
||||
import com.github.gtache.autosubtitle.subtitle.impl.SubtitleImpl;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class TestJSONSubtitleConverter {
|
||||
|
||||
private final ParseOptions parseOptions;
|
||||
private final FormatOptions formatOptions;
|
||||
private final JSONSubtitleConverter converter;
|
||||
|
||||
TestJSONSubtitleConverter(@Mock final ParseOptions parseOptions, @Mock final FormatOptions formatOptions) {
|
||||
this.parseOptions = Objects.requireNonNull(parseOptions);
|
||||
this.formatOptions = Objects.requireNonNull(formatOptions);
|
||||
this.converter = new JSONSubtitleConverter(new ObjectMapper());
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
when(parseOptions.maxLineLength()).thenReturn(100);
|
||||
when(parseOptions.maxLines()).thenReturn(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFormatName() {
|
||||
assertEquals("json", converter.formatName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testParseFormat() throws IOException, ParseException, FormatException {
|
||||
try (final var inStream = getClass().getResourceAsStream("in.json");
|
||||
final var outStream = getClass().getResourceAsStream("out.json")) {
|
||||
if (inStream == null || outStream == null) {
|
||||
throw new IOException("File not found");
|
||||
}
|
||||
final var in = new String(inStream.readAllBytes(), StandardCharsets.UTF_8);
|
||||
final var out = new String(outStream.readAllBytes(), StandardCharsets.UTF_8);
|
||||
final var expected = new SubtitleCollectionImpl<Subtitle>("This is a test. Yes.", List.of(new SubtitleImpl("This is a test.", 9, 410, null, null), new SubtitleImpl("Yes.", 450, 6963, null, null)), Language.FR);
|
||||
assertEquals(expected, converter.parse(in, parseOptions));
|
||||
assertEquals(out, converter.format(expected, formatOptions));
|
||||
}
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource({
|
||||
"test.json,true",
|
||||
".json,true",
|
||||
"abcd.json,true",
|
||||
"abcd.json2,false",
|
||||
"abcd.js,false",
|
||||
"abcd.jso,false",
|
||||
"json,false",
|
||||
})
|
||||
void testCanParse(final String name, final boolean expected) {
|
||||
assertEquals(expected, converter.canParse(Path.of(name)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.github.gtache.autosubtitle.subtitle.converter.json.impl;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
class TestJSONSubtitleSegment {
|
||||
|
||||
private final long start;
|
||||
private final long end;
|
||||
private final String text;
|
||||
private final JSONSubtitleSegment segment;
|
||||
|
||||
TestJSONSubtitleSegment() {
|
||||
this.start = 2;
|
||||
this.end = 4;
|
||||
this.text = "test";
|
||||
this.segment = new JSONSubtitleSegment(start, end, text);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetters() {
|
||||
assertEquals(start, segment.start());
|
||||
assertEquals(end, segment.end());
|
||||
assertEquals(text, segment.text());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIllegal() {
|
||||
assertThrows(NullPointerException.class, () -> new JSONSubtitleSegment(start, end, null));
|
||||
assertThrows(IllegalArgumentException.class, () -> new JSONSubtitleSegment(-1, end, text));
|
||||
assertThrows(IllegalArgumentException.class, () -> new JSONSubtitleSegment(start, -1, text));
|
||||
assertThrows(IllegalArgumentException.class, () -> new JSONSubtitleSegment(end, start, text));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.github.gtache.autosubtitle.subtitle.converter.json.impl;
|
||||
|
||||
import com.github.gtache.autosubtitle.Language;
|
||||
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.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class TestJSONSubtitles {
|
||||
|
||||
private final List<JSONSubtitleSegment> segments;
|
||||
private final Language language;
|
||||
private final JSONSubtitles subtitles;
|
||||
|
||||
TestJSONSubtitles(@Mock final JSONSubtitleSegment segment, @Mock final Language language) {
|
||||
this.segments = List.of(segment);
|
||||
this.language = Objects.requireNonNull(language);
|
||||
this.subtitles = new JSONSubtitles(segments, language);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetters() {
|
||||
assertEquals(segments, subtitles.segments());
|
||||
assertEquals(language, subtitles.language());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIllegal() {
|
||||
assertThrows(NullPointerException.class, () -> new JSONSubtitles(null, language));
|
||||
assertThrows(NullPointerException.class, () -> new JSONSubtitles(segments, null));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"segments": [
|
||||
{
|
||||
"start": 9,
|
||||
"end": 410,
|
||||
"text": "This is a test."
|
||||
},
|
||||
{
|
||||
"start": 450,
|
||||
"end": 6963,
|
||||
"text": "Yes."
|
||||
}
|
||||
],
|
||||
"language": "FR"
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"segments":[{"start":9,"end":410,"text":"This is a test."},{"start":450,"end":6963,"text":"Yes."}],"language":"FR"}
|
||||
Reference in New Issue
Block a user