Fix IPTV categories timing out under per-category stream fetches.
Return all live categories immediately; only wait briefly for bulk counts and keep a single Alle zenders entry. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
318f613b40
commit
1834d42bd5
2 changed files with 16 additions and 36 deletions
|
|
@ -205,7 +205,8 @@ export class XtreamClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
getLiveCategories() {
|
getLiveCategories() {
|
||||||
return this.getJson<XtreamCategory[]>({ action: "get_live_categories" }, 5 * 60_000);
|
// Korte TTL: na IPTV-wijzigingen snel weer alle planken zichtbaar
|
||||||
|
return this.getJson<XtreamCategory[]>({ action: "get_live_categories" }, 60_000);
|
||||||
}
|
}
|
||||||
|
|
||||||
getLiveStreams(categoryId?: string) {
|
getLiveStreams(categoryId?: string) {
|
||||||
|
|
|
||||||
|
|
@ -82,9 +82,14 @@ export async function getViewerIptvCategories(viewerId: string, config: Config)
|
||||||
const xt = await loadXtreamForToken(tokenId, config);
|
const xt = await loadXtreamForToken(tokenId, config);
|
||||||
if (!xt?.line.enableLive) throw new AppError("FORBIDDEN", "Live TV niet beschikbaar", 403);
|
if (!xt?.line.enableLive) throw new AppError("FORBIDDEN", "Live TV niet beschikbaar", 403);
|
||||||
|
|
||||||
|
// Categorieën zijn leidend — nooit blokkeren op de (soms trage/incomplete) bulk-zenderlijst
|
||||||
const catsRaw = await xt.client.getLiveCategories().catch(() => null);
|
const catsRaw = await xt.client.getLiveCategories().catch(() => null);
|
||||||
const streamsRaw = await xt.client.getLiveStreams().catch(() => null);
|
|
||||||
const cats = Array.isArray(catsRaw) ? catsRaw : [];
|
const cats = Array.isArray(catsRaw) ? catsRaw : [];
|
||||||
|
|
||||||
|
const streamsRaw = await Promise.race([
|
||||||
|
xt.client.getLiveStreams().catch(() => null),
|
||||||
|
new Promise<null>((resolve) => setTimeout(() => resolve(null), 6_000)),
|
||||||
|
]);
|
||||||
const streams = Array.isArray(streamsRaw) ? streamsRaw : [];
|
const streams = Array.isArray(streamsRaw) ? streamsRaw : [];
|
||||||
|
|
||||||
const counts = new Map<string, number>();
|
const counts = new Map<string, number>();
|
||||||
|
|
@ -94,23 +99,10 @@ export async function getViewerIptvCategories(viewerId: string, config: Config)
|
||||||
counts.set(key, (counts.get(key) ?? 0) + 1);
|
counts.set(key, (counts.get(key) ?? 0) + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Als de bulk-lijst incompleet is: per categorie bijtellen (gecached in XtreamClient)
|
|
||||||
const idsNeedingCount = cats
|
|
||||||
.map((c) => normalizeIptvCategoryId(c.category_id))
|
|
||||||
.filter((id) => id && (counts.get(id) ?? 0) === 0);
|
|
||||||
if (idsNeedingCount.length > 0) {
|
|
||||||
await mapPool(idsNeedingCount, 8, async (id) => {
|
|
||||||
const list = await xt.client.getLiveStreams(id).catch(() => null);
|
|
||||||
if (Array.isArray(list) && list.length > 0) {
|
|
||||||
counts.set(id, list.length);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const categories = cats
|
const categories = cats
|
||||||
.map((c) => {
|
.map((c) => {
|
||||||
const id = normalizeIptvCategoryId(c.category_id);
|
const id = normalizeIptvCategoryId(c.category_id);
|
||||||
const rawName = c.category_name ?? "";
|
const rawName = String(c.category_name ?? "");
|
||||||
return {
|
return {
|
||||||
id,
|
id,
|
||||||
name: friendlyCategoryName(rawName),
|
name: friendlyCategoryName(rawName),
|
||||||
|
|
@ -118,26 +110,25 @@ export async function getViewerIptvCategories(viewerId: string, config: Config)
|
||||||
channelCount: counts.get(id) ?? 0,
|
channelCount: counts.get(id) ?? 0,
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
// Alleen lege ids + provider-verzamelcategorieën weg (niet echte genre-planken)
|
// Toon ALLE echte planken; alleen pure verzamelbakken van de provider weg
|
||||||
.filter((c) => c.id.length > 0 && !isAggregateCategoryName(c.rawName))
|
.filter((c) => c.id.length > 0 && !isAggregateCategoryName(c.rawName))
|
||||||
.sort((a, b) => a.name.localeCompare(b.name, "nl"));
|
.sort((a, b) => a.name.localeCompare(b.name, "nl"));
|
||||||
|
|
||||||
// Één verzamelknop van ons — niet van de IPTV-provider
|
// Één eigen verzamelknop
|
||||||
const totalChannels =
|
|
||||||
streams.length > 0
|
|
||||||
? streams.length
|
|
||||||
: [...counts.values()].reduce((a, b) => a + b, 0);
|
|
||||||
categories.unshift({
|
categories.unshift({
|
||||||
id: "__all__",
|
id: "__all__",
|
||||||
name: "Alle zenders",
|
name: "Alle zenders",
|
||||||
rawName: "Alle zenders",
|
rawName: "Alle zenders",
|
||||||
channelCount: totalChannels,
|
channelCount: streams.length,
|
||||||
});
|
});
|
||||||
|
|
||||||
return { categories };
|
return { categories };
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Alleen echte verzamelbakken (Alles / All / Alle zenders), geen Sport/Films/etc. */
|
/**
|
||||||
|
* Alleen pure verzamelbakken (Alles / All), géén "All Sports", "Alle films", enz.
|
||||||
|
* Exacte match ná friendly-name (landcode-prefix al gestript).
|
||||||
|
*/
|
||||||
export function isAggregateCategoryName(raw: string): boolean {
|
export function isAggregateCategoryName(raw: string): boolean {
|
||||||
const n = friendlyCategoryName(raw).toLowerCase().replace(/\s+/g, " ").trim();
|
const n = friendlyCategoryName(raw).toLowerCase().replace(/\s+/g, " ").trim();
|
||||||
return (
|
return (
|
||||||
|
|
@ -162,18 +153,6 @@ export function normalizeIptvCategoryId(raw: unknown): string {
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function mapPool<T>(items: T[], concurrency: number, fn: (item: T) => Promise<void>) {
|
|
||||||
const queue = [...items];
|
|
||||||
const workers = Array.from({ length: Math.min(concurrency, queue.length) }, async () => {
|
|
||||||
while (queue.length > 0) {
|
|
||||||
const item = queue.shift();
|
|
||||||
if (item === undefined) return;
|
|
||||||
await fn(item);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
await Promise.all(workers);
|
|
||||||
}
|
|
||||||
|
|
||||||
function sortChannels(streams: XtreamLiveStream[]): XtreamLiveStream[] {
|
function sortChannels(streams: XtreamLiveStream[]): XtreamLiveStream[] {
|
||||||
return [...streams].sort((a, b) => (a.num ?? a.stream_id) - (b.num ?? b.stream_id));
|
return [...streams].sort((a, b) => (a.num ?? a.stream_id) - (b.num ?? b.stream_id));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue