OverviewFilesCommitsIssuesDocsReleasesPeers

stremio-vsembed commits / 06884c8

Subtitle sync: a repair that cannot run never withdraws a track (0.27.1)
Production, first backfill on No Other Choice: all 32 tracks "unfixable" and withdrawn from the library. Cause, reproduced on stortron: ffsubsync 0.5.1 picks the output format from the output EXTENSION -- for "….srt.synced" and for /dev/null it logs "unsupported output format", writes nothing, exits 0, after printing the offsets. No corrected file -> no re-check -> the code read "could not repair" as "unfixable". The test fake wrote any filename, which hid it. All 32 restored from subs-original/ (byte-identical) before the fix. - Output names keep .srt (synced-<name>, recheck-<name>). - A correction not written, or a re-check that cannot run, leaves the track alone ("unmeasured"); only a correction that re-measures OUT of tolerance withdraws one. - The fake now behaves like ffsubsync (non-.srt output -> nothing written). Red first: 6 failures against the shipped code. Verified with real ffsubsync on a temp copy of No Other Choice: en -2.25 s/ 1.001, de -18.5 s, fr -8.93 s all corrected, cue counts unchanged, re-run skips, summary "3 tracks · 3 corrected". Suite 485. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
06884c84dde31d1e0beb52398abcfa995751d992
by plankatron <fleeky@gmail.com> · 2026-10-05T01:48:36.000Z

Changed files (2) +79 -7

addon.py +19 -3
……@@ -619,7 +619,7 @@ TV_GENRES = [
619619
620620MANIFEST = {
621621 "id": "org.tabertron.vsembed",
622 "version": "0.27.0",
622 "version": "0.27.1",
623623 "name": "Tabertron VsEmbed",
624624 "description": "Self-hosted extractor with TMDB browse + search, played via vidcore and HdHub",
625625 "types": ["movie", "series"],
……@@ -2330,7 +2330,12 @@ def _sync_subtitles(dest: str) -> dict:
23302330 for path in todo:
23312331 if _cancel_requested():
23322332 break
2333 corrected = os.path.join(work, os.path.basename(path) + ".synced")
2333 # Output names KEEP the .srt extension: ffsubsync picks the output
2334 # format from it, and for anything else (".synced", /dev/null) it
2335 # logs "unsupported output format", writes nothing and exits 0 --
2336 # after printing the offsets. That withdrew 32 good tracks once.
2337 name = os.path.basename(path)
2338 corrected = os.path.join(work, "synced-" + name)
23342339 m = _subsync_measure(ref, path, corrected)
23352340 if not m:
23362341 result["unmeasured"] += 1
……@@ -2340,8 +2345,19 @@ def _sync_subtitles(dest: str) -> dict:
23402345 if _in_tolerance(m):
23412346 entry["status"] = "in_sync"
23422347 else:
2343 check = (_subsync_measure(ref, corrected, None)
2348 check = (_subsync_measure(ref, corrected,
2349 os.path.join(work, "recheck-" + name))
23442350 if os.path.exists(corrected) else None)
2351 if check is None:
2352 # No correction to judge, or no way to judge it: we
2353 # could not repair this track, which is NOT evidence it
2354 # is unfixable. A check that cannot run must not cost
2355 # the user a subtitle.
2356 log.warning("subsync: %s measured (%.2f s) but not "
2357 "repairable here -- left as it was",
2358 name, m["offset"])
2359 result["unmeasured"] += 1
2360 continue
23452361 entry["original"] = _keep_original(path)
23462362 if _in_tolerance(check):
23472363 # Across filesystems (/tmp -> the USB library), so a
test_subtitle_sync.py +60 -4
……@@ -60,11 +60,20 @@ def _measure(table):
6060 calls = []
6161
6262 def measure(ref, srt_in, srt_out):
63 calls.append(os.path.basename(srt_in))
64 offset, scale = table[os.path.basename(srt_in)]
65 if srt_out:
63 # Faithful to ffsubsync 0.5.1, measured 2026-10-04: the output format
64 # comes from the output EXTENSION. Anything but .srt here (".synced",
65 # /dev/null) logs "unsupported output format", writes NOTHING, exits
66 # 0 -- after printing the offsets. The first fake wrote any name, which
67 # is how 32 good tracks got withdrawn in production.
68 name = os.path.basename(srt_in)
69 for prefix in ("synced-", "recheck-"):
70 if name.startswith(prefix):
71 name = name[len(prefix):] + ".synced"
72 calls.append(name)
73 offset, scale = table[name]
74 if srt_out and srt_out.endswith(".srt"):
6675 with open(srt_out, "w") as f:
67 f.write(f"corrected {os.path.basename(srt_in)}")
76 f.write(f"corrected {name}")
6877 return {"offset": offset, "scale": scale, "score": 1.0}
6978 return measure, calls
7079
……@@ -222,3 +231,50 @@ def test_parse_reads_ffsubsyncs_own_output():
222231 assert addon._parse_subsync(out) == {"offset": -2.25, "scale": 1.001,
223232 "score": 343738.036}
224233 assert addon._parse_subsync("garbage") is None
234
235
236def test_a_correction_that_was_not_written_leaves_the_track_alone(lib, monkeypatch):
237 """The production failure: offsets measured, corrected file never
238 written. That is "could not repair", not "unfixable"."""
239 dest, _ = lib
240 fr = _srt(dest, "fr")
241
242 def measured_but_not_written(ref, srt_in, srt_out):
243 return {"offset": -8.9, "scale": 1.0, "score": 1.0}
244 monkeypatch.setattr(addon, "_subsync_measure", measured_but_not_written)
245 result = addon._sync_subtitles(dest)
246 assert result["unfixable"] == 0 and result["unmeasured"] == 1
247 assert os.path.exists(fr) and open(fr).read().startswith("1\n")
248
249
250def test_a_recheck_that_cannot_run_leaves_the_track_alone(lib, monkeypatch):
251 dest, _ = lib
252 fr = _srt(dest, "fr")
253 calls = []
254
255 def first_ok_then_none(ref, srt_in, srt_out):
256 calls.append(srt_in)
257 if len(calls) == 1:
258 with open(srt_out, "w") as f:
259 f.write("corrected")
260 return {"offset": -8.9, "scale": 1.0, "score": 1.0}
261 return None
262 monkeypatch.setattr(addon, "_subsync_measure", first_ok_then_none)
263 result = addon._sync_subtitles(dest)
264 assert result["unfixable"] == 0 and result["unmeasured"] == 1
265 assert open(fr).read().startswith("1\n")
266
267
268def test_every_output_path_keeps_the_srt_extension(lib, monkeypatch):
269 dest, _ = lib
270 _srt(dest, "fr")
271 outs = []
272
273 def record(ref, srt_in, srt_out):
274 outs.append(srt_out)
275 if srt_out:
276 open(srt_out, "w").write("x")
277 return {"offset": -8.9 if len(outs) == 1 else 0.0, "scale": 1.0, "score": 1.0}
278 monkeypatch.setattr(addon, "_subsync_measure", record)
279 addon._sync_subtitles(dest)
280 assert outs and all(o and o.endswith(".srt") for o in outs)