|
|
|
@ -14,7 +14,7 @@ from babelfish import Language
|
|
|
|
|
from babelfish.exceptions import LanguageError
|
|
|
|
|
import pysubs2
|
|
|
|
|
|
|
|
|
|
__version__ = "0.1.1"
|
|
|
|
|
__version__ = "0.1.2"
|
|
|
|
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
@ -126,9 +126,28 @@ class FFprobeSubtitleStream:
|
|
|
|
|
self.avg_frame_rate = stream.get("avg_frame_rate")
|
|
|
|
|
self.time_base = stream.get("time_base")
|
|
|
|
|
self.tags = stream.get("tags", {})
|
|
|
|
|
self.duration = float(stream.get("duration", 0))
|
|
|
|
|
self.start_time = float(stream.get("start_time", 0))
|
|
|
|
|
self.duration_ts = int(stream.get("duration_ts", 0))
|
|
|
|
|
|
|
|
|
|
self.duration, self.duration_ts = 0, 0
|
|
|
|
|
|
|
|
|
|
# some subtitles streams miss the duration_ts field and only have tags->DURATION field
|
|
|
|
|
# fixme: we still don't know if "DURATION" is a common tag/key
|
|
|
|
|
if "DURATION" in self.tags:
|
|
|
|
|
try:
|
|
|
|
|
h, m, s = self.tags["DURATION"].split(":")
|
|
|
|
|
except ValueError:
|
|
|
|
|
pass
|
|
|
|
|
else:
|
|
|
|
|
self.duration = float(s) + float(m) * 60 + float(h) * 60 * 60
|
|
|
|
|
self.duration_ts = int(self.duration * 1000)
|
|
|
|
|
else:
|
|
|
|
|
try:
|
|
|
|
|
self.duration = float(stream.get("duration", 0))
|
|
|
|
|
self.duration_ts = int(stream.get("duration_ts", 0))
|
|
|
|
|
# some subtitles streams miss a duration completely and has "N/A" as value
|
|
|
|
|
except ValueError:
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
self.start_pts = int(stream.get("start_pts", 0))
|
|
|
|
|
|
|
|
|
|
self.disposition = FFprobeSubtitleDisposition(stream.get("disposition", {}))
|
|
|
|
@ -320,15 +339,21 @@ def check_integrity(
|
|
|
|
|
except (pysubs2.Pysubs2Error, UnicodeError, OSError, FileNotFoundError) as error:
|
|
|
|
|
raise InvalidFile(error) from error
|
|
|
|
|
else:
|
|
|
|
|
off = abs(int(sub[-1].end) - subtitle.duration_ts)
|
|
|
|
|
if off > abs(sec_offset_threshold) * 1000:
|
|
|
|
|
raise InvalidFile(
|
|
|
|
|
f"The last subtitle timestamp ({sub[-1].end/1000} sec) is {off/1000} sec ahead"
|
|
|
|
|
f" from the subtitle stream total duration ({subtitle.duration} sec)"
|
|
|
|
|
# ignore the duration check if the stream has no duration listed at all
|
|
|
|
|
if subtitle.duration_ts:
|
|
|
|
|
off = abs(int(sub[-1].end) - subtitle.duration_ts)
|
|
|
|
|
if off > abs(sec_offset_threshold) * 1000:
|
|
|
|
|
raise InvalidFile(
|
|
|
|
|
f"The last subtitle timestamp ({sub[-1].end/1000} sec) is {off/1000} sec ahead"
|
|
|
|
|
f" from the subtitle stream total duration ({subtitle.duration} sec)"
|
|
|
|
|
)
|
|
|
|
|
logger.debug("Integrity check passed (%d sec offset)", off / 1000)
|
|
|
|
|
else:
|
|
|
|
|
logger.warning(
|
|
|
|
|
"Ignoring duration check, subtitle stream has bad duration values: %s",
|
|
|
|
|
subtitle,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
logger.debug("Integrity check passed (%d sec offset)", off / 1000)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def to_srt(
|
|
|
|
|
source: str, output: Optional[str] = None, remove_source: bool = False
|
|
|
|
|