v1.0.4 — fix mid-play swap: don't block Player callback thread
onAVStarted was calling _fetch_sb_segments synchronously, which subprocess.run()'s our sidecar — up to 8s of blocking on Kodi's serialized player event thread. When the user started a new video while one was playing, pv.youtube's stream resolve for the new video raced with our blocked callback and the new play got dropped as "unplayable item" before pv.youtube could finish. Moved the segment fetch + skip loop into a background thread that starts from onAVStarted and returns instantly. Player callbacks now clear in microseconds.
This commit is contained in:
parent
11eecbccc2
commit
63962b29b5
3 changed files with 22 additions and 15 deletions
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<addon id="plugin.video.torttube"
|
||||
name="torttube"
|
||||
version="1.0.3"
|
||||
version="1.0.4"
|
||||
provider-name="Sulkta-Coop">
|
||||
<requires>
|
||||
<import addon="xbmc.python" version="3.0.0"/>
|
||||
|
|
|
|||
|
|
@ -89,6 +89,10 @@ class TorttubePlayerMonitor(xbmc.Player):
|
|||
self._stop_event = threading.Event()
|
||||
|
||||
def onAVStarted(self) -> None:
|
||||
# Must return fast — Kodi's player event dispatch is serialized and
|
||||
# blocking here while pv.youtube is mid-resolve can drop the next
|
||||
# play. The actual segment fetch + skip loop runs in a background
|
||||
# thread.
|
||||
try:
|
||||
playing_file = self.getPlayingFile()
|
||||
except Exception:
|
||||
|
|
@ -98,26 +102,29 @@ class TorttubePlayerMonitor(xbmc.Player):
|
|||
return
|
||||
with self._lock:
|
||||
if yt_id == self._current_id:
|
||||
# Same video — segments already armed, skip thread alive.
|
||||
return
|
||||
self._stop_event.set()
|
||||
self._current_id = yt_id
|
||||
|
||||
segments = _fetch_sb_segments(yt_id)
|
||||
_log(f"armed {len(segments)} segments for {yt_id} (file={playing_file[:80]})")
|
||||
if not segments:
|
||||
return
|
||||
|
||||
stop_event = threading.Event()
|
||||
thread = threading.Thread(
|
||||
target=self._skip_loop,
|
||||
args=(yt_id, segments, stop_event),
|
||||
self._stop_event = stop_event
|
||||
self._skip_thread = threading.Thread(
|
||||
target=self._arm_and_skip,
|
||||
args=(yt_id, playing_file, stop_event),
|
||||
daemon=True,
|
||||
)
|
||||
with self._lock:
|
||||
self._stop_event = stop_event
|
||||
self._skip_thread = thread
|
||||
thread.start()
|
||||
self._skip_thread.start()
|
||||
|
||||
def _arm_and_skip(
|
||||
self,
|
||||
yt_id: str,
|
||||
playing_file: str,
|
||||
stop_event: threading.Event,
|
||||
) -> None:
|
||||
segments = _fetch_sb_segments(yt_id)
|
||||
_log(f"armed {len(segments)} segments for {yt_id} (file={playing_file[:80]})")
|
||||
if not segments or stop_event.is_set():
|
||||
return
|
||||
self._skip_loop(yt_id, segments, stop_event)
|
||||
|
||||
def onPlayBackStopped(self) -> None:
|
||||
self._stop_event.set()
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ resolver = "2"
|
|||
members = ["crates/torttube-sidecar"]
|
||||
|
||||
[workspace.package]
|
||||
version = "1.0.3"
|
||||
version = "1.0.4"
|
||||
edition = "2021"
|
||||
license = "GPL-3.0-or-later"
|
||||
authors = ["Cobb <cobb@sulkta.com>"]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue