1.0.0+kodimov.12: no duplicate TV guide instance; retry the web player registration

- The TV guide setup always wrote instance-settings-91.xml, so on a box
  already set up with another instance (from the original add-on) both read
  channels.m3u8 and every channel showed twice. The setup now rewrites the
  instance that already reads the list, and the service deletes extra ones
  at start (keeps the one that isn't 91, which holds what was watched) and
  restarts IPTV Simple when nothing plays.
- The web player registration for the new servers got one 401 error page
  and gave up, and the TV guide list then lacked LALIGA, DAZN LALIGA and
  Liga de Campeones until the next export 6 hours later. The registration
  is tried 3 times, and after a failure the service exports the list again
  every half hour, up to 6 times.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
XoséandClaude Opus 5.5 committed 2026-11-13 14:38:12 +01:00
1 parent b3bc561c35
commit 69a37ff064
5 files changed
+71 -8

No files matched your search

+52 -5
View File
@@ -320,7 +320,9 @@ def _web_device_id(m, account_number):
m.cache.save_file('web_device_id.conf', own)
return own
token = _autologin(m, uuid.uuid4().hex)
status, text = _soter(m, 'post', SOTER_DEVICES_URL.format(account=account_number), token, attempts=1)
# Like the other calls, it at times gets a 401 error page for no reason
# (seen 2026-11-13: refused once, registered on the next try)
status, text = _soter(m, 'post', SOTER_DEVICES_URL.format(account=account_number), token, attempts=3)
device_id = text.strip().strip('"')
if status not in (200, 201) or len(device_id) != 32:
raise ValueError('device registration refused ({})'.format(status))
@@ -385,6 +387,16 @@ def new_api_ready(m):
return False
def new_api_failed(profile_dir):
"""True if the last try to reach the new servers failed and none ever
worked: the TV guide list was written without LALIGA, DAZN LALIGA and
Liga de Campeones, so the service exports it again sooner."""
import glob
for stamp in glob.glob(os.path.join(profile_dir, 'account_*', 'new_api.stamp')):
if not os.path.exists(os.path.join(os.path.dirname(stamp), 'new_api.json')): return True
return False
def _token_life(m, token):
"""Seconds until the token expires (negative: expired or unreadable)."""
props = m.get_token_properties(token) if token else None
@@ -523,16 +535,51 @@ def restart_iptv_simple():
window.clearProperty(IPTV_RESTART_PROPERTY)
def has_iptv_instance(pvr_dir, addon_id):
"""True if an IPTV Simple instance reads this add-on's channel list."""
SETUP_INSTANCE_FILE = 'instance-settings-91.xml' # written by "Create IPTV settings"
def iptv_instance_files(pvr_dir, addon_id):
"""The IPTV Simple instance files that read this add-on's channel list,
the one to keep first: a box set up before (with the original add-on, or
by hand) has its instance besides 91, and that one holds what was watched;
91 is the one the TV guide setup writes."""
import glob
res = []
for filename in glob.glob(os.path.join(pvr_dir, 'instance-settings-*.xml')):
try:
with io.open(filename, 'r', encoding='utf-8') as f:
if 'plugin.video.{}/channels.m3u8'.format(addon_id) in f.read(): return True
if 'plugin.video.{}/channels.m3u8'.format(addon_id) in f.read(): res.append(filename)
except (IOError, OSError):
pass
return False
def number(filename):
try:
return int(os.path.basename(filename)[len('instance-settings-'):-len('.xml')])
except ValueError:
return 0
res.sort(key=lambda f: (os.path.basename(f) == SETUP_INSTANCE_FILE, number(f)))
return res
def has_iptv_instance(pvr_dir, addon_id):
"""True if an IPTV Simple instance reads this add-on's channel list."""
return bool(iptv_instance_files(pvr_dir, addon_id))
def remove_duplicate_instances(pvr_dir, addon_id):
"""Two instances reading the same channel list put every channel twice in
the TV guide. That happened when the TV guide setup (instance 91) was run
on a box already set up with another instance. Keeps one and deletes the
others. Returns True if a file was deleted (IPTV Simple must be restarted
to drop it)."""
removed = False
for filename in iptv_instance_files(pvr_dir, addon_id)[1:]:
try:
os.remove(filename)
LOG('remove_duplicate_instances: removed {}'.format(filename))
removed = True
except OSError as e:
LOG('remove_duplicate_instances: {}: {}'.format(filename, e))
return removed
def ensure_channel_numbers(marker):