import Database from 'better-sqlite3'; import { mkdirSync } from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { getPaths } from './config.mjs'; export function seed(databasePath = getPaths().database) { mkdirSync(path.dirname(databasePath), { recursive: true }); const database = new Database(databasePath); database.pragma('foreign_keys = ON'); const seedDatabase = database.transaction(() => { const replace = (table, columns, rows) => { const placeholders = columns.map(() => '?').join(', '); const update = columns .filter((column) => column !== 'id') .map((column) => `${column} = excluded.${column}`) .join(', '); const statement = database.prepare( `INSERT INTO ${table} (${columns.join(', ')}) VALUES (${placeholders}) ON CONFLICT(id) DO UPDATE SET ${update}` ); for (const row of rows) statement.run(...row); }; replace( 'section_categories', ['id', 'name', 'color', 'sort_order'], [ ['cat-jc', 'JC', '#67DDE8', 0], ['cat-reaction', 'Reaction', '#E7B957', 1], ['cat-game', 'Spiel', '#9B8AFB', 2] ] ); replace( 'formats', ['id', 'name'], [ ['format-find', 'Fundstück'], ['format-talk', 'Gespräch'], ['format-quiz', 'Quiz'] ] ); replace( 'topics', ['id', 'name'], [ ['topic-gaming', 'Gaming'], ['topic-film', 'Film & Serien'], ['topic-internet', 'Netzkultur'] ] ); const timestamp = '2026-10-21T10:00:00.000Z'; replace( 'pieces', [ 'id', 'title', 'summary', 'format_id', 'status', 'duration_seconds', 'intro', 'moderation_notes', 'transition_text', 'presentation_text', 'created_at_utc', 'updated_at_utc' ], [ [ 'piece-console', 'Die kleine Konsole mit dem großen Dreh', 'Ein ungewöhnliches Handheld-Konzept sorgt für Diskussionsstoff.', 'format-find', 'ready', 480, 'Eine Kurbel, ein gelber Knopf und sehr viel Charme: Was macht kleine Hardware spannend?', 'Erst Formfaktor zeigen, dann über bewusst gesetzte Grenzen sprechen.', 'Von der kleinen Konsole geht es zur ganz großen Leinwand.', 'Weniger Leistung, mehr Idee.', timestamp, timestamp ], [ 'piece-effects', 'Warum praktische Effekte wieder auffallen', 'Neue Produktionen mischen Miniaturen, Masken und digitale Nacharbeit.', 'format-talk', 'ready', 720, 'Man sieht manchmal gerade deshalb weniger Trick, weil wieder mehr vor der Kamera passiert.', 'Beispiele vergleichen, nicht analog gegen digital ausspielen.', 'Nach so viel Handarbeit wird es spielerisch.', 'Wenn das Licht wirklich auf ein Modell fällt.', timestamp, timestamp ], [ 'piece-quiz', 'Drei Sounds, ein Spiel', 'Kurzes Audioquiz mit bekannten Inventar- und Menüklängen.', 'format-quiz', 'preparing', 600, 'Augen zu: Erkennt ihr das Spiel nur an seinem Inventar?', 'Drei Runden, Auflösung jeweils erst nach den Tipps.', 'Damit sind die Ohren warm für unser erstes Spiel.', 'Welches Spiel steckt hinter diesem Klang?', timestamp, timestamp ], [ 'piece-pixels', 'Pixelart, die nicht nostalgisch sein will', 'Wie moderne Spiele Pixel als Material statt als Rückblick einsetzen.', 'format-find', 'ready', 540, 'Pixel sind längst keine technische Notlösung mehr.', 'Großaufnahmen vorbereiten, drei Stilmittel benennen.', 'Genug Theorie, jetzt spielen wir selbst.', 'Pixel als bewusste Entscheidung.', timestamp, timestamp ] ] ); database.prepare('DELETE FROM piece_topics WHERE piece_id LIKE ?').run('piece-%'); const addTopic = database.prepare( 'INSERT INTO piece_topics (piece_id, topic_id) VALUES (?, ?)' ); for (const values of [ ['piece-console', 'topic-gaming'], ['piece-effects', 'topic-film'], ['piece-quiz', 'topic-gaming'], ['piece-pixels', 'topic-gaming'], ['piece-pixels', 'topic-internet'] ]) addTopic.run(...values); database.prepare('DELETE FROM piece_category_suggestions WHERE piece_id LIKE ?').run('piece-%'); const addCategory = database.prepare( 'INSERT INTO piece_category_suggestions (piece_id, category_id) VALUES (?, ?)' ); for (const values of [ ['piece-console', 'cat-jc'], ['piece-effects', 'cat-reaction'], ['piece-quiz', 'cat-jc'], ['piece-pixels', 'cat-reaction'] ]) addCategory.run(...values); replace( 'piece_sources', ['id', 'piece_id', 'label', 'url', 'timecode_seconds', 'position'], [ [ 'source-console', 'piece-console', 'Herstellerseite', 'https://example.com/console', null, 0 ], [ 'source-effects', 'piece-effects', 'Werkstattbericht', 'https://example.com/effects', 95, 0 ], ['source-pixels', 'piece-pixels', 'Galerie', 'https://example.com/pixels', null, 0] ] ); replace( 'shows', [ 'id', 'title', 'starts_at_utc', 'twitch_title', 'announcement_text', 'status', 'created_at_utc', 'updated_at_utc' ], [ [ 'show-october', 'Herbstsendung: Kleine Welten', '2026-10-23T18:00:00.000Z', 'Kleine Welten, große Ideen | heinzlive', 'Heute schauen wir auf Hardware mit Kurbel, handgemachte Filmtricks und Pixel mit Haltung.', 'ready', timestamp, timestamp ] ] ); replace( 'show_sections', ['id', 'show_id', 'name', 'category_id', 'position', 'explicit_duration_seconds'], [ ['section-start', 'show-october', 'Ankommen & Warmwerden', 'cat-jc', 0, 300], ['section-stories', 'show-october', 'Kleine Welten', 'cat-reaction', 1, null], ['section-game', 'show-october', 'Spiel 1', 'cat-game', 2, 3600] ] ); replace( 'piece_usages', [ 'id', 'show_section_id', 'original_piece_id', 'position', 'status', 'duration_seconds', 'title_snapshot', 'intro_snapshot', 'moderation_snapshot', 'transition_snapshot', 'presentation_snapshot', 'is_reserve' ], [ [ 'usage-console', 'section-stories', 'piece-console', 0, 'current', 480, 'Die kleine Konsole mit dem großen Dreh', 'Eine Kurbel, ein gelber Knopf und sehr viel Charme: Was macht kleine Hardware spannend?', 'Erst Formfaktor zeigen, dann über bewusst gesetzte Grenzen sprechen.', 'Von der kleinen Konsole geht es zur ganz großen Leinwand.', 'Weniger Leistung, mehr Idee.', 0 ], [ 'usage-effects', 'section-stories', 'piece-effects', 1, 'open', 720, 'Warum praktische Effekte wieder auffallen', 'Man sieht manchmal gerade deshalb weniger Trick, weil wieder mehr vor der Kamera passiert.', 'Beispiele vergleichen, nicht analog gegen digital ausspielen.', 'Nach so viel Handarbeit wird es spielerisch.', 'Wenn das Licht wirklich auf ein Modell fällt.', 0 ], [ 'usage-reserve', 'section-stories', 'piece-pixels', 2, 'open', 540, 'Pixelart, die nicht nostalgisch sein will', 'Pixel sind längst keine technische Notlösung mehr.', 'Großaufnahmen vorbereiten, drei Stilmittel benennen.', 'Genug Theorie, jetzt spielen wir selbst.', 'Pixel als bewusste Entscheidung.', 1 ] ] ); }); seedDatabase(); database.close(); console.log(`Example data ready: ${databasePath}`); } if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { seed(); }