Release 0.6.0
This commit is contained in:
1 parent
fd47910d09
commit
b80dded55d
40 files changed
+4080
-856
No files matched your search
+159
-14
@@ -120,6 +120,7 @@ export type Show = {
|
||||
revision: number;
|
||||
createdAtUtc: string;
|
||||
updatedAtUtc: string;
|
||||
completedAtUtc: string | null;
|
||||
sections: ShowSection[];
|
||||
reserve: PieceUsage[];
|
||||
totalDurationSeconds: number;
|
||||
@@ -193,6 +194,7 @@ type ShowRow = {
|
||||
revision: number;
|
||||
created_at_utc: string;
|
||||
updated_at_utc: string;
|
||||
completed_at_utc: string | null;
|
||||
};
|
||||
|
||||
type SectionRow = {
|
||||
@@ -201,6 +203,8 @@ type SectionRow = {
|
||||
category_id: string | null;
|
||||
category: string | null;
|
||||
category_color: string | null;
|
||||
category_name_snapshot: string | null;
|
||||
category_color_snapshot: string | null;
|
||||
position: number;
|
||||
explicit_duration_seconds: number | null;
|
||||
is_reserve_bank: number;
|
||||
@@ -713,7 +717,11 @@ function copyUsageRelations(database: Database.Database, sourceUsageId: string,
|
||||
.run(usageId, sourceUsageId);
|
||||
}
|
||||
|
||||
export function listShows(): ShowListItem[] {
|
||||
export function listShows(statuses?: ShowStatus[]): ShowListItem[] {
|
||||
const requestedStatuses = statuses?.filter((status) => SHOW_STATUSES.includes(status));
|
||||
const where = requestedStatuses?.length
|
||||
? `WHERE sh.status IN (${requestedStatuses.map(() => '?').join(', ')})`
|
||||
: '';
|
||||
return (
|
||||
getDatabase()
|
||||
.prepare(
|
||||
@@ -724,10 +732,11 @@ export function listShows(): ShowListItem[] {
|
||||
(SELECT sum(u.duration_seconds) FROM piece_usages u WHERE u.show_section_id = s.id))
|
||||
ELSE 0 END), 0) AS total_duration_seconds
|
||||
FROM shows sh LEFT JOIN show_sections s ON s.show_id = sh.id
|
||||
${where}
|
||||
GROUP BY sh.id
|
||||
ORDER BY sh.starts_at_utc IS NULL, sh.starts_at_utc DESC, sh.created_at_utc DESC`
|
||||
)
|
||||
.all() as {
|
||||
.all(...(requestedStatuses ?? [])) as {
|
||||
id: string;
|
||||
title: string;
|
||||
starts_at_utc: string | null;
|
||||
@@ -781,13 +790,14 @@ export function getShow(id: string): Show | null {
|
||||
const show = database
|
||||
.prepare(
|
||||
`SELECT id, title, starts_at_utc, twitch_title, announcement_text, status, revision,
|
||||
created_at_utc, updated_at_utc FROM shows WHERE id = ?`
|
||||
created_at_utc, updated_at_utc, completed_at_utc FROM shows WHERE id = ?`
|
||||
)
|
||||
.get(id) as ShowRow | undefined;
|
||||
if (!show) return null;
|
||||
const sectionRows = database
|
||||
.prepare(
|
||||
`SELECT s.id, s.name, s.category_id, c.name AS category, c.color AS category_color,
|
||||
s.category_name_snapshot, s.category_color_snapshot,
|
||||
s.position, s.explicit_duration_seconds, s.is_reserve_bank
|
||||
FROM show_sections s LEFT JOIN section_categories c ON c.id = s.category_id
|
||||
WHERE s.show_id = ? ORDER BY s.position`
|
||||
@@ -821,8 +831,14 @@ export function getShow(id: string): Show | null {
|
||||
id: row.id,
|
||||
name: row.name,
|
||||
categoryId: row.category_id,
|
||||
category: row.category,
|
||||
categoryColor: row.category_color,
|
||||
category:
|
||||
show.status === 'completed'
|
||||
? row.category_name_snapshot
|
||||
: (row.category_name_snapshot ?? row.category),
|
||||
categoryColor:
|
||||
show.status === 'completed'
|
||||
? row.category_color_snapshot
|
||||
: (row.category_color_snapshot ?? row.category_color),
|
||||
position: row.position,
|
||||
explicitDurationSeconds: row.explicit_duration_seconds,
|
||||
durationSeconds:
|
||||
@@ -842,6 +858,7 @@ export function getShow(id: string): Show | null {
|
||||
revision: show.revision,
|
||||
createdAtUtc: show.created_at_utc,
|
||||
updatedAtUtc: show.updated_at_utc,
|
||||
completedAtUtc: show.completed_at_utc,
|
||||
sections,
|
||||
reserve: reserveRow ? (usagesBySection.get(reserveRow.id) ?? []) : [],
|
||||
totalDurationSeconds: sections.reduce((total, section) => total + section.durationSeconds, 0)
|
||||
@@ -868,14 +885,9 @@ export function getPieceUsageContexts(
|
||||
const database = getDatabase();
|
||||
const history = database
|
||||
.prepare(
|
||||
`SELECT u.id AS usage_id, u.original_piece_id, sh.id AS show_id,
|
||||
sh.title AS show_title, COALESCE(sh.starts_at_utc, sh.updated_at_utc) AS used_at_utc
|
||||
FROM piece_usages u
|
||||
JOIN show_sections s ON s.id = u.show_section_id
|
||||
JOIN shows sh ON sh.id = s.show_id
|
||||
WHERE u.original_piece_id IS NOT NULL AND u.status = 'done'
|
||||
AND sh.status = 'completed' AND s.is_reserve_bank = 0
|
||||
ORDER BY used_at_utc DESC, u.id DESC`
|
||||
`SELECT usage_id, original_piece_id, show_id,
|
||||
show_title_snapshot AS show_title, used_at_utc
|
||||
FROM piece_usage_history ORDER BY used_at_utc DESC, usage_id DESC`
|
||||
)
|
||||
.all() as {
|
||||
usage_id: string;
|
||||
@@ -976,6 +988,138 @@ export function createShow(input: CreateShowInput): CreatedMutationResult {
|
||||
return { id, revision: 1, updatedAtUtc: now };
|
||||
}
|
||||
|
||||
export function duplicateCompletedShow(
|
||||
sourceShowId: string,
|
||||
commandId: string
|
||||
): CreatedMutationResult {
|
||||
if (!commandId || commandId.length > 200) throw new Error('INVALID_COMMAND');
|
||||
const database = getDatabase();
|
||||
return database.transaction(() => {
|
||||
const duplicate = database
|
||||
.prepare(
|
||||
`SELECT created_show_id FROM show_duplication_commands
|
||||
WHERE source_show_id = ? AND command_id = ?`
|
||||
)
|
||||
.get(sourceShowId, commandId) as { created_show_id: string } | undefined;
|
||||
if (duplicate) {
|
||||
const show = database
|
||||
.prepare('SELECT revision, updated_at_utc FROM shows WHERE id = ?')
|
||||
.get(duplicate.created_show_id) as { revision: number; updated_at_utc: string };
|
||||
return {
|
||||
id: duplicate.created_show_id,
|
||||
revision: show.revision,
|
||||
updatedAtUtc: show.updated_at_utc
|
||||
};
|
||||
}
|
||||
const source = database
|
||||
.prepare(
|
||||
`SELECT sh.title, sh.twitch_title, sh.announcement_text
|
||||
FROM shows sh JOIN show_finalizations f ON f.show_id = sh.id WHERE sh.id = ?`
|
||||
)
|
||||
.get(sourceShowId) as
|
||||
{ title: string; twitch_title: string; announcement_text: string } | undefined;
|
||||
if (!source) throw new Error('NOT_FOUND');
|
||||
const id = randomUUID();
|
||||
const now = new Date().toISOString();
|
||||
const title = `${source.title} (Kopie)`.slice(0, 200);
|
||||
database
|
||||
.prepare(
|
||||
`INSERT INTO shows (id, title, starts_at_utc, twitch_title, announcement_text,
|
||||
status, revision, created_at_utc, updated_at_utc)
|
||||
VALUES (?, ?, NULL, ?, ?, 'draft', 1, ?, ?)`
|
||||
)
|
||||
.run(id, title, source.twitch_title, source.announcement_text, now, now);
|
||||
|
||||
const sections = database
|
||||
.prepare(
|
||||
`SELECT id, name, category_id, category_name_snapshot, category_color_snapshot,
|
||||
position, explicit_duration_seconds, is_reserve_bank
|
||||
FROM show_sections WHERE show_id = ? ORDER BY position`
|
||||
)
|
||||
.all(sourceShowId) as {
|
||||
id: string;
|
||||
name: string;
|
||||
category_id: string | null;
|
||||
category_name_snapshot: string | null;
|
||||
category_color_snapshot: string | null;
|
||||
position: number;
|
||||
explicit_duration_seconds: number | null;
|
||||
is_reserve_bank: number;
|
||||
}[];
|
||||
const insertSection = database.prepare(
|
||||
`INSERT INTO show_sections (id, show_id, name, category_id, category_name_snapshot,
|
||||
category_color_snapshot, position, explicit_duration_seconds, is_reserve_bank)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
||||
);
|
||||
const usages = database.prepare(
|
||||
'SELECT * FROM piece_usages WHERE show_section_id = ? ORDER BY position'
|
||||
);
|
||||
const insertUsage = database.prepare(
|
||||
`INSERT INTO piece_usages (id, show_section_id, original_piece_id, position, status,
|
||||
duration_seconds, title_snapshot, output_title_snapshot, intro_snapshot, research_snapshot,
|
||||
moderation_snapshot, interaction_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', ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
||||
);
|
||||
for (const section of sections) {
|
||||
const sectionId = randomUUID();
|
||||
insertSection.run(
|
||||
sectionId,
|
||||
id,
|
||||
section.name,
|
||||
section.category_id,
|
||||
section.category_name_snapshot,
|
||||
section.category_color_snapshot,
|
||||
section.position,
|
||||
section.explicit_duration_seconds,
|
||||
section.is_reserve_bank
|
||||
);
|
||||
for (const usage of usages.all(section.id) as (UsageRow & { is_reserve: number })[]) {
|
||||
const usageId = randomUUID();
|
||||
insertUsage.run(
|
||||
usageId,
|
||||
sectionId,
|
||||
usage.original_piece_id,
|
||||
usage.position,
|
||||
usage.duration_seconds,
|
||||
usage.title_snapshot,
|
||||
usage.output_title_snapshot,
|
||||
usage.intro_snapshot,
|
||||
usage.research_snapshot,
|
||||
usage.moderation_snapshot,
|
||||
usage.interaction_snapshot,
|
||||
usage.transition_snapshot,
|
||||
usage.presentation_snapshot,
|
||||
usage.presentation_mode_snapshot,
|
||||
usage.presentation_source_label_snapshot,
|
||||
usage.presentation_source_url_snapshot,
|
||||
usage.presentation_source_timecode_snapshot,
|
||||
usage.presentation_title_meta_snapshot,
|
||||
usage.presentation_description_snapshot,
|
||||
usage.presentation_image_url_snapshot,
|
||||
usage.presentation_site_name_snapshot,
|
||||
usage.presentation_provider_snapshot,
|
||||
usage.presentation_provider_id_snapshot,
|
||||
usage.own_notes,
|
||||
usage.is_reserve
|
||||
);
|
||||
copyUsageRelations(database, usage.id, usageId);
|
||||
}
|
||||
}
|
||||
database
|
||||
.prepare(
|
||||
`INSERT INTO show_duplication_commands
|
||||
(source_show_id, command_id, created_show_id, created_at_utc) VALUES (?, ?, ?, ?)`
|
||||
)
|
||||
.run(sourceShowId, commandId, id, now);
|
||||
return { id, revision: 1, updatedAtUtc: now };
|
||||
})();
|
||||
}
|
||||
|
||||
export function saveShowMetadata(
|
||||
showId: string,
|
||||
expectedRevision: number,
|
||||
@@ -1055,7 +1199,8 @@ export function updateShowSection(
|
||||
sectionBelongsToShow(database, showId, sectionId);
|
||||
database
|
||||
.prepare(
|
||||
`UPDATE show_sections SET name = ?, category_id = ?, explicit_duration_seconds = ?
|
||||
`UPDATE show_sections SET name = ?, category_id = ?, explicit_duration_seconds = ?,
|
||||
category_name_snapshot = NULL, category_color_snapshot = NULL
|
||||
WHERE id = ?`
|
||||
)
|
||||
.run(name, input.categoryId, duration, sectionId);
|
||||
|
||||
Reference in new issue
Block a user