content: # -*- coding: utf-8 -*-
  from datetime import timedelta
  import logging
  from typing import Any, cast, List, Optional
  
  try:
      import cchardet
  except:  # noqa: E722
      cchardet = None
  try:
      import chardet
  except:  # noqa: E722
      chardet = None
  try:
      import charset_normalizer
  except:  # noqa: E722
      charset_normalizer = None
  import pysubs2
  from ffsubsync.sklearn_shim import TransformerMixin
  import srt
  
  from ffsubsync.constants import (
      DEFAULT_ENCODING,
      DEFAULT_MAX_SUBTITLE_SECONDS,
      DEFAULT_START_SECONDS,
  )
  from ffsubsync.file_utils import open_file
  from ffsubsync.generic_subtitles import GenericSubtitle, GenericSubtitlesFile, SubsMixin
  
  logging.basicConfig(level=logging.INFO)
  logger: logging.Logger = logging.getLogger(__name__)
  
  
  def _preprocess_subs(
      subs,
      max_subtitle_seconds: Optional[int] = None,
      start_seconds: int = 0,
      tolerant: bool = True,
  ) -> List[GenericSubtitle]:
      subs_list = []
      start_time = timedelta(seconds=start_seconds)
      max_duration = timedelta(days=1)
      if max_subtitle_seconds is not None:
          max_duration = timedelta(seconds=max_subtitle_seconds)
      subs = iter(subs)
      while True:
          try:
              next_sub = GenericSubtitle.wrap_inner_subtitle(next(subs))
              if next_sub.start < start_time:
                  continue
              next_sub.end = min(next_sub.end, next_sub.start + max_duration)
              subs_list.append(next_sub)
          # We don't catch SRTParseError here b/c that is typically raised when we
          # are trying to parse with the wrong encoding, in which case we might
          # be able to try another one on the *entire* set of subtitles elsewhere.
          except ValueError as e:
              if tolerant:
                  logger.warning(e)
                  continue
              else:
                  raise
          except StopIteration:
              break
      return subs_list
  
  
  class GenericSubtitleParser(SubsMixin, TransformerMixin):
      def __init__(
          self,
          fmt: str = "srt",
          encoding: str = "infer",
          caching: bool = False,
          max_subtitle_seconds: Optional[int] = None,
          start_seconds: int = 0,
          skip_ssa_info: bool = False,
          strict: bool = False,
      ) -> None:
          super(self.__class__, self).__init__()
          self.sub_format: str = fmt
          self.encoding: str = encoding
          self.caching: bool = caching
          self.fit_fname: Optional[str] = None
          self.detected_encoding_: Optional[str] = None
          self.max_subtitle_seconds: Optional[int] = max_subtitle_seconds
          self.start_seconds: int = start_seconds
          # FIXME: hack to get tests to pass; remove
          self._skip_ssa_info: bool = skip_ssa_info
          self._strict: bool = strict
  
      def fit(self, fname: str, *_) -> "GenericSubtitleParser":
          if self.caching and self.fit_fname == ("<stdin>" if fname is None else fname):
              return self
          encodings_to_try = (self.encoding,)
          with open_file(fname, "rb") as f:
              subs = f.read()
          if self.encoding == "infer":
              for chardet_lib in (cchardet, charset_normalizer, chardet):
                  if chardet_lib is not None:
                      try:
                          detected_encoding = cast(
                              Optional[str], chardet_lib.detect(subs)["encoding"]
                          )
                      except:  # noqa: E722
                          continue
                      if detected_encoding is not None:
                          self.detected_encoding_ = detected_encoding
                          encodings_to_try = (detected_encoding,)
                          break
              assert self.detected_encoding_ is not None
              logger.info("detected encoding: %s" % self.detected_encoding_)
          exc = None
          for encoding in encodings_to_try:
              try:
                  decoded_subs = subs.decode(encoding, errors="replace").strip()
                  if self.sub_format == "srt":
                      parsed_subs = srt.parse(
                          decoded_subs, ignore_errors=not self._strict
                      )
                  elif self.sub_format in ("ass", "ssa", "sub", "vtt"):
                      parsed_subs = pysubs2.SSAFile.from_string(decoded_subs)
                  else:
                      raise NotImplementedError(
                          "unsupported format: %s" % self.sub_format
                      )
                  extra_generic_subtitle_file_kwargs = {}
                  if isinstance(parsed_subs, pysubs2.SSAFile):
                      extra_generic_subtitle_file_kwargs.update(
                          dict(
                              styles=parsed_subs.styles,
                              # pysubs2 on Python >= 3.6 doesn't support this
                              fonts_opaque=getattr(parsed_subs, "fonts_opaque", None),
                              info=parsed_subs.info if not self._skip_ssa_info else None,
                          )
                      )
                  self.subs_ = GenericSubtitlesFile(
                      _preprocess_subs(
                          parsed_subs,
                          max_subtitle_seconds=self.max_subtitle_seconds,
                          start_seconds=self.start_seconds,
                      ),
                      sub_format=self.sub_format,
                      encoding=encoding,
                      **extra_generic_subtitle_file_kwargs,
                  )
                  self.fit_fname = "<stdin>" if fname is None else fname
                  if len(encodings_to_try) > 1:
                      self.detected_encoding_ = encoding
                      logger.info("detected encoding: %s" % self.detected_encoding_)
                  return self
              except Exception as e:
                  exc = e
                  continue
          raise exc
  
      def transform(self, *_) -> GenericSubtitlesFile:
          return self.subs_
  
  
  def make_subtitle_parser(
      fmt: str,
      encoding: str = DEFAULT_ENCODING,
      caching: bool = False,
      max_subtitle_seconds: int = DEFAULT_MAX_SUBTITLE_SECONDS,
      start_seconds: int = DEFAULT_START_SECONDS,
      **kwargs: Any,
  ) -> GenericSubtitleParser:
      return GenericSubtitleParser(
          fmt=fmt,
          encoding=encoding,
          caching=caching,
          max_subtitle_seconds=max_subtitle_seconds,
          start_seconds=start_seconds,
          skip_ssa_info=kwargs.get("skip_ssa_info", False),
          strict=kwargs.get("strict", False),
      )
  