eaf_base_api 4.1.0__tar.gz → 4.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: eaf_base_api
3
- Version: 4.1.0
3
+ Version: 4.1.1
4
4
  Summary: A base API for EchterAlsFake's Porn APIs
5
5
  Author: Johannes Habel
6
6
  Author-email: Johannes Habel <EchterAlsFake@proton.me>
@@ -102,6 +102,47 @@ def _contains_http_status(error: BaseException, status_code: int) -> bool:
102
102
  return False
103
103
 
104
104
 
105
+ def _normalize_packet_timestamps(
106
+ packet: Any,
107
+ offsets: dict[int, int],
108
+ last_dts: dict[int, int],
109
+ last_durations: dict[int, int],
110
+ ) -> int:
111
+ """Keep remuxed packet timestamps continuous across HLS discontinuities."""
112
+ if packet.dts is None:
113
+ return 0
114
+
115
+ stream_index = packet.stream.index
116
+ raw_dts = packet.dts
117
+ offset = offsets.get(stream_index, 0)
118
+ normalized_dts = raw_dts + offset
119
+ previous_dts = last_dts.get(stream_index)
120
+ correction = 0
121
+
122
+ if previous_dts is not None:
123
+ step = max(1, last_durations.get(stream_index, packet.duration or 1))
124
+ expected_dts = previous_dts + step
125
+ try:
126
+ ten_seconds = max(1, int(10 / float(packet.time_base)))
127
+ except (TypeError, ValueError, ZeroDivisionError):
128
+ ten_seconds = step * 240
129
+
130
+ discontinuity_threshold = max(step * 10, ten_seconds)
131
+ delta = normalized_dts - expected_dts
132
+ if normalized_dts <= previous_dts or abs(delta) > discontinuity_threshold:
133
+ correction = -delta
134
+ offset += correction
135
+ offsets[stream_index] = offset
136
+ normalized_dts = raw_dts + offset
137
+
138
+ packet.dts = normalized_dts
139
+ if packet.pts is not None:
140
+ packet.pts += offset
141
+ last_dts[stream_index] = normalized_dts
142
+ last_durations[stream_index] = max(1, packet.duration or 1)
143
+ return correction
144
+
145
+
105
146
  @dataclass(frozen=True, slots=True)
106
147
  class RequestCacheKey:
107
148
  """Identity of a cacheable HTTP request without retaining credentials."""
@@ -2798,7 +2839,11 @@ class BaseCore:
2798
2839
  if remux:
2799
2840
  self.logger.info(f"Remuxing TS to MP4: input={tmp_path} output={path}")
2800
2841
  # Offload heavy CPU/IO bound task
2801
- await asyncio.to_thread(self._convert_ts_to_mp4, tmp_path, path, callback_remux, ios_support)
2842
+ try:
2843
+ await asyncio.to_thread(self._convert_ts_to_mp4, tmp_path, path, callback_remux, ios_support)
2844
+ except Exception:
2845
+ self._safe_remove(path)
2846
+ raise
2802
2847
  # This is important, because not all players can play MPEG-TS AND I want to write
2803
2848
  # metadata to the files, and this doesn't work without a container.
2804
2849
  self._safe_remove(tmp_path)
@@ -2928,6 +2973,9 @@ class BaseCore:
2928
2973
  progress_step = max(1, total // 10) if total else 0
2929
2974
  next_progress_log = progress_step if progress_step else 0
2930
2975
  current_progress = 0
2976
+ timestamp_offsets: dict[int, int] = {}
2977
+ last_dts: dict[int, int] = {}
2978
+ last_durations: dict[int, int] = {}
2931
2979
 
2932
2980
  for idx, packet in enumerate(packets):
2933
2981
  pkt_size = getattr(packet, "size", 0) or 0
@@ -2938,6 +2986,20 @@ class BaseCore:
2938
2986
  callback(current_progress, total)
2939
2987
  continue
2940
2988
 
2989
+ timestamp_correction = _normalize_packet_timestamps(
2990
+ packet,
2991
+ timestamp_offsets,
2992
+ last_dts,
2993
+ last_durations,
2994
+ )
2995
+ if timestamp_correction:
2996
+ self.logger.info(
2997
+ "Normalized HLS timestamp discontinuity: stream=%s correction=%s time_base=%s",
2998
+ packet.stream.index,
2999
+ timestamp_correction,
3000
+ packet.time_base,
3001
+ )
3002
+
2941
3003
  if packet.stream == in_video:
2942
3004
  packet.stream = out_video
2943
3005
  output.mux(packet)
@@ -4,7 +4,7 @@ build-backend = "uv_build"
4
4
 
5
5
  [project]
6
6
  name = "eaf_base_api"
7
- version = "4.1.0"
7
+ version = "4.1.1"
8
8
  description = "A base API for EchterAlsFake's Porn APIs"
9
9
  readme = { file = "README.md", content-type = "text/markdown" }
10
10
  requires-python = ">=3.12"
File without changes
File without changes