Proyecto LCX Dispatcharr multicuenta
Base Image Build / prepare (push) Has been cancelled
Build and Push Multi-Arch Docker Image / build-and-push (push) Has been cancelled
Frontend Tests / test (push) Has been cancelled
Base Image Build / docker (amd64, ubuntu-24.04) (push) Has been cancelled
Base Image Build / docker (arm64, ubuntu-24.04-arm) (push) Has been cancelled
Base Image Build / create-manifest (push) Has been cancelled

This commit is contained in:
root
2026-05-09 21:24:50 +02:00
commit f56b088643
721 changed files with 177870 additions and 0 deletions
+93
View File
@@ -0,0 +1,93 @@
"""
Enlaza streams de la cuenta secundaria (Javi) como respaldo en canales que solo tienen la principal (Boss).
Uso (después de refrescar las listas M3U en Dispatcharr):
cd /opt/dispatcharr && set -a && . ./.env && set +a && \\
./.venv/bin/python manage.py shell < scripts/link_backup_streams_boss_javi.py
Empareja por tvg-id; si falta, por nombre normalizado. Idempotente (no duplica enlaces).
IDs de cuenta: revisa en UI M3U Accounts si cambian (por defecto Boss=2, Javi=4).
"""
import re
from collections import defaultdict
from apps.channels.models import Channel, ChannelStream, Stream
BOSS_ACCOUNT_ID = 2
BACKUP_ACCOUNT_ID = 4
def norm_name(s):
if not s:
return ""
return re.sub(r"\s+", " ", str(s).strip().lower())
def run():
by_tvg = {}
by_name = defaultdict(list)
javi_count = 0
for s in Stream.objects.filter(
m3u_account_id=BACKUP_ACCOUNT_ID, is_stale=False
).iterator(chunk_size=500):
javi_count += 1
tid = (s.tvg_id or "").strip()
if tid and tid not in by_tvg:
by_tvg[tid] = s
by_name[norm_name(s.name)].append(s)
linked = 0
skipped_has_backup = 0
skipped_primary_not_boss = 0
missing = 0
for ch in Channel.objects.prefetch_related("channelstream_set__stream").iterator(
chunk_size=400
):
css = sorted(ch.channelstream_set.all(), key=lambda x: x.order)
if not css:
continue
if any(cs.stream.m3u_account_id == BACKUP_ACCOUNT_ID for cs in css):
skipped_has_backup += 1
continue
boss = css[0].stream
if boss.m3u_account_id != BOSS_ACCOUNT_ID:
skipped_primary_not_boss += 1
continue
match = None
tid = (boss.tvg_id or "").strip()
if tid and tid in by_tvg:
match = by_tvg[tid]
if match is None:
cands = by_name.get(norm_name(boss.name))
if cands:
match = cands[0]
if match:
max_order = max(cs.order for cs in css)
_, created = ChannelStream.objects.get_or_create(
channel=ch,
stream=match,
defaults={"order": max_order + 1},
)
if created:
linked += 1
else:
missing += 1
print(
"[link_backup_streams] Javi streams indexados:",
javi_count,
"| nuevos enlaces:",
linked,
"| ya tenían backup:",
skipped_has_backup,
"| primer stream no es Boss:",
skipped_primary_not_boss,
"| sin pareja:",
missing,
)
run()