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

com.github.dnbn.submerge.api.SubmergeAPI Maven / Gradle / Ivy

The newest version!
package com.github.dnbn.submerge.api;

import java.time.LocalTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import com.github.dnbn.submerge.api.subtitle.ass.ASSSub;
import com.github.dnbn.submerge.api.subtitle.ass.Events;
import com.github.dnbn.submerge.api.subtitle.common.TimedLine;
import com.github.dnbn.submerge.api.subtitle.common.TimedObject;
import com.github.dnbn.submerge.api.subtitle.common.TimedTextFile;
import com.github.dnbn.submerge.api.subtitle.config.SimpleSubConfig;
import com.github.dnbn.submerge.api.subtitle.srt.SRTLine;
import com.github.dnbn.submerge.api.subtitle.srt.SRTSub;
import com.github.dnbn.submerge.api.subtitle.srt.SRTTime;
import com.github.dnbn.submerge.api.utils.ConvertionUtils;

/**
 * Service used to manage subtitles
 */
public class SubmergeAPI {

	/**
	 * Change the framerate of a subtitle
	 * 
	 * @param timedFile the subtitle
	 * @param sourceFramerate le source framerate. Ex: 25.000
	 * @param targetFramerate the target framerate. Ex: 23.976
	 */
	public void convertFramerate(TimedTextFile timedFile, double sourceFramerate, double targetFramerate) {

		double ratio = sourceFramerate / targetFramerate;

		for (TimedLine timedLine : timedFile.getTimedLines()) {

			TimedObject time = timedLine.getTime();
			long s = Math.round(time.getStart().toNanoOfDay() * ratio);
			long e = Math.round(time.getEnd().toNanoOfDay() * ratio);

			time.setStart(LocalTime.ofNanoOfDay(s));
			time.setEnd(LocalTime.ofNanoOfDay(e));
		}
	}

	/**
	 * TimedTextFile to SRT conversion
	 * 
	 * @param timedFile the TimedTextFile
	 * @return the SRTSub object
	 */
	public SRTSub toSRT(TimedTextFile timedFile) {

		SRTSub srt = new SRTSub();

		int i = 0;
		for (TimedLine timedLine : timedFile.getTimedLines()) {

			int id = ++i;
			TimedObject time = timedLine.getTime();
			LocalTime start = time.getStart();
			LocalTime end = time.getEnd();
			SRTTime srtTime = new SRTTime(start, end);

			List textLines = timedLine.getTextLines();
			List newLines = new ArrayList<>();

			for (String textLine : textLines) {
				newLines.add(ConvertionUtils.toSRTString(textLine));
			}

			SRTLine srtLine = new SRTLine(id, srtTime, newLines);

			srt.add(srtLine);
		}

		return srt;
	}

	/**
	 * SubInput to ASS conversion
	 * 
	 * @param config the configuration object
	 * @return the ASSSub object
	 */
	public ASSSub toASS(SimpleSubConfig config) {

		return mergeToAss(config);
	}

	/**
	 * Merge several subtitles into one ASS
	 * 
	 * @param configs : configuration object of the subtitles
	 * @return
	 */
	public ASSSub mergeToAss(SimpleSubConfig... configs) {

		ASSSub ass = new ASSSub();
		Set ev = ass.getEvents();

		for (SimpleSubConfig config : configs) {
			ass.getStyle().add(ConvertionUtils.createV4Style(config));
			TimedTextFile sub = config.getSub();
			sub.getTimedLines().forEach(line -> ev.add(ConvertionUtils.createEvent(line, config.getStyleName())));
		}

		return ass;
	}

	/**
	 * Transform all multi-lines subtitles to single-line
	 * 
	 * @param timedFile the TimedTextFile
	 */
	public void mergeTextLines(TimedTextFile timedFile) {

		for (TimedLine timedLine : timedFile.getTimedLines()) {
			List textLines = timedLine.getTextLines();
			if (textLines.size() > 1) {
				textLines.set(0, textLines.stream().collect(Collectors.joining(" ")));
				textLines.subList(1, textLines.size()).clear();
			}
		}
	}

	/**
	 * Synchronise the timecodes of a subtitle from another one
	 * 
	 * @param fileToAdjust the subtitle to modify
	 * @param referenceFile the subtitle to take the timecodes from
	 * @param delay the number of milliseconds allowed to differ
	 */
	public void adjustTimecodes(TimedTextFile fileToAdjust, TimedTextFile referenceFile, int delay) {

		TimedLinesAPI linesAPI = new TimedLinesAPI();
		List timedLines = new ArrayList<>(fileToAdjust.getTimedLines());
		List referenceLines = new ArrayList<>(referenceFile.getTimedLines());

		for (TimedLine lineToAdjust : timedLines) {

			TimedObject originalTime = lineToAdjust.getTime();
			LocalTime originalStart = originalTime.getStart();

			TimedLine referenceLine = linesAPI.closestByStart(referenceLines, originalStart, delay);

			if (referenceLine != null) {
				LocalTime targetStart = referenceLine.getTime().getStart();
				LocalTime targetEnd = referenceLine.getTime().getEnd();

				TimedLine fullIntersect = linesAPI.intersected(timedLines, targetStart, targetEnd);

				if (fullIntersect != null && !lineToAdjust.equals(fullIntersect)) {
					continue;
				}

				TimedLine startIntersect = linesAPI.intersected(timedLines, targetStart);
				TimedLine endIntersect = linesAPI.intersected(timedLines, targetEnd);

				if (startIntersect == null || originalTime.equals(startIntersect.getTime())) {
					originalTime.setStart(targetStart);
				} else {
					originalTime.setStart(startIntersect.getTime().getEnd());
				}

				if (endIntersect == null || originalTime.getStart().equals(endIntersect.getTime().getStart())) {
					originalTime.setEnd(targetEnd);
				} else {
					originalTime.setEnd(endIntersect.getTime().getStart());
				}
			}
		}

		expandLongLines(timedLines, referenceLines, 1500);
	}

	/**
	 * Expand lines in the adjusted file that should be displayed during 2 lines of the
	 * reference file
	 * 
	 * @param adjustedLines the adjusted lines (ascending sort)
	 * @param referenceLines the reference lines (ascending sort)
	 */
	private static void expandLongLines(List adjustedLines,
			List referenceLines, int delay) {

		TimedLinesAPI linesAPI = new TimedLinesAPI();
		for (int i = 0; i < adjustedLines.size(); i++) {

			TimedObject currentElement = adjustedLines.get(i).getTime();

			int index = linesAPI.findByTime(referenceLines, currentElement);
			if (index >= 0) {

				int nextReferenceIndex = index + 1;
				if (nextReferenceIndex < referenceLines.size() && i + 1 < adjustedLines.size()) {

					TimedObject nextReference = referenceLines.get(nextReferenceIndex).getTime();
					TimedObject nextElement = adjustedLines.get(i + 1).getTime();

					if (linesAPI.isEqualsOrAfter(currentElement, nextReference)
							&& linesAPI.getDelay(currentElement.getEnd(), nextReference.getStart()) < delay
							&& linesAPI.isEqualsOrAfter(nextReference, nextElement)) {

						currentElement.setEnd(nextReference.getEnd());
					}
				}
			}
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy