SRT works ; adds support for export/import ass
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
package com.github.gtache.autosubtitle.archive;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Provider of {@link Archiver}s
|
||||
*/
|
||||
public interface ArchiverProvider {
|
||||
|
||||
/**
|
||||
* @return the list of all available archivers
|
||||
*/
|
||||
Collection<Archiver> allArchivers();
|
||||
|
||||
/**
|
||||
* Returns the archiver for the given extension
|
||||
*
|
||||
* @param extension The extension
|
||||
* @return The archiver (or null if not found)
|
||||
*/
|
||||
Archiver getArchiver(String extension);
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.github.gtache.autosubtitle.subtitle;
|
||||
|
||||
import com.github.gtache.autosubtitle.Language;
|
||||
import com.github.gtache.autosubtitle.VideoInfo;
|
||||
import com.github.gtache.autosubtitle.subtitle.converter.ParseException;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -28,20 +29,22 @@ public interface SubtitleImporterExporter<T extends Subtitle> {
|
||||
* Exports multiple collections to a file
|
||||
*
|
||||
* @param collections The subtitle collections
|
||||
* @param videoInfo The video info (e.g. ASS format uses it)
|
||||
* @param file The path to the file
|
||||
* @throws IOException If an error occurred
|
||||
*/
|
||||
void exportSubtitles(final Collection<? extends SubtitleCollection<?>> collections, final Path file) throws IOException;
|
||||
void exportSubtitles(final Collection<? extends SubtitleCollection<?>> collections, final VideoInfo videoInfo, final Path file) throws IOException;
|
||||
|
||||
/**
|
||||
* Exports a single collection to a file
|
||||
*
|
||||
* @param collection The subtitle collection
|
||||
* @param videoInfo The video info (e.g. ASS format uses it)
|
||||
* @param file The path to the file
|
||||
* @throws IOException If an error occurred
|
||||
*/
|
||||
default void exportSubtitles(final SubtitleCollection<?> collection, final Path file) throws IOException {
|
||||
exportSubtitles(List.of(collection), file);
|
||||
default void exportSubtitles(final SubtitleCollection<?> collection, final VideoInfo videoInfo, final Path file) throws IOException {
|
||||
exportSubtitles(List.of(collection), videoInfo, file);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.github.gtache.autosubtitle.subtitle.converter;
|
||||
|
||||
import com.github.gtache.autosubtitle.VideoInfo;
|
||||
import com.github.gtache.autosubtitle.subtitle.Subtitle;
|
||||
import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
|
||||
|
||||
@@ -16,9 +17,10 @@ public interface SubtitleConverter<T extends Subtitle> {
|
||||
* Converts the subtitle collection
|
||||
*
|
||||
* @param collection The collection
|
||||
* @param videoInfo The video info (e.g. ASS format uses it)
|
||||
* @return The converted subtitles as the content of a file
|
||||
*/
|
||||
String format(final SubtitleCollection<?> collection);
|
||||
String format(final SubtitleCollection<?> collection, final VideoInfo videoInfo);
|
||||
|
||||
/**
|
||||
* Parses a subtitle collection
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.github.gtache.autosubtitle.subtitle.converter;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Provider of {@link SubtitleConverter}s
|
||||
*/
|
||||
public interface SubtitleConverterProvider {
|
||||
|
||||
/**
|
||||
* @return the list of all available converters
|
||||
*/
|
||||
Collection<SubtitleConverter<?>> allConverters();
|
||||
|
||||
/**
|
||||
* Returns the converter for the given format
|
||||
*
|
||||
* @param format The format
|
||||
* @return The converter (or null if not found)
|
||||
*/
|
||||
SubtitleConverter<?> getConverter(final String format);
|
||||
}
|
||||
@@ -4,15 +4,14 @@ import com.github.gtache.autosubtitle.modules.deepl.DeepLModule;
|
||||
import com.github.gtache.autosubtitle.modules.ffmpeg.FFmpegModule;
|
||||
import com.github.gtache.autosubtitle.modules.impl.CoreModule;
|
||||
import com.github.gtache.autosubtitle.modules.whisperx.WhisperXModule;
|
||||
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverter;
|
||||
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverterProvider;
|
||||
import dagger.Component;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
import java.util.Map;
|
||||
|
||||
@Component(modules = {CoreModule.class, DeepLModule.class, FFmpegModule.class, WhisperXModule.class})
|
||||
@Singleton
|
||||
public interface CliComponent {
|
||||
|
||||
Map<String, SubtitleConverter> getSubtitleConverters();
|
||||
SubtitleConverterProvider getSubtitleConverterProvider();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.github.gtache.autosubtitle.archive.impl;
|
||||
|
||||
|
||||
import com.github.gtache.autosubtitle.archive.Archiver;
|
||||
import com.github.gtache.autosubtitle.archive.ArchiverProvider;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Implementation of {@link ArchiverProvider}
|
||||
*/
|
||||
public class ArchiverProviderImpl implements ArchiverProvider {
|
||||
|
||||
private final Map<String, Archiver> archiverMap;
|
||||
|
||||
@Inject
|
||||
ArchiverProviderImpl(final Map<String, Archiver> archiverMap) {
|
||||
this.archiverMap = archiverMap.entrySet().stream().collect(Collectors.toMap(e -> e.getKey().toUpperCase(), Map.Entry::getValue));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Archiver> allArchivers() {
|
||||
return archiverMap.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Archiver getArchiver(final String extension) {
|
||||
return archiverMap.get(extension.toUpperCase());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.github.gtache.autosubtitle.modules.archive.impl;
|
||||
|
||||
import com.github.gtache.autosubtitle.archive.Archiver;
|
||||
import com.github.gtache.autosubtitle.archive.ArchiverProvider;
|
||||
import com.github.gtache.autosubtitle.archive.impl.ArchiverProviderImpl;
|
||||
import com.github.gtache.autosubtitle.archive.impl.ZipDecompresser;
|
||||
import dagger.Binds;
|
||||
import dagger.Module;
|
||||
import dagger.multibindings.IntoMap;
|
||||
import dagger.multibindings.StringKey;
|
||||
|
||||
/**
|
||||
* Dagger module for Archivers
|
||||
*/
|
||||
@Module
|
||||
public abstract class ArchiveModule {
|
||||
|
||||
private ArchiveModule() {
|
||||
|
||||
}
|
||||
|
||||
@Binds
|
||||
abstract ArchiverProvider bindsArchiverProvider(ArchiverProviderImpl provider);
|
||||
|
||||
@Binds
|
||||
@StringKey("zip")
|
||||
@IntoMap
|
||||
abstract Archiver bindsZipDecompresser(final ZipDecompresser decompresser);
|
||||
}
|
||||
@@ -1,17 +1,13 @@
|
||||
package com.github.gtache.autosubtitle.modules.impl;
|
||||
|
||||
import com.github.gtache.autosubtitle.archive.Archiver;
|
||||
import com.github.gtache.autosubtitle.archive.impl.ZipDecompresser;
|
||||
import com.github.gtache.autosubtitle.impl.Architecture;
|
||||
import com.github.gtache.autosubtitle.impl.DaggerException;
|
||||
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 dagger.Binds;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import dagger.multibindings.IntoMap;
|
||||
import dagger.multibindings.StringKey;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
import java.util.prefs.Preferences;
|
||||
@@ -19,18 +15,13 @@ import java.util.prefs.Preferences;
|
||||
/**
|
||||
* Dagger module for Core
|
||||
*/
|
||||
@Module(includes = {SetupModule.class, SubtitleModule.class})
|
||||
@Module(includes = {ArchiveModule.class, SetupModule.class, SubtitleModule.class})
|
||||
public abstract class CoreModule {
|
||||
|
||||
private CoreModule() {
|
||||
|
||||
}
|
||||
|
||||
@Binds
|
||||
@StringKey("zip")
|
||||
@IntoMap
|
||||
abstract Archiver bindsZipDecompresser(final ZipDecompresser decompresser);
|
||||
|
||||
@Provides
|
||||
static OS providesOS() {
|
||||
final var os = OS.getOS();
|
||||
@@ -74,4 +65,16 @@ public abstract class CoreModule {
|
||||
static int providesMaxLines() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FontName
|
||||
static String providesFontName() {
|
||||
return "Arial";
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FontSize
|
||||
static int providesFontSize() {
|
||||
return 12;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.github.gtache.autosubtitle.modules.gui.impl;
|
||||
package com.github.gtache.autosubtitle.modules.impl;
|
||||
|
||||
import javax.inject.Qualifier;
|
||||
import java.lang.annotation.Documented;
|
||||
@@ -12,5 +12,5 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
@Documented
|
||||
@Retention(RUNTIME)
|
||||
@Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD})
|
||||
public @interface FontFamily {
|
||||
public @interface FontName {
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.github.gtache.autosubtitle.modules.gui.impl;
|
||||
package com.github.gtache.autosubtitle.modules.impl;
|
||||
|
||||
import javax.inject.Qualifier;
|
||||
import java.lang.annotation.Documented;
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.github.gtache.autosubtitle.modules.subtitle.converter.impl;
|
||||
|
||||
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;
|
||||
import com.github.gtache.autosubtitle.subtitle.converter.impl.SRTSubtitleConverter;
|
||||
import com.github.gtache.autosubtitle.subtitle.converter.impl.SubtitleConverterProviderImpl;
|
||||
import dagger.Binds;
|
||||
import dagger.Module;
|
||||
import dagger.multibindings.IntoMap;
|
||||
import dagger.multibindings.StringKey;
|
||||
|
||||
/**
|
||||
* Dagger module for the subtitle converters
|
||||
*/
|
||||
@Module
|
||||
public abstract class SubtitleConverterModule {
|
||||
|
||||
private SubtitleConverterModule() {
|
||||
}
|
||||
|
||||
@Binds
|
||||
abstract SubtitleConverterProvider bindsSubtitleConverterProvider(final SubtitleConverterProviderImpl subtitleConverterProvider);
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@StringKey("srt")
|
||||
abstract SubtitleConverter bindsSrtSubtitleConverter(final SRTSubtitleConverter converter);
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@StringKey("ass")
|
||||
abstract SubtitleConverter bindsAssSubtitleConverter(final ASSSubtitleConverter converter);
|
||||
}
|
||||
@@ -1,28 +1,20 @@
|
||||
package com.github.gtache.autosubtitle.modules.subtitle.impl;
|
||||
|
||||
import com.github.gtache.autosubtitle.modules.subtitle.converter.impl.SubtitleConverterModule;
|
||||
import com.github.gtache.autosubtitle.subtitle.SubtitleImporterExporter;
|
||||
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverter;
|
||||
import com.github.gtache.autosubtitle.subtitle.converter.impl.SRTSubtitleConverter;
|
||||
import com.github.gtache.autosubtitle.subtitle.impl.SubtitleImporterExporterImpl;
|
||||
import dagger.Binds;
|
||||
import dagger.Module;
|
||||
import dagger.multibindings.IntoMap;
|
||||
import dagger.multibindings.StringKey;
|
||||
|
||||
/**
|
||||
* Dagger module for subtitles
|
||||
*/
|
||||
@Module
|
||||
@Module(includes = {SubtitleConverterModule.class})
|
||||
public abstract class SubtitleModule {
|
||||
|
||||
private SubtitleModule() {
|
||||
}
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@StringKey("srt")
|
||||
abstract SubtitleConverter bindsSubtitleConverter(final SRTSubtitleConverter converter);
|
||||
|
||||
@Binds
|
||||
abstract SubtitleImporterExporter bindsSubtitleImporterExporter(final SubtitleImporterExporterImpl impl);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
package com.github.gtache.autosubtitle.subtitle.converter.impl;
|
||||
|
||||
import com.github.gtache.autosubtitle.VideoInfo;
|
||||
import com.github.gtache.autosubtitle.modules.impl.FontName;
|
||||
import com.github.gtache.autosubtitle.modules.impl.FontSize;
|
||||
import com.github.gtache.autosubtitle.subtitle.Font;
|
||||
import com.github.gtache.autosubtitle.subtitle.Subtitle;
|
||||
import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
|
||||
import com.github.gtache.autosubtitle.subtitle.converter.ParseException;
|
||||
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverter;
|
||||
import com.github.gtache.autosubtitle.subtitle.impl.FontImpl;
|
||||
import com.github.gtache.autosubtitle.subtitle.impl.SubtitleCollectionImpl;
|
||||
import com.github.gtache.autosubtitle.subtitle.impl.SubtitleImpl;
|
||||
import com.github.gtache.autosubtitle.translation.Translator;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.prefs.Preferences;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/**
|
||||
* Converts subtitles to SRT format
|
||||
*/
|
||||
public class ASSSubtitleConverter implements SubtitleConverter<SubtitleImpl> {
|
||||
|
||||
private static final String STYLE = "Style:";
|
||||
private static final String FORMAT = "Format:";
|
||||
private static final String DIALOGUE = "Dialogue:";
|
||||
private static final String EVENTS_SECTION = "[Events]";
|
||||
private static final String STYLES_SECTION = "[V4+ Styles]";
|
||||
private final Translator<?> translator;
|
||||
private final Preferences preferences;
|
||||
private final String defaultFontName;
|
||||
private final int defaultFontSize;
|
||||
|
||||
@Inject
|
||||
ASSSubtitleConverter(final Translator translator, final Preferences preferences,
|
||||
@FontName final String defaultFontName, @FontSize final int defaultFontSize) {
|
||||
this.translator = requireNonNull(translator);
|
||||
this.preferences = requireNonNull(preferences);
|
||||
this.defaultFontName = Objects.requireNonNull(defaultFontName);
|
||||
this.defaultFontSize = defaultFontSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String format(final SubtitleCollection<?> collection, final VideoInfo videoInfo) {
|
||||
final var subtitles = collection.subtitles().stream().sorted(Comparator.comparing(Subtitle::start).thenComparing(Subtitle::end)).toList();
|
||||
final var scriptInfo = getScriptInfo(videoInfo);
|
||||
final var styles = getStyles(subtitles);
|
||||
final var events = getEvents(subtitles);
|
||||
return scriptInfo + "\n\n" + styles + "\n\n" + events;
|
||||
}
|
||||
|
||||
private String getEvents(final Collection<? extends Subtitle> subtitles) {
|
||||
return EVENTS_SECTION + "\n" + "Format: Start, End, Style, Text\n" +
|
||||
subtitles.stream().map(this::getEvent).collect(Collectors.joining("\n"));
|
||||
}
|
||||
|
||||
private String getEvent(final Subtitle subtitle) {
|
||||
return DIALOGUE + " " + formatTime(subtitle.start()) + "," + formatTime(subtitle.end()) + "," + getName(subtitle.font()) + "," + subtitle.content();
|
||||
}
|
||||
|
||||
private String getName(final Font font) {
|
||||
final String fontName;
|
||||
final int fontSize;
|
||||
if (font == null) {
|
||||
fontName = preferences.get("fontName", defaultFontName);
|
||||
fontSize = preferences.getInt("fontSize", defaultFontSize);
|
||||
} else {
|
||||
fontName = font.name();
|
||||
fontSize = font.size();
|
||||
}
|
||||
return fontName + fontSize;
|
||||
}
|
||||
|
||||
private String getStyles(final Collection<? extends Subtitle> subtitles) {
|
||||
return STYLES_SECTION + "\n" + "Format: Name, Fontname, Fontsize\n" + listStyles(subtitles);
|
||||
}
|
||||
|
||||
private String listStyles(final Collection<? extends Subtitle> subtitles) {
|
||||
final var uniqueStyles = subtitles.stream().map(Subtitle::font).filter(Objects::nonNull).collect(Collectors.toSet());
|
||||
if (subtitles.stream().anyMatch(s -> s.font() == null)) {
|
||||
uniqueStyles.add(new FontImpl(preferences.get("fontName", defaultFontName), preferences.getInt("fontSize", defaultFontSize)));
|
||||
}
|
||||
return uniqueStyles.stream().map(f -> STYLE + " " + getName(f) + ", " + f.name() + ", " + f.size()).collect(Collectors.joining("\n"));
|
||||
}
|
||||
|
||||
private static String getScriptInfo(final VideoInfo videoInfo) {
|
||||
return """
|
||||
[Script Info]
|
||||
PlayResX: %d
|
||||
PlayResY: %d
|
||||
WrapStyle: 1""".formatted(videoInfo.width(), videoInfo.height());
|
||||
}
|
||||
|
||||
private static String formatTime(final long time) {
|
||||
final var millisPerHour = 3600000;
|
||||
final var millisPerMinute = 60000;
|
||||
final var hours = time / millisPerHour;
|
||||
final var minutes = (time - hours * millisPerHour) / millisPerMinute;
|
||||
final var seconds = (time - hours * millisPerHour - minutes * millisPerMinute) / 1000;
|
||||
final var millis = time - hours * millisPerHour - minutes * millisPerMinute - seconds * 1000;
|
||||
final var hundredths = millis / 10;
|
||||
return String.format("%d:%02d:%02d.%02d", hours, minutes, seconds, hundredths);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubtitleCollectionImpl<SubtitleImpl> parse(final String content) throws ParseException {
|
||||
final var fonts = parseFonts(content);
|
||||
final var subtitles = parseSubtitles(content, fonts);
|
||||
final var text = subtitles.stream().map(Subtitle::content).collect(Collectors.joining());
|
||||
final var language = translator.getLanguage(text);
|
||||
return new SubtitleCollectionImpl<>(text, subtitles, language);
|
||||
}
|
||||
|
||||
private static List<SubtitleImpl> parseSubtitles(final String content, final Map<String, Font> fonts) throws ParseException {
|
||||
final var fontIndex = content.indexOf(EVENTS_SECTION);
|
||||
if (fontIndex == -1) {
|
||||
throw new ParseException("Events section not found in " + content);
|
||||
} else {
|
||||
final var split = content.substring(fontIndex).split("\\n");
|
||||
final List<String> fields;
|
||||
if (split[0].startsWith(EVENTS_SECTION)) {
|
||||
fields = getFields(split[1]);
|
||||
} else {
|
||||
throw new ParseException("Couldn't parse events : " + content);
|
||||
}
|
||||
final var startIndex = fields.indexOf("Start");
|
||||
final var endIndex = fields.indexOf("End");
|
||||
final var styleIndex = fields.indexOf("Style");
|
||||
final var textIndex = fields.indexOf("Text");
|
||||
if (startIndex == -1 || endIndex == -1 || styleIndex == -1 || textIndex == -1) {
|
||||
throw new ParseException("Couldn't parse events : " + content);
|
||||
}
|
||||
return Arrays.stream(split).filter(s -> s.startsWith(DIALOGUE))
|
||||
.map(s -> {
|
||||
final var values = Arrays.stream(s.replace(DIALOGUE, "").split(",")).map(String::trim).filter(w -> !w.isBlank()).toList();
|
||||
final var start = parseTime(values.get(startIndex));
|
||||
final var end = parseTime(values.get(endIndex));
|
||||
final var style = values.get(styleIndex);
|
||||
final var font = fonts.get(style);
|
||||
final var text = values.stream().skip(textIndex).collect(Collectors.joining(", "));
|
||||
return new SubtitleImpl(text, start, end, font, null); //TODO pos style overrides
|
||||
}).toList();
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Font> parseFonts(final String content) throws ParseException {
|
||||
final var fontIndex = content.indexOf(STYLES_SECTION);
|
||||
if (fontIndex == -1) {
|
||||
throw new ParseException("Styles section not found in " + content);
|
||||
} else {
|
||||
final var split = content.substring(fontIndex).split("\\n");
|
||||
final List<String> fields;
|
||||
if (split[0].startsWith(STYLES_SECTION)) {
|
||||
fields = getFields(split[1]);
|
||||
} else {
|
||||
throw new ParseException("Couldn't parse styles : " + content);
|
||||
}
|
||||
final var fontNameIndex = fields.indexOf("Fontname");
|
||||
final var fontSizeIndex = fields.indexOf("Fontsize");
|
||||
if (fontNameIndex == -1 || fontSizeIndex == -1) {
|
||||
throw new ParseException("Couldn't parse styles : " + content);
|
||||
}
|
||||
return Arrays.stream(split).filter(s -> s.startsWith(STYLE))
|
||||
.map(s -> {
|
||||
final var values = Arrays.stream(s.replace(STYLE, "").split(",")).map(String::trim).filter(w -> !w.isBlank()).toList();
|
||||
final var name = values.get(fontNameIndex);
|
||||
final var size = Integer.parseInt(values.get(fontSizeIndex));
|
||||
return new FontImpl(name, size);
|
||||
}).collect(Collectors.toMap(this::getName, f -> f));
|
||||
}
|
||||
}
|
||||
|
||||
private static List<String> getFields(final String string) {
|
||||
return Arrays.stream(string.replace(FORMAT, "").split(",")).map(String::trim).filter(s -> !s.isBlank()).toList();
|
||||
}
|
||||
|
||||
private static long parseTime(final String timeStr) {
|
||||
final var split = timeStr.split(":");
|
||||
final var hours = Integer.parseInt(split[0]);
|
||||
final var minutes = Integer.parseInt(split[1]);
|
||||
final var secondsSplit = split[2].split("\\.");
|
||||
final var seconds = Integer.parseInt(secondsSplit[0]);
|
||||
final var hundredths = Integer.parseInt(secondsSplit[1]);
|
||||
return (hours * 3600L + minutes * 60L + seconds) * 1000L + hundredths * 10L;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String formatName() {
|
||||
return "ass";
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.github.gtache.autosubtitle.subtitle.converter.impl;
|
||||
|
||||
import com.github.gtache.autosubtitle.VideoInfo;
|
||||
import com.github.gtache.autosubtitle.subtitle.Subtitle;
|
||||
import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
|
||||
import com.github.gtache.autosubtitle.subtitle.converter.ParseException;
|
||||
@@ -29,7 +30,7 @@ public class SRTSubtitleConverter implements SubtitleConverter<SubtitleImpl> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String format(final SubtitleCollection<?> collection) {
|
||||
public String format(final SubtitleCollection<?> collection, final VideoInfo videoInfo) {
|
||||
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);
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.github.gtache.autosubtitle.subtitle.converter.impl;
|
||||
|
||||
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverter;
|
||||
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverterProvider;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Implementation of {@link SubtitleConverterProvider}
|
||||
*/
|
||||
public class SubtitleConverterProviderImpl implements SubtitleConverterProvider {
|
||||
|
||||
private final Map<String, SubtitleConverter<?>> converters;
|
||||
|
||||
@Inject
|
||||
SubtitleConverterProviderImpl(final Map<String, SubtitleConverter> converters) {
|
||||
this.converters = converters.entrySet().stream().collect(Collectors.toMap(e -> e.getKey().toUpperCase(), Map.Entry::getValue));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<SubtitleConverter<?>> allConverters() {
|
||||
return converters.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubtitleConverter<?> getConverter(final String format) {
|
||||
return converters.get(format.toUpperCase());
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,14 @@
|
||||
package com.github.gtache.autosubtitle.subtitle.impl;
|
||||
|
||||
import com.github.gtache.autosubtitle.Language;
|
||||
import com.github.gtache.autosubtitle.VideoInfo;
|
||||
import com.github.gtache.autosubtitle.archive.Archiver;
|
||||
import com.github.gtache.autosubtitle.archive.ArchiverProvider;
|
||||
import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
|
||||
import com.github.gtache.autosubtitle.subtitle.SubtitleImporterExporter;
|
||||
import com.github.gtache.autosubtitle.subtitle.converter.ParseException;
|
||||
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;
|
||||
|
||||
@@ -17,7 +20,8 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/**
|
||||
* Implementation of {@link SubtitleImporterExporter}
|
||||
@@ -25,32 +29,32 @@ import java.util.stream.Collectors;
|
||||
public class SubtitleImporterExporterImpl implements SubtitleImporterExporter<SubtitleImpl> {
|
||||
|
||||
private static final Logger logger = LogManager.getLogger(SubtitleImporterExporterImpl.class);
|
||||
private final Map<String, Archiver> archiverMap;
|
||||
private final Map<String, SubtitleConverter<?>> converterMap;
|
||||
private final ArchiverProvider archiverProvider;
|
||||
private final SubtitleConverterProvider converterProvider;
|
||||
|
||||
@Inject
|
||||
SubtitleImporterExporterImpl(final Map<String, Archiver> archiverMap, final Map<String, SubtitleConverter> converterMap) {
|
||||
this.archiverMap = Map.copyOf(archiverMap);
|
||||
this.converterMap = converterMap.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
SubtitleImporterExporterImpl(final ArchiverProvider archiverProvider, final SubtitleConverterProvider converterProvider) {
|
||||
this.archiverProvider = requireNonNull(archiverProvider);
|
||||
this.converterProvider = requireNonNull(converterProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Language, SubtitleCollection<SubtitleImpl>> importSubtitles(final Path file) throws IOException, ParseException {
|
||||
final var fileName = file.getFileName().toString();
|
||||
final var extension = fileName.substring(fileName.lastIndexOf('.') + 1);
|
||||
if (archiverMap.containsKey(extension)) {
|
||||
return loadArchive(file);
|
||||
} else {
|
||||
if (archiverProvider.getArchiver(extension) == null) {
|
||||
final var loaded = loadSingleFile(file);
|
||||
logger.info("Loaded {}", file);
|
||||
return Map.of(loaded.language(), loaded);
|
||||
} else {
|
||||
return loadArchive(file);
|
||||
}
|
||||
}
|
||||
|
||||
private SubtitleCollection<SubtitleImpl> loadSingleFile(final Path file) throws ParseException {
|
||||
final var fileName = file.getFileName().toString();
|
||||
final var extension = fileName.substring(fileName.lastIndexOf('.') + 1);
|
||||
final var parser = converterMap.get(extension);
|
||||
final var parser = converterProvider.getConverter(extension);
|
||||
if (parser == null) {
|
||||
throw new ParseException("No converter found for " + file);
|
||||
} else {
|
||||
@@ -62,7 +66,7 @@ public class SubtitleImporterExporterImpl implements SubtitleImporterExporter<Su
|
||||
private Map<Language, SubtitleCollection<SubtitleImpl>> loadArchive(final Path file) throws IOException, ParseException {
|
||||
final var fileName = file.getFileName().toString();
|
||||
final var extension = fileName.substring(fileName.lastIndexOf('.') + 1);
|
||||
final var archiver = archiverMap.get(extension);
|
||||
final var archiver = archiverProvider.getArchiver(extension);
|
||||
final var tempDirectory = Files.createTempDirectory("autosubtitle");
|
||||
archiver.decompress(file, tempDirectory);
|
||||
final var files = new ArrayList<Path>();
|
||||
@@ -81,27 +85,27 @@ public class SubtitleImporterExporterImpl implements SubtitleImporterExporter<Su
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exportSubtitles(final Collection<? extends SubtitleCollection<?>> collections, final Path file) throws IOException {
|
||||
public void exportSubtitles(final Collection<? extends SubtitleCollection<?>> collections, final VideoInfo videoInfo, final Path file) throws IOException {
|
||||
final var fileName = file.getFileName().toString();
|
||||
final var extension = fileName.substring(fileName.lastIndexOf('.') + 1);
|
||||
if (archiverMap.containsKey(extension)) {
|
||||
saveArchive(file, collections);
|
||||
if (archiverProvider.getArchiver(extension) != null) {
|
||||
saveArchive(file, collections, videoInfo);
|
||||
} else if (collections.size() == 1) {
|
||||
saveSingleFile(file, collections.iterator().next());
|
||||
saveSingleFile(file, collections.iterator().next(), videoInfo);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Cannot export multiple collections to a non-archive file : " + file);
|
||||
}
|
||||
}
|
||||
|
||||
private void saveArchive(final Path file, final Iterable<? extends SubtitleCollection<?>> collections) throws IOException {
|
||||
private void saveArchive(final Path file, final Iterable<? extends SubtitleCollection<?>> collections, final VideoInfo videoInfo) throws IOException {
|
||||
final var fileName = file.getFileName().toString();
|
||||
final var extension = fileName.substring(fileName.lastIndexOf('.') + 1);
|
||||
final var archiver = archiverMap.get(extension);
|
||||
final var singleExporter = converterMap.getOrDefault("json", converterMap.values().iterator().next());
|
||||
final var archiver = archiverProvider.getArchiver(extension);
|
||||
final var singleExporter = converterProvider.getConverter("json");
|
||||
final var tempDir = Files.createTempDirectory("autosubtitle");
|
||||
for (final var collection : collections) {
|
||||
final var subtitleFile = tempDir.resolve(collection.language() + "." + singleExporter.formatName());
|
||||
saveSingleFile(subtitleFile, collection);
|
||||
saveSingleFile(subtitleFile, collection, videoInfo);
|
||||
}
|
||||
final var files = new ArrayList<Path>();
|
||||
try (final var stream = Files.list(tempDir)) {
|
||||
@@ -115,14 +119,14 @@ public class SubtitleImporterExporterImpl implements SubtitleImporterExporter<Su
|
||||
logger.info("Saved {}", file);
|
||||
}
|
||||
|
||||
private void saveSingleFile(final Path file, final SubtitleCollection<?> collection) throws IOException {
|
||||
private void saveSingleFile(final Path file, final SubtitleCollection<?> collection, final VideoInfo videoInfo) throws IOException {
|
||||
final var fileName = file.getFileName().toString();
|
||||
final var extension = fileName.substring(fileName.lastIndexOf('.') + 1);
|
||||
final var converter = converterMap.get(extension);
|
||||
final var converter = converterProvider.getConverter(extension);
|
||||
if (converter == null) {
|
||||
throw new IOException("No converter found for " + file);
|
||||
} else {
|
||||
final var string = converter.format(collection);
|
||||
final var string = converter.format(collection, videoInfo);
|
||||
Files.writeString(file, string);
|
||||
logger.info("Saved {}", file);
|
||||
}
|
||||
@@ -131,11 +135,11 @@ public class SubtitleImporterExporterImpl implements SubtitleImporterExporter<Su
|
||||
|
||||
@Override
|
||||
public Collection<String> supportedArchiveExtensions() {
|
||||
return archiverMap.keySet();
|
||||
return archiverProvider.allArchivers().stream().map(Archiver::archiveExtension).toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> supportedSingleFileExtensions() {
|
||||
return converterMap.keySet();
|
||||
return converterProvider.allConverters().stream().map(SubtitleConverter::formatName).toList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.github.gtache.autosubtitle.subtitle.converter.impl;
|
||||
|
||||
import com.github.gtache.autosubtitle.Language;
|
||||
import com.github.gtache.autosubtitle.VideoInfo;
|
||||
import com.github.gtache.autosubtitle.subtitle.converter.ParseException;
|
||||
import com.github.gtache.autosubtitle.subtitle.impl.SubtitleCollectionImpl;
|
||||
import com.github.gtache.autosubtitle.subtitle.impl.SubtitleImpl;
|
||||
@@ -23,10 +24,12 @@ class TestSRTSubtitleConverter {
|
||||
|
||||
private final Translator translator;
|
||||
private final Language language;
|
||||
private final VideoInfo videoInfo;
|
||||
|
||||
TestSRTSubtitleConverter(@Mock final Translator translator, @Mock final Language language) {
|
||||
TestSRTSubtitleConverter(@Mock final Translator translator, @Mock final Language language, @Mock final VideoInfo videoInfo) {
|
||||
this.translator = Objects.requireNonNull(translator);
|
||||
this.language = Objects.requireNonNull(language);
|
||||
this.videoInfo = Objects.requireNonNull(videoInfo);
|
||||
when(translator.getLanguage(anyString())).thenReturn(language);
|
||||
}
|
||||
|
||||
@@ -53,7 +56,7 @@ class TestSRTSubtitleConverter {
|
||||
final var subtitles = new SubtitleCollectionImpl<>(subtitle1.content() + " " + subtitle2.content(), Arrays.asList(subtitle1, subtitle2), language);
|
||||
final var converter = new SRTSubtitleConverter(translator);
|
||||
assertEquals(subtitles, converter.parse(in));
|
||||
assertEquals(in, converter.format(subtitles));
|
||||
assertEquals(in, converter.format(subtitles, videoInfo));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.github.gtache.autosubtitle.Audio;
|
||||
import com.github.gtache.autosubtitle.File;
|
||||
import com.github.gtache.autosubtitle.Video;
|
||||
import com.github.gtache.autosubtitle.VideoConverter;
|
||||
import com.github.gtache.autosubtitle.VideoInfo;
|
||||
import com.github.gtache.autosubtitle.impl.AudioInfoImpl;
|
||||
import com.github.gtache.autosubtitle.impl.FileAudioImpl;
|
||||
import com.github.gtache.autosubtitle.impl.FileVideoImpl;
|
||||
@@ -13,6 +14,7 @@ import com.github.gtache.autosubtitle.modules.setup.ffmpeg.FFmpegSystemPath;
|
||||
import com.github.gtache.autosubtitle.process.impl.AbstractProcessRunner;
|
||||
import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
|
||||
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverter;
|
||||
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverterProvider;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.IOException;
|
||||
@@ -23,9 +25,9 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.SequencedMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.prefs.Preferences;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
@@ -37,13 +39,16 @@ public class FFmpegVideoConverter extends AbstractProcessRunner implements Video
|
||||
private static final String TEMP_FILE_PREFIX = "autosubtitle";
|
||||
private final Path bundledPath;
|
||||
private final Path systemPath;
|
||||
private final SubtitleConverter<?> subtitleConverter;
|
||||
private final SubtitleConverterProvider converterProvider;
|
||||
private final Preferences preferences;
|
||||
|
||||
@Inject
|
||||
FFmpegVideoConverter(@FFmpegBundledPath final Path bundledPath, @FFmpegSystemPath final Path systemPath, final Map<String, SubtitleConverter> subtitleConverters) {
|
||||
FFmpegVideoConverter(@FFmpegBundledPath final Path bundledPath, @FFmpegSystemPath final Path systemPath,
|
||||
final SubtitleConverterProvider converterProvider, final Preferences preferences) {
|
||||
this.bundledPath = requireNonNull(bundledPath);
|
||||
this.systemPath = requireNonNull(systemPath);
|
||||
this.subtitleConverter = subtitleConverters.get("srt");
|
||||
this.converterProvider = requireNonNull(converterProvider);
|
||||
this.preferences = requireNonNull(preferences);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -56,7 +61,7 @@ public class FFmpegVideoConverter extends AbstractProcessRunner implements Video
|
||||
@Override
|
||||
public void addSoftSubtitles(final Video video, final Collection<? extends SubtitleCollection<?>> subtitles, final Path path) throws IOException {
|
||||
final var videoPath = getPath(video);
|
||||
final var collectionMap = dumpCollections(subtitles);
|
||||
final var collectionMap = dumpCollections(subtitles, video.info());
|
||||
final var args = new ArrayList<String>();
|
||||
args.add(getFFmpegPath());
|
||||
args.add("-y");
|
||||
@@ -86,7 +91,7 @@ public class FFmpegVideoConverter extends AbstractProcessRunner implements Video
|
||||
args.add("mov_text");
|
||||
} else {
|
||||
args.add("-c:s");
|
||||
args.add(subtitleConverter.formatName());
|
||||
args.add("copy");
|
||||
}
|
||||
final var j = new AtomicInteger(0);
|
||||
collectionMap.forEach((c, p) -> {
|
||||
@@ -108,9 +113,9 @@ public class FFmpegVideoConverter extends AbstractProcessRunner implements Video
|
||||
@Override
|
||||
public void addHardSubtitles(final Video video, final SubtitleCollection<?> subtitles, final Path path) throws IOException {
|
||||
final var videoPath = getPath(video);
|
||||
final var subtitlesPath = dumpSubtitles(subtitles);
|
||||
final var subtitlesPath = dumpSubtitles(subtitles, video.info());
|
||||
final var escapedPath = escapeVF(subtitlesPath.toString());
|
||||
final var subtitleArg = subtitleConverter.formatName().equalsIgnoreCase("ass") ? "ass='" + escapedPath + "'" : "subtitles='" + escapedPath + "'";
|
||||
final var subtitleArg = getSubtitleConverter().formatName().equalsIgnoreCase("ass") ? "ass='" + escapedPath + "'" : "subtitles='" + escapedPath + "'";
|
||||
final var args = List.of(
|
||||
getFFmpegPath(),
|
||||
"-i",
|
||||
@@ -122,6 +127,10 @@ public class FFmpegVideoConverter extends AbstractProcessRunner implements Video
|
||||
runListen(args, Duration.ofHours(1));
|
||||
}
|
||||
|
||||
private SubtitleConverter<?> getSubtitleConverter() {
|
||||
return converterProvider.getConverter(preferences.get("outputFormat", "ass"));
|
||||
}
|
||||
|
||||
private static String escapeVF(final String path) {
|
||||
return path.replace("\\", "\\\\").replace(":", "\\:").replace("'", "'\\''")
|
||||
.replace("%", "\\%");
|
||||
@@ -165,17 +174,17 @@ public class FFmpegVideoConverter extends AbstractProcessRunner implements Video
|
||||
return path;
|
||||
}
|
||||
|
||||
private <T extends SubtitleCollection<?>> SequencedMap<T, Path> dumpCollections(final Collection<T> collections) throws IOException {
|
||||
private <T extends SubtitleCollection<?>> SequencedMap<T, Path> dumpCollections(final Collection<T> collections, final VideoInfo videoInfo) throws IOException {
|
||||
final var ret = LinkedHashMap.<T, Path>newLinkedHashMap(collections.size());
|
||||
for (final var subtitles : collections) {
|
||||
ret.put(subtitles, dumpSubtitles(subtitles));
|
||||
ret.put(subtitles, dumpSubtitles(subtitles, videoInfo));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
private Path dumpSubtitles(final SubtitleCollection<?> subtitles) throws IOException {
|
||||
final var path = getTempFile("srt");
|
||||
Files.writeString(path, subtitleConverter.format(subtitles));
|
||||
private Path dumpSubtitles(final SubtitleCollection<?> subtitles, final VideoInfo videoInfo) throws IOException {
|
||||
final var path = getTempFile(getSubtitleConverter().formatName().toLowerCase());
|
||||
Files.writeString(path, getSubtitleConverter().format(subtitles, videoInfo));
|
||||
return path;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.github.gtache.autosubtitle.setup.ffmpeg;
|
||||
|
||||
import com.github.gtache.autosubtitle.archive.Archiver;
|
||||
import com.github.gtache.autosubtitle.archive.ArchiverProvider;
|
||||
import com.github.gtache.autosubtitle.impl.Architecture;
|
||||
import com.github.gtache.autosubtitle.setup.SetupException;
|
||||
import com.github.gtache.autosubtitle.setup.SetupManager;
|
||||
@@ -16,7 +16,6 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.github.gtache.autosubtitle.impl.Architecture.ARMEL;
|
||||
import static com.github.gtache.autosubtitle.impl.Architecture.ARMHF;
|
||||
@@ -29,14 +28,14 @@ import static java.util.Objects.requireNonNull;
|
||||
public class FFmpegSetupManager extends AbstractSetupManager {
|
||||
private static final Logger logger = LogManager.getLogger(FFmpegSetupManager.class);
|
||||
private final FFmpegSetupConfiguration configuration;
|
||||
private final Map<String, Archiver> archivers;
|
||||
private final ArchiverProvider archiverProvider;
|
||||
|
||||
@Inject
|
||||
FFmpegSetupManager(final FFmpegSetupConfiguration configuration, final Map<String, Archiver> archivers,
|
||||
FFmpegSetupManager(final FFmpegSetupConfiguration configuration, final ArchiverProvider archiverProvider,
|
||||
final HttpClient httpClient) {
|
||||
super(httpClient);
|
||||
this.configuration = requireNonNull(configuration);
|
||||
this.archivers = Map.copyOf(archivers);
|
||||
this.archiverProvider = requireNonNull(archiverProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -135,7 +134,7 @@ public class FFmpegSetupManager extends AbstractSetupManager {
|
||||
try {
|
||||
final var filename = from.getFileName().toString();
|
||||
final var extension = filename.substring(filename.lastIndexOf('.') + 1);
|
||||
archivers.get(extension).decompress(from, to);
|
||||
archiverProvider.getArchiver(extension).decompress(from, to);
|
||||
} catch (final IOException e) {
|
||||
throw new SetupException(e);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ module com.github.gtache.autosubtitle.ffmpeg {
|
||||
requires org.apache.logging.log4j;
|
||||
requires org.tukaani.xz;
|
||||
requires org.apache.commons.compress;
|
||||
requires java.prefs;
|
||||
|
||||
exports com.github.gtache.autosubtitle.ffmpeg;
|
||||
exports com.github.gtache.autosubtitle.setup.ffmpeg;
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.github.gtache.autosubtitle.VideoInfo;
|
||||
import com.github.gtache.autosubtitle.impl.OS;
|
||||
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.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
@@ -14,8 +15,8 @@ import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.prefs.Preferences;
|
||||
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@@ -24,14 +25,16 @@ class TestFFmpegVideoConverter {
|
||||
|
||||
private final FFmpegVideoConverter converter;
|
||||
private final SubtitleConverter subtitleConverter;
|
||||
private final SubtitleConverterProvider subtitleConverterProvider;
|
||||
private final Video video;
|
||||
private final VideoInfo videoInfo;
|
||||
private final Path tmpFile;
|
||||
private final Path outputPath;
|
||||
private final SubtitleCollection<?> collection;
|
||||
private final Preferences preferences;
|
||||
|
||||
TestFFmpegVideoConverter(@Mock final SubtitleConverter subtitleConverter, @Mock final Video video,
|
||||
@Mock final VideoInfo videoInfo, @Mock final SubtitleCollection<?> collection) throws IOException {
|
||||
TestFFmpegVideoConverter(@Mock final SubtitleConverter subtitleConverter, @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);
|
||||
@@ -43,7 +46,9 @@ class TestFFmpegVideoConverter {
|
||||
}
|
||||
this.outputPath = Path.of(output, "test-ffmpeg-output.txt");
|
||||
this.subtitleConverter = Objects.requireNonNull(subtitleConverter);
|
||||
this.converter = new FFmpegVideoConverter(tmpFile, tmpFile, Map.of("srt", subtitleConverter));
|
||||
this.subtitleConverterProvider = Objects.requireNonNull(subtitleConverterProvider);
|
||||
this.preferences = Objects.requireNonNull(preferences);
|
||||
this.converter = new FFmpegVideoConverter(tmpFile, tmpFile, subtitleConverterProvider, preferences);
|
||||
this.collection = Objects.requireNonNull(collection);
|
||||
}
|
||||
|
||||
|
||||
@@ -48,12 +48,12 @@ public interface ParametersModel {
|
||||
/**
|
||||
* @return The current font family
|
||||
*/
|
||||
String fontFamily();
|
||||
String fontName();
|
||||
|
||||
/**
|
||||
* @param fontFamily The new font family
|
||||
*/
|
||||
void setFontFamily(String fontFamily);
|
||||
void setFontName(String fontFamily);
|
||||
|
||||
/**
|
||||
* @return The current font size
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.github.gtache.autosubtitle.gui;
|
||||
|
||||
import com.github.gtache.autosubtitle.Language;
|
||||
import com.github.gtache.autosubtitle.VideoInfo;
|
||||
import com.github.gtache.autosubtitle.subtitle.Subtitle;
|
||||
import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
|
||||
|
||||
@@ -32,6 +33,18 @@ public interface SubtitlesModel<T extends Subtitle, U extends SubtitleCollection
|
||||
*/
|
||||
void setVideoLanguage(Language language);
|
||||
|
||||
/**
|
||||
* @return The video info
|
||||
*/
|
||||
VideoInfo videoInfo();
|
||||
|
||||
/**
|
||||
* Sets the video info
|
||||
*
|
||||
* @param videoInfo The new video info
|
||||
*/
|
||||
void setVideoInfo(VideoInfo videoInfo);
|
||||
|
||||
/**
|
||||
* @return The list of available translations languages
|
||||
*/
|
||||
|
||||
@@ -46,16 +46,4 @@ public final class GuiCoreModule {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FontFamily
|
||||
static String providesFontFamily() {
|
||||
return "Arial";
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FontSize
|
||||
static int providesFontSize() {
|
||||
return 12;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,8 @@ package com.github.gtache.autosubtitle.modules.gui.impl;
|
||||
import com.github.gtache.autosubtitle.gui.impl.CombinedResourceBundle;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class TestGuiCoreModule {
|
||||
|
||||
@@ -21,14 +22,4 @@ class TestGuiCoreModule {
|
||||
void testPauseImage() {
|
||||
assertTrue(GuiCoreModule.providesPauseImage().length > 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFontFamily() {
|
||||
assertEquals("Arial", GuiCoreModule.providesFontFamily());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFontSize() {
|
||||
assertEquals(12, GuiCoreModule.providesFontSize());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ public class FXParametersController extends AbstractFXController implements Para
|
||||
extractionOutputFormat.valueProperty().bindBidirectional(model.outputFormatProperty());
|
||||
|
||||
fontFamilyCombobox.setItems(model.availableFontFamilies());
|
||||
fontFamilyCombobox.valueProperty().bindBidirectional(model.fontFamilyProperty());
|
||||
fontFamilyCombobox.valueProperty().bindBidirectional(model.fontNameProperty());
|
||||
|
||||
final UnaryOperator<TextFormatter.Change> integerFilter = change -> {
|
||||
final var newText = change.getControlNewText();
|
||||
@@ -85,14 +85,14 @@ public class FXParametersController extends AbstractFXController implements Para
|
||||
private void loadPreferences() {
|
||||
final var extractionModel = preferences.get("extractionModel", model.extractionModel().name());
|
||||
final var outputFormat = preferences.get("outputFormat", model.outputFormat().name());
|
||||
final var fontFamily = preferences.get("fontFamily", model.fontFamily());
|
||||
final var fontFamily = preferences.get("fontName", model.fontName());
|
||||
final var fontSize = preferences.getInt("fontSize", model.fontSize());
|
||||
final var maxLineLength = preferences.getInt("maxLineLength", model.maxLineLength());
|
||||
final var maxLines = preferences.getInt("maxLines", model.maxLines());
|
||||
|
||||
model.setExtractionModel(extractionModelProvider.getExtractionModel(extractionModel));
|
||||
model.setOutputFormat(OutputFormat.valueOf(outputFormat));
|
||||
model.setFontFamily(fontFamily);
|
||||
model.setFontName(fontFamily);
|
||||
model.setFontSize(fontSize);
|
||||
model.setMaxLineLength(maxLineLength);
|
||||
model.setMaxLines(maxLines);
|
||||
@@ -105,7 +105,7 @@ public class FXParametersController extends AbstractFXController implements Para
|
||||
logger.info("Saving preferences");
|
||||
preferences.put("extractionModel", model.extractionModel().name());
|
||||
preferences.put("outputFormat", model.outputFormat().name());
|
||||
preferences.put("fontFamily", model.fontFamily());
|
||||
preferences.put("fontName", model.fontName());
|
||||
preferences.putInt("fontSize", model.fontSize());
|
||||
preferences.putInt("maxLineLength", model.maxLineLength());
|
||||
preferences.putInt("maxLines", model.maxLines());
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package com.github.gtache.autosubtitle.gui.fx;
|
||||
|
||||
import com.github.gtache.autosubtitle.gui.ParametersModel;
|
||||
import com.github.gtache.autosubtitle.modules.gui.impl.FontFamily;
|
||||
import com.github.gtache.autosubtitle.modules.gui.impl.FontSize;
|
||||
import com.github.gtache.autosubtitle.modules.impl.FontName;
|
||||
import com.github.gtache.autosubtitle.modules.impl.FontSize;
|
||||
import com.github.gtache.autosubtitle.modules.impl.MaxLineLength;
|
||||
import com.github.gtache.autosubtitle.modules.impl.MaxLines;
|
||||
import com.github.gtache.autosubtitle.subtitle.OutputFormat;
|
||||
@@ -31,20 +31,20 @@ public class FXParametersModel implements ParametersModel {
|
||||
private final ObservableList<OutputFormat> availableOutputFormats;
|
||||
private final ObjectProperty<OutputFormat> outputFormat;
|
||||
private final ObservableList<String> availableFontFamilies;
|
||||
private final StringProperty fontFamily;
|
||||
private final StringProperty fontName;
|
||||
private final IntegerProperty fontSize;
|
||||
private final IntegerProperty maxLineLength;
|
||||
private final IntegerProperty maxLines;
|
||||
|
||||
@Inject
|
||||
FXParametersModel(final ExtractionModelProvider extractionModelProvider, @FontFamily final String defaultFontFamily,
|
||||
FXParametersModel(final ExtractionModelProvider extractionModelProvider, @FontName final String defaultFontFamily,
|
||||
@FontSize final int defaultFontSize, @MaxLineLength final int defaultMaxLineLength, @MaxLines final int defaultMaxLines) {
|
||||
this.availableExtractionModels = FXCollections.unmodifiableObservableList(FXCollections.observableArrayList(extractionModelProvider.getAvailableExtractionModels()));
|
||||
this.extractionModel = new SimpleObjectProperty<>(extractionModelProvider.getDefaultExtractionModel());
|
||||
this.availableOutputFormats = FXCollections.unmodifiableObservableList(FXCollections.observableArrayList(OutputFormat.SRT));
|
||||
this.outputFormat = new SimpleObjectProperty<>(OutputFormat.SRT);
|
||||
this.availableOutputFormats = FXCollections.unmodifiableObservableList(FXCollections.observableArrayList(OutputFormat.values()));
|
||||
this.outputFormat = new SimpleObjectProperty<>(OutputFormat.ASS);
|
||||
this.availableFontFamilies = FXCollections.unmodifiableObservableList(FXCollections.observableArrayList("Arial"));
|
||||
this.fontFamily = new SimpleStringProperty(defaultFontFamily);
|
||||
this.fontName = new SimpleStringProperty(defaultFontFamily);
|
||||
this.fontSize = new SimpleIntegerProperty(defaultFontSize);
|
||||
this.maxLineLength = new SimpleIntegerProperty(defaultMaxLineLength);
|
||||
this.maxLines = new SimpleIntegerProperty(defaultMaxLines);
|
||||
@@ -94,17 +94,17 @@ public class FXParametersModel implements ParametersModel {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String fontFamily() {
|
||||
return fontFamily.get();
|
||||
public String fontName() {
|
||||
return fontName.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFontFamily(final String fontFamily) {
|
||||
this.fontFamily.set(fontFamily);
|
||||
public void setFontName(final String fontFamily) {
|
||||
this.fontName.set(fontFamily);
|
||||
}
|
||||
|
||||
StringProperty fontFamilyProperty() {
|
||||
return fontFamily;
|
||||
StringProperty fontNameProperty() {
|
||||
return fontName;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.github.gtache.autosubtitle.gui.fx;
|
||||
|
||||
import com.github.gtache.autosubtitle.Language;
|
||||
import com.github.gtache.autosubtitle.Video;
|
||||
import com.github.gtache.autosubtitle.gui.WorkStatus;
|
||||
import com.github.gtache.autosubtitle.subtitle.gui.fx.ObservableSubtitleCollectionImpl;
|
||||
import javafx.beans.binding.Bindings;
|
||||
@@ -34,6 +35,7 @@ public class FXSubtitlesBinder implements FXBinder {
|
||||
workModel.selectedSubtitleProperty().bind(subtitlesModel.selectedSubtitleProperty());
|
||||
workModel.canExportProperty().bind(Bindings.isNotEmpty(subtitlesModel.collections()).and(workModel.statusProperty().isEqualTo(WorkStatus.IDLE)));
|
||||
workModel.videoLanguageProperty().bind(subtitlesModel.videoLanguageProperty());
|
||||
subtitlesModel.videoInfoProperty().bind(workModel.videoProperty().map(Video::info));
|
||||
|
||||
subtitlesModel.translatingProperty().addListener((observable, oldValue, newValue) -> {
|
||||
if (newValue) {
|
||||
|
||||
@@ -152,6 +152,9 @@ public class FXSubtitlesController extends AbstractFXController implements Subti
|
||||
model.setSelectedCollection(model.collections().get(value));
|
||||
} else {
|
||||
logger.error("Error while translating to {}", value, t);
|
||||
final var newCollection = new ObservableSubtitleCollectionImpl();
|
||||
loadCollection(newCollection);
|
||||
model.setSelectedCollection(newCollection);
|
||||
}
|
||||
model.setTranslating(false);
|
||||
}, Platform::runLater);
|
||||
@@ -284,9 +287,9 @@ public class FXSubtitlesController extends AbstractFXController implements Subti
|
||||
final var filename = file.getFileName().toString();
|
||||
final var extension = filename.substring(filename.lastIndexOf('.') + 1);
|
||||
if (subtitleExtensions.contains(extension)) {
|
||||
importerExporter.exportSubtitles(model.selectedCollection(), file);
|
||||
importerExporter.exportSubtitles(model.selectedCollection(), model.videoInfo(), file);
|
||||
} else {
|
||||
importerExporter.exportSubtitles(model.collections().values(), file);
|
||||
importerExporter.exportSubtitles(model.collections().values(), model.videoInfo(), file);
|
||||
}
|
||||
} catch (final IOException e) {
|
||||
logger.error("Error saving subtitles {}", file, e);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.github.gtache.autosubtitle.gui.fx;
|
||||
|
||||
import com.github.gtache.autosubtitle.Language;
|
||||
import com.github.gtache.autosubtitle.VideoInfo;
|
||||
import com.github.gtache.autosubtitle.gui.SubtitlesModel;
|
||||
import com.github.gtache.autosubtitle.subtitle.gui.fx.ObservableSubtitleCollectionImpl;
|
||||
import com.github.gtache.autosubtitle.subtitle.gui.fx.ObservableSubtitleImpl;
|
||||
@@ -29,6 +30,7 @@ public class FXSubtitlesModel implements SubtitlesModel<ObservableSubtitleImpl,
|
||||
|
||||
private final ObservableList<Language> availableVideoLanguages;
|
||||
private final ObjectProperty<Language> videoLanguage;
|
||||
private final ObjectProperty<VideoInfo> videoInfo;
|
||||
private final ObservableList<Language> availableTranslationLanguages;
|
||||
private final ObservableList<Language> selectedTranslationsLanguages;
|
||||
private final ObjectProperty<Language> selectedLanguage;
|
||||
@@ -59,6 +61,7 @@ public class FXSubtitlesModel implements SubtitlesModel<ObservableSubtitleImpl,
|
||||
}).toList()));
|
||||
this.availableTranslationLanguages = FXCollections.observableArrayList(Arrays.stream(Language.values()).filter(l -> l != Language.AUTO).toList());
|
||||
this.videoLanguage = new SimpleObjectProperty<>(Language.AUTO);
|
||||
this.videoInfo = new SimpleObjectProperty<>();
|
||||
this.selectedTranslationsLanguages = FXCollections.observableArrayList();
|
||||
this.selectedLanguage = new SimpleObjectProperty<>(Language.AUTO);
|
||||
this.collections = FXCollections.observableHashMap();
|
||||
@@ -108,6 +111,20 @@ public class FXSubtitlesModel implements SubtitlesModel<ObservableSubtitleImpl,
|
||||
return videoLanguage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VideoInfo videoInfo() {
|
||||
return videoInfo.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVideoInfo(final VideoInfo videoInfo) {
|
||||
this.videoInfo.set(videoInfo);
|
||||
}
|
||||
|
||||
ObjectProperty<VideoInfo> videoInfoProperty() {
|
||||
return videoInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObservableList<Language> availableTranslationsLanguage() {
|
||||
return FXCollections.unmodifiableObservableList(availableTranslationLanguages);
|
||||
|
||||
@@ -77,13 +77,13 @@ class TestFXParametersModel {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFontFamily() {
|
||||
assertEquals(DEFAULT_FONT_FAMILY, model.fontFamily());
|
||||
assertEquals(DEFAULT_FONT_FAMILY, model.fontFamilyProperty().get());
|
||||
void testFontName() {
|
||||
assertEquals(DEFAULT_FONT_FAMILY, model.fontName());
|
||||
assertEquals(DEFAULT_FONT_FAMILY, model.fontNameProperty().get());
|
||||
final var fontFamily = DEFAULT_FONT_FAMILY + " A";
|
||||
model.setFontFamily(fontFamily);
|
||||
assertEquals(fontFamily, model.fontFamily());
|
||||
assertEquals(fontFamily, model.fontFamilyProperty().get());
|
||||
model.setFontName(fontFamily);
|
||||
assertEquals(fontFamily, model.fontName());
|
||||
assertEquals(fontFamily, model.fontNameProperty().get());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -3,7 +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.subtitle.converter.SubtitleConverter;
|
||||
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverterProvider;
|
||||
import com.github.gtache.autosubtitle.subtitle.extractor.ExtractionModel;
|
||||
import com.github.gtache.autosubtitle.subtitle.extractor.SubtitleExtractor;
|
||||
import com.github.gtache.autosubtitle.subtitle.extractor.whisper.AbstractWhisperSubtitleExtractor;
|
||||
@@ -14,7 +14,6 @@ import javax.inject.Singleton;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Whisper implementation of {@link SubtitleExtractor}
|
||||
@@ -24,8 +23,8 @@ public class WhisperSubtitleExtractor extends AbstractWhisperSubtitleExtractor {
|
||||
|
||||
|
||||
@Inject
|
||||
WhisperSubtitleExtractor(@WhisperVenvPath final Path venvPath, final Map<String, SubtitleConverter> converters, final OS os) {
|
||||
super(venvPath, converters, os);
|
||||
WhisperSubtitleExtractor(@WhisperVenvPath final Path venvPath, final SubtitleConverterProvider converterProvider, final OS os) {
|
||||
super(venvPath, converterProvider, os);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.github.gtache.autosubtitle.subtitle.parser.json.whisper.base;
|
||||
|
||||
import com.github.gtache.autosubtitle.Language;
|
||||
import com.github.gtache.autosubtitle.VideoInfo;
|
||||
import com.github.gtache.autosubtitle.subtitle.Subtitle;
|
||||
import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
|
||||
import com.github.gtache.autosubtitle.subtitle.converter.ParseException;
|
||||
@@ -35,7 +36,7 @@ public class JSONSubtitleConverter implements SubtitleConverter<SubtitleImpl> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String format(final SubtitleCollection<?> collection) {
|
||||
public String format(final SubtitleCollection<?> collection, final VideoInfo videoInfo) {
|
||||
final var id = new AtomicInteger(0);
|
||||
final var segments = collection.subtitles().stream().map(s -> new JSONSubtitleSegment(id.incrementAndGet(), 0, s.start() / (double) 1000,
|
||||
s.end() / (double) 1000, s.content(), List.of(), 0, 0, 0, 0)).toList();
|
||||
|
||||
@@ -10,6 +10,7 @@ import com.github.gtache.autosubtitle.subtitle.Subtitle;
|
||||
import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
|
||||
import com.github.gtache.autosubtitle.subtitle.converter.ParseException;
|
||||
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverter;
|
||||
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverterProvider;
|
||||
import com.github.gtache.autosubtitle.subtitle.extractor.ExtractEvent;
|
||||
import com.github.gtache.autosubtitle.subtitle.extractor.ExtractException;
|
||||
import com.github.gtache.autosubtitle.subtitle.extractor.ExtractionModel;
|
||||
@@ -25,7 +26,6 @@ import java.nio.file.Path;
|
||||
import java.time.Duration;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -47,9 +47,9 @@ public abstract class AbstractWhisperSubtitleExtractor extends AbstractProcessRu
|
||||
private final OS os;
|
||||
private final Set<SubtitleExtractorListener> listeners;
|
||||
|
||||
protected AbstractWhisperSubtitleExtractor(final Path venvPath, final Map<String, SubtitleConverter> converters, final OS os) {
|
||||
protected AbstractWhisperSubtitleExtractor(final Path venvPath, final SubtitleConverterProvider converterProvider, final OS os) {
|
||||
this.venvPath = requireNonNull(venvPath);
|
||||
this.converter = requireNonNull(converters.get("json"));
|
||||
this.converter = requireNonNull(converterProvider.getConverter("json"));
|
||||
this.os = requireNonNull(os);
|
||||
this.listeners = new HashSet<>();
|
||||
}
|
||||
|
||||
@@ -3,7 +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.subtitle.converter.SubtitleConverter;
|
||||
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverterProvider;
|
||||
import com.github.gtache.autosubtitle.subtitle.extractor.ExtractionModel;
|
||||
import com.github.gtache.autosubtitle.subtitle.extractor.SubtitleExtractor;
|
||||
import com.github.gtache.autosubtitle.subtitle.extractor.whisper.AbstractWhisperSubtitleExtractor;
|
||||
@@ -14,7 +14,6 @@ import javax.inject.Singleton;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* WhisperX implementation of {@link SubtitleExtractor}
|
||||
@@ -23,8 +22,8 @@ import java.util.Map;
|
||||
public class WhisperXSubtitleExtractor extends AbstractWhisperSubtitleExtractor {
|
||||
|
||||
@Inject
|
||||
WhisperXSubtitleExtractor(@WhisperVenvPath final Path venvPath, final Map<String, SubtitleConverter> converters, final OS os) {
|
||||
super(venvPath, converters, os);
|
||||
WhisperXSubtitleExtractor(@WhisperVenvPath final Path venvPath, final SubtitleConverterProvider converterProvider, final OS os) {
|
||||
super(venvPath, converterProvider, os);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.github.gtache.autosubtitle.subtitle.parser.json.whisperx;
|
||||
|
||||
import com.github.gtache.autosubtitle.Language;
|
||||
import com.github.gtache.autosubtitle.VideoInfo;
|
||||
import com.github.gtache.autosubtitle.modules.impl.MaxLineLength;
|
||||
import com.github.gtache.autosubtitle.modules.impl.MaxLines;
|
||||
import com.github.gtache.autosubtitle.subtitle.Subtitle;
|
||||
@@ -47,7 +48,7 @@ public class JSONSubtitleConverter implements SubtitleConverter<SubtitleImpl> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String format(final SubtitleCollection<?> collection) {
|
||||
public String format(final SubtitleCollection<?> collection, final VideoInfo videoInfo) {
|
||||
final var segments = collection.subtitles().stream().map(s -> new JSONSubtitleSegment(s.start() / (double) 1000,
|
||||
s.end() / (double) 1000, s.content(), List.of())).toList();
|
||||
final var subtitles = new JSONSubtitles(segments, collection.language().iso2());
|
||||
|
||||
Reference in New Issue
Block a user