Include custom live lists in admin TV category order.
Lists appear alongside XUI categories and respect the saved order in apps.
This commit is contained in:
parent
c97374c9cb
commit
94bf98ded7
2 changed files with 84 additions and 59 deletions
|
|
@ -919,9 +919,10 @@ export default function SettingsPage() {
|
|||
Categorie-volgorde
|
||||
</h3>
|
||||
<p className="muted" style={{ marginBottom: "0.75rem" }}>
|
||||
Volgorde van links naar rechts in de TV-apps. Nieuwe XUI-categorieën komen onderaan.
|
||||
Volgorde van links naar rechts in de TV-apps. XUI-categorieën én Live-lijsten
|
||||
(ClearKey/DASH). Nieuwe items komen onderaan.
|
||||
{tvCategories.length === 0
|
||||
? " Geen actieve IPTV-lijn gevonden — koppel eerst een kijker."
|
||||
? " Geen categorieën — koppel een IPTV-lijn en/of maak Live-lijsten."
|
||||
: ""}
|
||||
</p>
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: "0.35rem" }}>
|
||||
|
|
|
|||
|
|
@ -84,24 +84,27 @@ export async function getViewerIptvCategories(viewerId: string, config: Config)
|
|||
throw new AppError("FORBIDDEN", "Geen TV-toegang", 403);
|
||||
}
|
||||
|
||||
const prefs = await getIptvSettings(config);
|
||||
|
||||
let categories: Array<{
|
||||
id: string;
|
||||
name: string;
|
||||
rawName: string;
|
||||
channelCount: number;
|
||||
}> = [];
|
||||
let streamCountForAll = 0;
|
||||
|
||||
if (xt?.line.enableLive) {
|
||||
const [catsRaw, streamsRaw, iptvPrefs] = await Promise.all([
|
||||
const [catsRaw, streamsRaw] = await Promise.all([
|
||||
xt.client.getLiveCategories().catch(() => [] as Awaited<ReturnType<typeof xt.client.getLiveCategories>>),
|
||||
Promise.race([
|
||||
xt.client.getLiveStreams().catch(() => [] as XtreamLiveStream[]),
|
||||
new Promise<XtreamLiveStream[]>((resolve) => setTimeout(() => resolve([]), 6_000)),
|
||||
]),
|
||||
getIptvSettings(config),
|
||||
]);
|
||||
const cats = Array.isArray(catsRaw) ? catsRaw : [];
|
||||
const streams: XtreamLiveStream[] = Array.isArray(streamsRaw) ? streamsRaw : [];
|
||||
streamCountForAll = streams.length;
|
||||
|
||||
const counts = new Map<string, number>();
|
||||
for (const s of streams) {
|
||||
|
|
@ -131,18 +134,6 @@ export async function getViewerIptvCategories(viewerId: string, config: Config)
|
|||
channelCount,
|
||||
}));
|
||||
}
|
||||
|
||||
const prefs = iptvPrefs ?? (await getIptvSettings(config));
|
||||
categories = applyIptvCategoryOrder(categories, prefs.categoryOrder);
|
||||
|
||||
if (prefs.showAlleZenders) {
|
||||
categories.unshift({
|
||||
id: "__all__",
|
||||
name: "Alle zenders",
|
||||
rawName: "Alle zenders",
|
||||
channelCount: streams.length,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const customLists = await listAssignedLiveListsForViewer(viewerId);
|
||||
|
|
@ -155,61 +146,94 @@ export async function getViewerIptvCategories(viewerId: string, config: Config)
|
|||
});
|
||||
}
|
||||
|
||||
categories = applyIptvCategoryOrder(categories, prefs.categoryOrder);
|
||||
|
||||
if (prefs.showAlleZenders && xt?.line.enableLive) {
|
||||
categories.unshift({
|
||||
id: "__all__",
|
||||
name: "Alle zenders",
|
||||
rawName: "Alle zenders",
|
||||
channelCount: streamCountForAll,
|
||||
});
|
||||
}
|
||||
|
||||
return { categories };
|
||||
}
|
||||
|
||||
/** Admin: categorieën uit een actieve IPTV-lijn voor volgorde-UI. */
|
||||
/** Admin: XUI-categorieën + custom live-lijsten voor volgorde-UI. */
|
||||
export async function listIptvCategoriesForAdmin(config: Config) {
|
||||
const iptvPrefs = await getIptvSettings(config);
|
||||
|
||||
let categories: Array<{
|
||||
id: string;
|
||||
name: string;
|
||||
rawName: string;
|
||||
channelCount: number;
|
||||
}> = [];
|
||||
|
||||
const line = await prisma.iptvLine.findFirst({
|
||||
where: { enableLive: true },
|
||||
orderBy: { updatedAt: "desc" },
|
||||
select: { id: true },
|
||||
});
|
||||
if (!line) return [] as Array<{ id: string; name: string; channelCount: number }>;
|
||||
|
||||
const xt = await loadXtreamByLineId(line.id, config);
|
||||
if (!xt?.line.enableLive) {
|
||||
return [] as Array<{ id: string; name: string; channelCount: number }>;
|
||||
if (line) {
|
||||
const xt = await loadXtreamByLineId(line.id, config);
|
||||
if (xt?.line.enableLive) {
|
||||
const [catsRaw, streamsRaw] = await Promise.all([
|
||||
xt.client.getLiveCategories().catch(() => []),
|
||||
Promise.race([
|
||||
xt.client.getLiveStreams().catch(() => [] as XtreamLiveStream[]),
|
||||
new Promise<XtreamLiveStream[]>((resolve) => setTimeout(() => resolve([]), 8_000)),
|
||||
]),
|
||||
]);
|
||||
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 = normalizeIptvCategoryId(s.category_id);
|
||||
if (!key) continue;
|
||||
counts.set(key, (counts.get(key) ?? 0) + 1);
|
||||
}
|
||||
|
||||
categories = cats
|
||||
.map((c) => {
|
||||
const id = normalizeIptvCategoryId(c.category_id);
|
||||
const rawName = String(c.category_name ?? "");
|
||||
return {
|
||||
id,
|
||||
name: friendlyCategoryName(rawName),
|
||||
rawName,
|
||||
channelCount: counts.get(id) ?? 0,
|
||||
};
|
||||
})
|
||||
.filter((c) => c.id.length > 0 && !isAggregateCategoryName(c.rawName));
|
||||
|
||||
if (categories.length === 0 && counts.size > 0) {
|
||||
categories = [...counts.entries()].map(([id, channelCount]) => ({
|
||||
id,
|
||||
name: `Groep ${id}`,
|
||||
rawName: `Groep ${id}`,
|
||||
channelCount,
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const [catsRaw, streamsRaw, iptvPrefs] = await Promise.all([
|
||||
xt.client.getLiveCategories().catch(() => []),
|
||||
Promise.race([
|
||||
xt.client.getLiveStreams().catch(() => [] as XtreamLiveStream[]),
|
||||
new Promise<XtreamLiveStream[]>((resolve) => setTimeout(() => resolve([]), 8_000)),
|
||||
]),
|
||||
getIptvSettings(config),
|
||||
]);
|
||||
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 = normalizeIptvCategoryId(s.category_id);
|
||||
if (!key) continue;
|
||||
counts.set(key, (counts.get(key) ?? 0) + 1);
|
||||
}
|
||||
|
||||
let categories = cats
|
||||
.map((c) => {
|
||||
const id = normalizeIptvCategoryId(c.category_id);
|
||||
const rawName = String(c.category_name ?? "");
|
||||
return {
|
||||
id,
|
||||
name: friendlyCategoryName(rawName),
|
||||
rawName,
|
||||
channelCount: counts.get(id) ?? 0,
|
||||
};
|
||||
})
|
||||
.filter((c) => c.id.length > 0 && !isAggregateCategoryName(c.rawName));
|
||||
|
||||
if (categories.length === 0 && counts.size > 0) {
|
||||
categories = [...counts.entries()].map(([id, channelCount]) => ({
|
||||
id,
|
||||
name: `Groep ${id}`,
|
||||
rawName: `Groep ${id}`,
|
||||
channelCount,
|
||||
}));
|
||||
const customLists = await prisma.liveChannelList.findMany({
|
||||
where: { enabled: true },
|
||||
orderBy: [{ sortOrder: "asc" }, { name: "asc" }],
|
||||
include: { _count: { select: { channels: { where: { enabled: true } } } } },
|
||||
});
|
||||
const { customCategoryId } = await import("./live-channels");
|
||||
for (const list of customLists) {
|
||||
categories.push({
|
||||
id: customCategoryId(list.id),
|
||||
name: list.name,
|
||||
rawName: list.name,
|
||||
channelCount: list._count.channels,
|
||||
});
|
||||
}
|
||||
|
||||
categories = applyIptvCategoryOrder(categories, iptvPrefs.categoryOrder);
|
||||
|
|
|
|||
Loading…
Reference in a new issue