Fix missing IPTV categories hidden by zero channelCount filter.
Show all live categories even when the full stream list is incomplete or category ids do not match exactly; harden Xtream list parsing. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
cf9cdd43db
commit
6a0eee0313
2 changed files with 51 additions and 12 deletions
|
|
@ -150,12 +150,27 @@ export class XtreamClient {
|
|||
|
||||
const res = await fetch(url, {
|
||||
headers: { "User-Agent": "MediaCluster/1.0" },
|
||||
signal: AbortSignal.timeout(20000),
|
||||
// Grote live-lijsten kunnen langer duren dan 20s
|
||||
signal: AbortSignal.timeout(
|
||||
params.action === "get_live_streams" && !params.category_id ? 60_000 : 25_000
|
||||
),
|
||||
});
|
||||
if (!res.ok) {
|
||||
throw new Error(`Xtream API HTTP ${res.status}`);
|
||||
}
|
||||
const data = (await res.json()) as T;
|
||||
// Nooit een niet-array cachen voor list-actions (Xtream geeft soms auth/error-object)
|
||||
if (
|
||||
(params.action === "get_live_categories" ||
|
||||
params.action === "get_live_streams" ||
|
||||
params.action === "get_vod_categories" ||
|
||||
params.action === "get_vod_streams" ||
|
||||
params.action === "get_series_categories" ||
|
||||
params.action === "get_series") &&
|
||||
!Array.isArray(data)
|
||||
) {
|
||||
throw new Error(`Xtream API gaf geen lijst terug voor ${params.action}`);
|
||||
}
|
||||
this.cacheSet(cacheKey, data);
|
||||
return data;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,24 +71,38 @@ export async function getViewerIptvCategories(viewerId: string, config: Config)
|
|||
const xt = await loadXtreamForToken(tokenId, config);
|
||||
if (!xt?.line.enableLive) throw new AppError("FORBIDDEN", "Live TV niet beschikbaar", 403);
|
||||
|
||||
const cats = await xt.client.getLiveCategories().catch(() => []);
|
||||
const streams = await xt.client.getLiveStreams().catch(() => []);
|
||||
const catsRaw = await xt.client.getLiveCategories().catch(() => null);
|
||||
const streamsRaw = await xt.client.getLiveStreams().catch(() => null);
|
||||
const cats = Array.isArray(catsRaw) ? catsRaw : [];
|
||||
const streams = Array.isArray(streamsRaw) ? streamsRaw : [];
|
||||
|
||||
const counts = new Map<string, number>();
|
||||
for (const s of streams) {
|
||||
const key = String(s.category_id ?? "");
|
||||
const key = normalizeIptvCategoryId(s.category_id);
|
||||
if (!key) continue;
|
||||
counts.set(key, (counts.get(key) ?? 0) + 1);
|
||||
}
|
||||
|
||||
// Geen filter op channelCount>0: de volledige get_live_streams-lijst is soms
|
||||
// incompleet of category_id-format wijkt af, waardoor echte categorieën verdwenen.
|
||||
const categories = cats
|
||||
.map((c) => ({
|
||||
id: String(c.category_id),
|
||||
name: friendlyCategoryName(c.category_name),
|
||||
rawName: c.category_name,
|
||||
channelCount: counts.get(String(c.category_id)) ?? 0,
|
||||
}))
|
||||
.filter((c) => c.channelCount > 0)
|
||||
.sort((a, b) => a.name.localeCompare(b.name, "nl"));
|
||||
.map((c) => {
|
||||
const id = normalizeIptvCategoryId(c.category_id);
|
||||
return {
|
||||
id,
|
||||
name: friendlyCategoryName(c.category_name),
|
||||
rawName: c.category_name,
|
||||
channelCount: counts.get(id) ?? 0,
|
||||
};
|
||||
})
|
||||
.filter((c) => c.id.length > 0)
|
||||
.sort((a, b) => {
|
||||
// Categorieën mét getelde zenders eerst, daarna alfabetisch
|
||||
if ((a.channelCount > 0) !== (b.channelCount > 0)) {
|
||||
return a.channelCount > 0 ? -1 : 1;
|
||||
}
|
||||
return a.name.localeCompare(b.name, "nl");
|
||||
});
|
||||
|
||||
if (streams.length > 0) {
|
||||
categories.unshift({
|
||||
|
|
@ -102,6 +116,15 @@ export async function getViewerIptvCategories(viewerId: string, config: Config)
|
|||
return { categories };
|
||||
}
|
||||
|
||||
/** Xtream geeft category_id soms als number, "12" of "12.0". */
|
||||
export function normalizeIptvCategoryId(raw: unknown): string {
|
||||
if (raw == null) return "";
|
||||
const s = String(raw).trim();
|
||||
if (!s) return "";
|
||||
if (/^\d+(\.0+)?$/.test(s)) return String(parseInt(s, 10));
|
||||
return s;
|
||||
}
|
||||
|
||||
function sortChannels(streams: XtreamLiveStream[]): XtreamLiveStream[] {
|
||||
return [...streams].sort((a, b) => (a.num ?? a.stream_id) - (b.num ?? b.stream_id));
|
||||
}
|
||||
|
|
@ -124,6 +147,7 @@ export async function getViewerIptvChannels(
|
|||
!categoryId || categoryId === "__all__"
|
||||
? await xt.client.getLiveStreams()
|
||||
: await xt.client.getLiveStreams(categoryId);
|
||||
if (!Array.isArray(streams)) streams = [];
|
||||
streams = sortChannels(streams);
|
||||
|
||||
const epgTitles = includeEpg
|
||||
|
|
|
|||
Loading…
Reference in a new issue