184 lines
4.7 KiB
TypeScript
184 lines
4.7 KiB
TypeScript
import { getDatabase } from './database.js';
|
|
import { getShow as getShowById, listShows } from './shows.js';
|
|
|
|
export type PreviewPiece = {
|
|
id: string;
|
|
title: string;
|
|
summary: string;
|
|
status: 'inbox' | 'preparing' | 'ready' | 'archived';
|
|
durationSeconds: number;
|
|
format: string;
|
|
category: string;
|
|
categoryColor: string;
|
|
topics: string[];
|
|
sourceCount: number;
|
|
};
|
|
|
|
export type PreviewUsage = {
|
|
id: string;
|
|
title: string;
|
|
durationSeconds: number;
|
|
status: 'open' | 'current' | 'done' | 'skipped';
|
|
intro: string;
|
|
moderation: string;
|
|
transition: string;
|
|
isReserve: boolean;
|
|
};
|
|
|
|
export type PreviewSection = {
|
|
id: string;
|
|
name: string;
|
|
category: string;
|
|
categoryColor: string;
|
|
durationSeconds: number;
|
|
usages: PreviewUsage[];
|
|
};
|
|
|
|
export type PreviewShow = {
|
|
id: string;
|
|
title: string;
|
|
startsAtUtc: string;
|
|
startsAtBerlin: string;
|
|
twitchTitle: string;
|
|
announcement: string;
|
|
status: string;
|
|
sections: PreviewSection[];
|
|
totalDurationSeconds: number;
|
|
};
|
|
|
|
type PieceRow = {
|
|
id: string;
|
|
title: string;
|
|
summary: string;
|
|
status: PreviewPiece['status'];
|
|
duration_seconds: number;
|
|
format: string;
|
|
category: string | null;
|
|
category_color: string | null;
|
|
topics: string | null;
|
|
source_count: number;
|
|
};
|
|
|
|
export function getPieces(): PreviewPiece[] {
|
|
const rows = getDatabase()
|
|
.prepare(
|
|
`SELECT
|
|
p.id,
|
|
p.title,
|
|
p.summary,
|
|
p.status,
|
|
p.duration_seconds,
|
|
COALESCE(f.name, 'Ohne Format') AS format,
|
|
(
|
|
SELECT c.name FROM piece_category_suggestions pcs
|
|
JOIN section_categories c ON c.id = pcs.category_id
|
|
WHERE pcs.piece_id = p.id ORDER BY c.sort_order LIMIT 1
|
|
) AS category,
|
|
(
|
|
SELECT c.color FROM piece_category_suggestions pcs
|
|
JOIN section_categories c ON c.id = pcs.category_id
|
|
WHERE pcs.piece_id = p.id ORDER BY c.sort_order LIMIT 1
|
|
) AS category_color,
|
|
(
|
|
SELECT group_concat(t.name, '||') FROM piece_topics pt
|
|
JOIN topics t ON t.id = pt.topic_id WHERE pt.piece_id = p.id
|
|
) AS topics,
|
|
(SELECT count(*) FROM piece_sources ps WHERE ps.piece_id = p.id) AS source_count
|
|
FROM pieces p
|
|
LEFT JOIN formats f ON f.id = p.format_id
|
|
WHERE p.status IN ('preparing', 'ready')
|
|
ORDER BY CASE p.status WHEN 'ready' THEN 0 ELSE 1 END, p.title`
|
|
)
|
|
.all() as PieceRow[];
|
|
|
|
return rows.map((row) => ({
|
|
id: row.id,
|
|
title: row.title,
|
|
summary: row.summary,
|
|
status: row.status,
|
|
durationSeconds: row.duration_seconds,
|
|
format: row.format,
|
|
category: row.category ?? 'Offen',
|
|
categoryColor: row.category_color ?? '#8b8f98',
|
|
topics: row.topics?.split('||') ?? [],
|
|
sourceCount: row.source_count
|
|
}));
|
|
}
|
|
|
|
export function getShow(showId?: string): PreviewShow {
|
|
const latest = showId ? null : listShows().find((show) => show.startsAtUtc !== null);
|
|
const show = getShowById(showId ?? latest?.id ?? '');
|
|
if (!show) throw new Error('No preview show found. Run npm run db:seed.');
|
|
const sections: PreviewSection[] = show.sections.map((section) => ({
|
|
id: section.id,
|
|
name: section.name,
|
|
category: section.category ?? 'Offen',
|
|
categoryColor: section.categoryColor ?? '#8b8f98',
|
|
durationSeconds: section.durationSeconds,
|
|
usages: section.usages.map((usage) => ({
|
|
id: usage.id,
|
|
title: usage.title,
|
|
durationSeconds: usage.durationSeconds,
|
|
status: usage.status,
|
|
intro: usage.intro,
|
|
moderation: usage.moderation,
|
|
transition: usage.transition,
|
|
isReserve: false
|
|
}))
|
|
}));
|
|
const reserveHost = sections[Math.min(1, sections.length - 1)];
|
|
if (reserveHost) {
|
|
reserveHost.usages.push(
|
|
...show.reserve.map((usage) => ({
|
|
id: usage.id,
|
|
title: usage.title,
|
|
durationSeconds: usage.durationSeconds,
|
|
status: usage.status,
|
|
intro: usage.intro,
|
|
moderation: usage.moderation,
|
|
transition: usage.transition,
|
|
isReserve: true
|
|
}))
|
|
);
|
|
}
|
|
|
|
return {
|
|
id: show.id,
|
|
title: show.title,
|
|
startsAtUtc: show.startsAtUtc ?? '',
|
|
startsAtBerlin: show.startsAtUtc
|
|
? new Intl.DateTimeFormat('de-DE', {
|
|
timeZone: 'Europe/Berlin',
|
|
weekday: 'short',
|
|
day: '2-digit',
|
|
month: '2-digit',
|
|
year: 'numeric',
|
|
hour: '2-digit',
|
|
minute: '2-digit'
|
|
}).format(new Date(show.startsAtUtc))
|
|
: 'Termin offen',
|
|
twitchTitle: show.twitchTitle,
|
|
announcement: show.announcementText,
|
|
status: show.status,
|
|
sections,
|
|
totalDurationSeconds: show.totalDurationSeconds
|
|
};
|
|
}
|
|
|
|
export function getTaxonomy() {
|
|
const database = getDatabase();
|
|
return {
|
|
categories: database
|
|
.prepare('SELECT id, name, color FROM section_categories ORDER BY sort_order')
|
|
.all() as { id: string; name: string; color: string }[],
|
|
formats: database.prepare('SELECT id, name FROM formats ORDER BY name').all() as {
|
|
id: string;
|
|
name: string;
|
|
}[],
|
|
topics: database.prepare('SELECT id, name FROM topics ORDER BY name').all() as {
|
|
id: string;
|
|
name: string;
|
|
}[]
|
|
};
|
|
}
|