All Downloads are FREE. Search and download functionalities are using the official Maven repository.

nl.vpro.domain.subtitles.SubtitlesUtil Maven / Gradle / Ivy

Go to download

Subtitles are related to media, but are implemented completely parallel. Classes to contain, parse, and assemble subtitle objects are here.

There is a newer version: 8.3.0
Show newest version
package nl.vpro.domain.subtitles;

import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.nio.charset.Charset;
import java.nio.charset.UnsupportedCharsetException;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Stream;

import org.apache.commons.io.IOUtils;
import org.checkerframework.checker.nullness.qual.NonNull;

import nl.vpro.util.BasicWrappedIterator;
import nl.vpro.util.CountedIterator;

import static nl.vpro.util.ISO6937CharsetProvider.ISO6937;


/**
 * @author Michiel Meeuwissen
 * @since 4.7
 */
@Slf4j
public class SubtitlesUtil {

    private SubtitlesUtil() {
    }


    public static Subtitles tt888(String parent, Duration offset, Locale locale, InputStream input) throws IOException {
        StringWriter w = new StringWriter();
        if (input == null) {
            throw new IllegalArgumentException("InputStream is null");
        }
        IOUtils.copy(new InputStreamReader(input, ISO6937), w);
        return Subtitles.builder()
            .mid(parent)
            .offset(offset)
            .language(locale)
            .format(SubtitlesFormat.TT888)
            .content(w.toString())
            .build();
    }



    public static Subtitles vtt(String parent, Duration offset, Locale locale, InputStream input) throws IOException {
        StringWriter w = new StringWriter();
        if (input == null) {
            throw new IllegalArgumentException("InputStream is null");
        }
        IOUtils.copy(new InputStreamReader(input, WEBVTTandSRT.VTT_CHARSET), w);
        return Subtitles.builder()
            .mid(parent)
            .offset(offset)
            .language(locale)
            .format(SubtitlesFormat.WEBVTT)
            .content(w.toString())
            .build();
    }




    @NonNull
    public static ParseResult parse(@NonNull Subtitles subtitles, boolean guessOffset) {

        SubtitlesContent content = subtitles.getContent();
        String mid = subtitles.getMid();
        Duration offset = subtitles.getOffset();

        Function offsetGuesser = guessOffset ? new DefaultOffsetGuesser(subtitles.getCreationInstant()) : timeLine -> Duration.ZERO;
        switch (content.getFormat()) {
            case TT888:
                return ParseResult.of(TT888.parse(mid, offset, offsetGuesser, content.asStream(), getCharset(content.getCharset(),  TT888.CHARSET)));
            case WEBVTT:
                return WEBVTTandSRT.parseWEBVTT(mid, offset, content.asStream(), getCharset(content.getCharset(), WEBVTTandSRT.VTT_CHARSET));
            case SRT:
                return ParseResult.of(WEBVTTandSRT.parseSRT(mid, offset, content.asStream(), getCharset(content.getCharset(), WEBVTTandSRT.SRT_CHARSET)));
            case EBU:
                return ParseResult.of(EBU.parse(mid, offset, offsetGuesser, content.asStream()));
            default:
                throw new IllegalArgumentException("Not supported format " + content.getFormat());
        }
    }




    private static Charset getCharset(String c, Charset defaultValue) {
        if (c == null) {
            log.debug("Using default");
            return defaultValue;
        }
        try {
            return Charset.forName(c);
        } catch (UnsupportedCharsetException usce) {
            if (!defaultValue.name().equals(c)) {
                log.warn(usce.getMessage());
            }
            return defaultValue;

        }
    }

    public static Stream<@NonNull StandaloneCue> standaloneStream(Subtitles subtitles, boolean guessOffset, boolean fillCueNumbers) {
        if (subtitles == null) {
            return Stream.empty();
        }
        Stream stream = parse(subtitles, guessOffset).getCues();
        if (fillCueNumbers) {
            stream = fillCueNumber(stream);
        }
        return stream.map(c -> StandaloneCue.of(c, subtitles.getLanguage(), subtitles.getType()))
            .filter(Objects::nonNull);
    }



    public static CountedIterator iterator(Subtitles subtitles, boolean guessOffset){
        return new BasicWrappedIterator<>(
            (long) subtitles.getCueCount(),
            parse(subtitles, guessOffset)
                .iterator());
    }

    public static CountedIterator iterator(Subtitles subtitles) {
        return iterator(subtitles, false);
    }

    public static CountedIterator standaloneIterator(Subtitles subtitles, boolean guessOffset, boolean fillCueNumbers) {
        return new BasicWrappedIterator<>((long) subtitles.getCueCount(), standaloneStream(subtitles, guessOffset, fillCueNumbers).iterator());

    }
    public static void stream(Iterator cueIterator, SubtitlesFormat format, OutputStream output) throws IOException {
        switch(format) {
            case TT888:
                toTT888(cueIterator, output);
                return;
            case WEBVTT:
                toVTT(cueIterator, output);
                return;
            case SRT:
                toSRT(cueIterator, output);
                return;
            default:
                throw new UnsupportedOperationException();
        }
    }


    public static void toTT888(Iterator cueIterator, OutputStream outputStream) throws IOException {
        TT888.format(cueIterator, outputStream);

    }

    public static void toVTT(Iterator cueIterator, OutputStream outputStream) throws IOException {
        WEBVTTandSRT.formatWEBVTT(cueIterator, outputStream);
    }

    public static void toSRT(Iterator cueIterator, OutputStream outputStream) throws IOException {
        WEBVTTandSRT.formatSRT(cueIterator, outputStream);

    }

    public static Stream fillCueNumber(Stream cues) {
        final AtomicInteger cueNumber = new AtomicInteger(0);
        return cues
            .peek((cue) -> {
                if (cue != null) {
                    if (cue.getSequence() == null) {
                        cue.sequence = cueNumber.incrementAndGet();
                    } else {
                        cueNumber.set(cue.getSequence());
                    }
                }
                }
            );

    }


}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy