Release 0.5.8

This commit is contained in:
hnzio committed 2026-10-23 07:47:27 +02:00
1 parent 22ae3a68d6
commit 2851c7d936
33 files changed
+1263 -272

No files matched your search

+76 -10
View File
@@ -1,5 +1,6 @@
import { randomUUID } from 'node:crypto';
import type Database from 'better-sqlite3';
import { createPiece } from './content.js';
import { getDatabase } from './database.js';
import { detectVideoProvider, type PresentationMode } from './presentation.js';
@@ -42,6 +43,7 @@ export type PieceUsage = {
status: UsageStatus;
durationSeconds: number;
title: string;
outputTitle: string;
intro: string;
research: string;
moderation: string;
@@ -129,6 +131,7 @@ export type PieceUsageInput = {
export type PieceUsageUpdate = {
title: string;
outputTitle: string;
intro: string;
research: string;
moderation: string;
@@ -185,6 +188,7 @@ type UsageRow = {
status: UsageStatus;
duration_seconds: number;
title_snapshot: string;
output_title_snapshot: string;
intro_snapshot: string;
research_snapshot: string;
moderation_snapshot: string;
@@ -376,6 +380,7 @@ function usageFromRow(row: UsageRow): PieceUsage {
status: row.status,
durationSeconds: row.duration_seconds,
title: row.title_snapshot,
outputTitle: row.output_title_snapshot,
intro: row.intro_snapshot,
research: row.research_snapshot,
moderation: row.moderation_snapshot,
@@ -478,7 +483,7 @@ export function getShow(id: string): Show | null {
const usageRows = database
.prepare(
`SELECT u.id, u.show_section_id, u.original_piece_id, u.position, u.status,
u.duration_seconds, u.title_snapshot, u.intro_snapshot, u.research_snapshot,
u.duration_seconds, u.title_snapshot, u.output_title_snapshot, u.intro_snapshot, u.research_snapshot,
u.moderation_snapshot, u.transition_snapshot, u.presentation_snapshot,
u.presentation_mode_snapshot, u.presentation_source_label_snapshot,
u.presentation_source_url_snapshot, u.presentation_source_timecode_snapshot,
@@ -805,7 +810,7 @@ export function addPieceUsage(
sectionBelongsToShow(database, showId, sectionId, input.sectionId === null);
const piece = database
.prepare(
`SELECT p.title, p.duration_seconds, p.intro, p.research_text, p.moderation_notes,
`SELECT p.title, p.output_title, p.duration_seconds, p.intro, p.research_text, p.moderation_notes,
p.transition_text, p.presentation_text, p.status, p.presentation_mode,
COALESCE(ps.label, '') AS presentation_source_label,
COALESCE(ps.url, '') AS presentation_source_url,
@@ -818,6 +823,7 @@ export function addPieceUsage(
.get(input.pieceId) as
| {
title: string;
output_title: string;
duration_seconds: number;
intro: string;
research_text: string;
@@ -849,14 +855,14 @@ export function addPieceUsage(
database
.prepare(
`INSERT INTO piece_usages (id, show_section_id, original_piece_id, position, status,
duration_seconds, title_snapshot, intro_snapshot, research_snapshot,
duration_seconds, title_snapshot, output_title_snapshot, intro_snapshot, research_snapshot,
moderation_snapshot, transition_snapshot, presentation_snapshot,
presentation_mode_snapshot, presentation_source_label_snapshot,
presentation_source_url_snapshot, presentation_source_timecode_snapshot,
presentation_title_meta_snapshot, presentation_description_snapshot,
presentation_image_url_snapshot, presentation_site_name_snapshot,
presentation_provider_snapshot, presentation_provider_id_snapshot, own_notes, is_reserve)
VALUES (?, ?, ?, ?, 'open', ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, '', ?)`
VALUES (?, ?, ?, ?, 'open', ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, '', ?)`
)
.run(
id,
@@ -865,6 +871,7 @@ export function addPieceUsage(
ids.length,
piece.duration_seconds,
piece.title,
piece.output_title,
piece.intro,
piece.research_text,
piece.moderation_notes,
@@ -888,6 +895,60 @@ export function addPieceUsage(
})();
}
export function createAndAddPieceUsage(
showId: string,
expectedRevision: number,
commandId: string,
input: Omit<PieceUsageInput, 'pieceId'> & { title: string; templateId: string }
) {
if (!commandId || commandId.length > 200) throw new Error('INVALID_COMMAND');
const title = cleanText(input.title, 200, 'title', true);
const database = getDatabase();
return database.transaction(() => {
const duplicate = database
.prepare(
`SELECT piece_id, usage_id, result_revision FROM planning_commands
WHERE show_id = ? AND command_id = ?`
)
.get(showId, commandId) as
{ piece_id: string; usage_id: string; result_revision: number } | undefined;
if (duplicate) {
return {
pieceId: duplicate.piece_id,
usageId: duplicate.usage_id,
revision: duplicate.result_revision,
duplicate: true
};
}
const pieceId = createPiece(input.templateId || 'blank');
database
.prepare(
`UPDATE pieces SET title = ?, status = 'ready', revision = revision + 1,
updated_at_utc = ? WHERE id = ?`
)
.run(title, new Date().toISOString(), pieceId);
const usage = addPieceUsage(showId, expectedRevision, {
pieceId,
sectionId: input.sectionId,
position: input.position
});
database
.prepare(
`INSERT INTO planning_commands
(show_id, command_id, piece_id, usage_id, result_revision, created_at_utc)
VALUES (?, ?, ?, ?, ?, ?)`
)
.run(showId, commandId, pieceId, usage.id, usage.revision, new Date().toISOString());
return {
pieceId,
usageId: usage.id,
revision: usage.revision,
duplicate: false
};
})();
}
export function duplicatePieceUsage(
showId: string,
usageId: string,
@@ -899,7 +960,7 @@ export function duplicatePieceUsage(
const sourceSection = usageSection(database, showId, usageId);
const source = database
.prepare(
`SELECT original_piece_id, status, duration_seconds, title_snapshot, intro_snapshot,
`SELECT original_piece_id, status, duration_seconds, title_snapshot, output_title_snapshot, intro_snapshot,
research_snapshot, moderation_snapshot, transition_snapshot, presentation_snapshot,
presentation_mode_snapshot, presentation_source_label_snapshot,
presentation_source_url_snapshot, presentation_source_timecode_snapshot,
@@ -922,14 +983,14 @@ export function duplicatePieceUsage(
database
.prepare(
`INSERT INTO piece_usages (id, show_section_id, original_piece_id, position, status,
duration_seconds, title_snapshot, intro_snapshot, research_snapshot,
duration_seconds, title_snapshot, output_title_snapshot, intro_snapshot, research_snapshot,
moderation_snapshot, transition_snapshot, presentation_snapshot,
presentation_mode_snapshot, presentation_source_label_snapshot,
presentation_source_url_snapshot, presentation_source_timecode_snapshot,
presentation_title_meta_snapshot, presentation_description_snapshot,
presentation_image_url_snapshot, presentation_site_name_snapshot,
presentation_provider_snapshot, presentation_provider_id_snapshot, own_notes, is_reserve)
VALUES (?, ?, ?, ?, 'open', ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
VALUES (?, ?, ?, ?, 'open', ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
)
.run(
id,
@@ -938,6 +999,7 @@ export function duplicatePieceUsage(
ids.length,
source.duration_seconds,
source.title_snapshot,
source.output_title_snapshot,
source.intro_snapshot,
source.research_snapshot,
source.moderation_snapshot,
@@ -973,7 +1035,7 @@ export function refreshPieceUsageFromOriginal(
usageSection(database, showId, usageId);
const source = database
.prepare(
`SELECT p.title, p.duration_seconds, p.intro, p.research_text,
`SELECT p.title, p.output_title, p.duration_seconds, p.intro, p.research_text,
p.moderation_notes, p.transition_text, p.presentation_text, p.presentation_mode,
COALESCE(ps.label, '') AS presentation_source_label,
COALESCE(ps.url, '') AS presentation_source_url,
@@ -988,6 +1050,7 @@ export function refreshPieceUsageFromOriginal(
.get(usageId) as
| {
title: string;
output_title: string;
duration_seconds: number;
intro: string;
research_text: string;
@@ -1009,7 +1072,7 @@ export function refreshPieceUsageFromOriginal(
if (!source) throw new Error('ORIGINAL_NOT_FOUND');
database
.prepare(
`UPDATE piece_usages SET title_snapshot = ?, duration_seconds = ?, intro_snapshot = ?,
`UPDATE piece_usages SET title_snapshot = ?, output_title_snapshot = ?, duration_seconds = ?, intro_snapshot = ?,
research_snapshot = ?, moderation_snapshot = ?, transition_snapshot = ?,
presentation_snapshot = ?, presentation_mode_snapshot = ?,
presentation_source_label_snapshot = ?, presentation_source_url_snapshot = ?,
@@ -1020,6 +1083,7 @@ export function refreshPieceUsageFromOriginal(
)
.run(
source.title,
source.output_title,
source.duration_seconds,
source.intro,
source.research_text,
@@ -1049,6 +1113,7 @@ export function updatePieceUsage(
input: PieceUsageUpdate
): MutationResult {
const title = cleanText(input.title, 200, 'title', true);
const outputTitle = cleanText(input.outputTitle, 300, 'output_title');
const duration = validDuration(input.durationSeconds)!;
const values = {
intro: cleanText(input.intro, 50_000, 'intro'),
@@ -1074,7 +1139,7 @@ export function updatePieceUsage(
usageSection(database, showId, usageId);
database
.prepare(
`UPDATE piece_usages SET title_snapshot = ?, intro_snapshot = ?, research_snapshot = ?,
`UPDATE piece_usages SET title_snapshot = ?, output_title_snapshot = ?, intro_snapshot = ?, research_snapshot = ?,
moderation_snapshot = ?, transition_snapshot = ?, presentation_snapshot = ?,
presentation_mode_snapshot = ?, presentation_source_label_snapshot = ?,
presentation_source_url_snapshot = ?, presentation_source_timecode_snapshot = ?,
@@ -1085,6 +1150,7 @@ export function updatePieceUsage(
)
.run(
title,
outputTitle,
values.intro,
values.research,
values.moderation,