1.0.0+kodimov.13: updates don't stop live TV; IPTV Simple restarts only when idle
- After an add-on update the new service found port 57031 still held by the old proxy for a moment and moved to a random port; the stream playing kept asking 57031 for its manifest and died a minute later (.49, 2026-11-13). The proxy now waits up to 10 s for the port, and SO_REUSEADDR / allow_reuse_address are set before binding (upstream set them after, so they did nothing). - IPTV Simple is restarted only when nothing plays and no key was pressed for 30 s: on .49 a channel opened during a restart failed with an error. - After start or update, changed instance files (duplicate removed, refresh timer) are picked up by the guide reload instead of a restart of their own; the service restarts IPTV Simple itself only if that reload didn't happen within 10 minutes. 3 restarts after kodimov.12 -> 2. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
69a37ff064
commit
7873b50203
6 files changed
+63
-15
No files matched your search
@@ -492,25 +492,51 @@ def reload_iptv_guide(pvr_dir, addon_id):
|
||||
restart it with the hourly timer. Restarting stops live TV, so this does
|
||||
nothing if something is playing."""
|
||||
import xbmc
|
||||
monitor, player = xbmc.Monitor(), xbmc.Player()
|
||||
if player.isPlaying(): return
|
||||
monitor = xbmc.Monitor()
|
||||
if not wait_until_idle(timeout=600): return
|
||||
if not set_iptv_refresh(pvr_dir, addon_id, 1): return
|
||||
try:
|
||||
restart_iptv_simple()
|
||||
monitor.waitForAbort(90)
|
||||
finally:
|
||||
set_iptv_refresh(pvr_dir, addon_id, 60)
|
||||
while player.isPlaying(): # until then it keeps reloading every minute
|
||||
if monitor.waitForAbort(10): return
|
||||
if not monitor.abortRequested():
|
||||
if wait_until_idle(): # until then it keeps reloading every minute
|
||||
restart_iptv_simple()
|
||||
|
||||
|
||||
def is_idle(seconds=30):
|
||||
"""Nothing playing and nobody pressed a key for a while. Restarting IPTV
|
||||
Simple stops live TV, and a channel opened while it restarts fails."""
|
||||
import xbmc
|
||||
return not xbmc.Player().isPlaying() and xbmc.getGlobalIdleTime() >= seconds
|
||||
|
||||
|
||||
def wait_until_idle(timeout=None):
|
||||
"""Waits until is_idle(). False if Kodi is closing or the timeout ran out."""
|
||||
import xbmc
|
||||
monitor = xbmc.Monitor()
|
||||
end = time.time() + timeout if timeout else None
|
||||
while not is_idle():
|
||||
if monitor.waitForAbort(5) or (end and time.time() > end): return False
|
||||
return not monitor.abortRequested()
|
||||
|
||||
|
||||
# IPTV Simple is shared with other add-ons (Radios de Vigo keeps its own
|
||||
# instance for the Radio section). Restarting it restarts every instance, so
|
||||
# the add-ons take turns: whoever restarts it says so in this property of
|
||||
# Kodi's home window (the time it started), and the others wait.
|
||||
IPTV_RESTART_PROPERTY = 'pvr.iptvsimple.restarting'
|
||||
# When this add-on last restarted it, so the service can skip a restart that
|
||||
# one from the plugin (the guide reload) already did
|
||||
LAST_RESTART_PROPERTY = 'kodimovplus.iptvsimple.restarted'
|
||||
|
||||
|
||||
def last_iptv_restart():
|
||||
import xbmcgui
|
||||
try:
|
||||
return float(xbmcgui.Window(10000).getProperty(LAST_RESTART_PROPERTY) or 0)
|
||||
except ValueError:
|
||||
return 0
|
||||
|
||||
|
||||
def restart_iptv_simple():
|
||||
@@ -526,6 +552,7 @@ def restart_iptv_simple():
|
||||
if not busy: break
|
||||
xbmc.sleep(1000)
|
||||
window.setProperty(IPTV_RESTART_PROPERTY, str(time.time()))
|
||||
window.setProperty(LAST_RESTART_PROPERTY, str(time.time()))
|
||||
try:
|
||||
for enabled in (False, True):
|
||||
xbmc.executeJSONRPC(json.dumps({'jsonrpc': '2.0', 'id': 1, 'method': 'Addons.SetAddonEnabled',
|
||||
|
||||
@@ -835,7 +835,7 @@ def export_epg_now(quiet=False, reload_guide=False):
|
||||
if True: #not addon.getSettingBool('use_external_epg'):
|
||||
notify(addon.getLocalizedString(30311))
|
||||
changed = m.export_epg_to_xml(epg_filename, addon.getSettingInt('export_days'), report_progress, only_subscribed)
|
||||
if changed and reload_guide:
|
||||
if reload_guide == 'force' or (changed and reload_guide):
|
||||
from . import kodimov
|
||||
try:
|
||||
pvr_dir = translatePath(xbmcaddon.Addon('pvr.iptvsimple').getAddonInfo('profile'))
|
||||
@@ -941,7 +941,7 @@ def router(paramstring):
|
||||
elif params['action'] == 'create_iptv_settings':
|
||||
create_iptv_settings()
|
||||
elif params['action'] == 'export_epg_now':
|
||||
export_epg_now(quiet=params.get('quiet') == '1', reload_guide=params.get('reload_guide') == '1')
|
||||
export_epg_now(quiet=params.get('quiet') == '1', reload_guide=params.get('reload_guide'))
|
||||
elif params['action'] == 'to_wishlist':
|
||||
to_wishlist(params)
|
||||
elif 'iptv' in params['action']:
|
||||
|
||||
+13
-1
@@ -274,6 +274,10 @@ PORT = 57031
|
||||
|
||||
class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
|
||||
daemon_threads = True
|
||||
# KodiMov+: must be set before the server binds (setting it on the
|
||||
# instance afterwards did nothing): connections of the previous proxy
|
||||
# left in TIME_WAIT would otherwise keep the port taken
|
||||
allow_reuse_address = True
|
||||
|
||||
class Proxy(object):
|
||||
started = False
|
||||
@@ -281,8 +285,8 @@ class Proxy(object):
|
||||
def check_port(self, port=0, default=False):
|
||||
try:
|
||||
with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s:
|
||||
s.bind((HOST, port))
|
||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
s.bind((HOST, port))
|
||||
return s.getsockname()[1]
|
||||
except:
|
||||
return default
|
||||
@@ -292,6 +296,14 @@ class Proxy(object):
|
||||
return
|
||||
|
||||
port = self.check_port(PORT)
|
||||
# KodiMov+: when the add-on updates, the previous service may still
|
||||
# hold the port for a few seconds. Wait for it: a stream that is
|
||||
# playing keeps asking this port for its manifest, and dies if the
|
||||
# proxy moves to another one.
|
||||
for _ in range(20):
|
||||
if port: break
|
||||
time.sleep(0.5)
|
||||
port = self.check_port(PORT)
|
||||
if not port:
|
||||
port = self.check_port(0)
|
||||
LOG('port: {}'.format(port))
|
||||
|
||||
Reference in new issue
Block a user