Skip to content

Commit

Permalink
Merge pull request #245 from tv2/SOF-2016/add-invalidity-to-invalid-p…
Browse files Browse the repository at this point in the history
…arts

SOF-2016: Add invalidity description to invalid parts
  • Loading branch information
KvelaGorrrrnio authored Jun 17, 2024
2 parents 7b59648 + f2848c6 commit d436793
Show file tree
Hide file tree
Showing 16 changed files with 105 additions and 55 deletions.
12 changes: 4 additions & 8 deletions src/tv2-common/cues/ekstern.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
import {
IBlueprintPart,
PieceLifespan,
RemoteContent,
TimelineObjectCoreExt,
WithTimeline
} from 'blueprints-integration'
import { PieceLifespan, RemoteContent, TimelineObjectCoreExt, WithTimeline } from 'blueprints-integration'
import {
CueDefinitionEkstern,
EvaluateCueResult,
literal,
Part,
PartDefinition,
ShowStyleContext,
TransitionStyle,
Expand All @@ -33,7 +28,7 @@ export function EvaluateEksternBase<
ShowStyleConfig extends TV2BlueprintConfigBase<StudioConfig>
>(
context: ShowStyleContext<ShowStyleConfig>,
part: IBlueprintPart,
part: Part,
partId: string,
parsedCue: CueDefinitionEkstern,
partDefinition: PartDefinition,
Expand All @@ -46,6 +41,7 @@ export function EvaluateEksternBase<
if (parsedCue.sourceDefinition.sourceType !== SourceType.REMOTE || sourceInfoEkstern === undefined) {
context.core.notifyUserWarning(`EKSTERN source is not valid: "${parsedCue.sourceDefinition.raw}"`)
part.invalid = true
part.invalidity = { reason: `No configuration found for the remote source '${parsedCue.sourceDefinition.raw}"'.` }
return result
}
const switcherInput = sourceInfoEkstern.port
Expand Down
20 changes: 10 additions & 10 deletions src/tv2-common/getSegment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
TimeFromINewsField
} from './inewsConversion'
import { CreatePartInvalid, ServerPartProps } from './parts'
import { Invalidity } from './types/invalidity'

export interface GetSegmentShowstyleOptions<ShowStyleConfig extends TV2ShowStyleConfig> {
CreatePartContinuity: (
Expand Down Expand Up @@ -85,14 +86,10 @@ export interface GetSegmentShowstyleOptions<ShowStyleConfig extends TV2ShowStyle
}

interface Segment<T> extends IBlueprintSegment<T> {
invalidity?: SegmentInvalidity
invalidity?: Invalidity
definesShowStyleVariant?: boolean
}

interface SegmentInvalidity {
reason: string
}

interface SegmentMetadata {
miniShelfVideoClipFile?: string
}
Expand Down Expand Up @@ -179,7 +176,13 @@ export async function getSegmentBase<ShowStyleConfig extends TV2ShowStyleConfig>
(c) => c.type === CueType.UNPAIRED_TARGET && IsTargetingFull(c.target)
) as CueDefinitionUnpairedTarget[]
if (unpairedTargets.length) {
blueprintParts.push(CreatePartInvalid(part))
blueprintParts.push(
CreatePartInvalid(part, {
reason: `The part has one or more unpaired targets: ${unpairedTargets
.map((cue) => cue.iNewsCommand)
.join(', ')}`
})
)
unpairedTargets.forEach((cue) => {
context.core.notifyUserWarning(`No graphic found after ${cue.iNewsCommand} cue`)
})
Expand Down Expand Up @@ -408,10 +411,7 @@ export async function getSegmentBase<ShowStyleConfig extends TV2ShowStyleConfig>
}
}

function getSegmentInvalidity(
segment: Segment<SegmentMetadata>,
parts: BlueprintResultPart[]
): SegmentInvalidity | undefined {
function getSegmentInvalidity(segment: Segment<SegmentMetadata>, parts: BlueprintResultPart[]): Invalidity | undefined {
const doesSegmentHaveMiniShelf: boolean = !!segment.metaData?.miniShelfVideoClipFile
const doesSegmentHaveValidParts: boolean = parts.length > 0 && parts.some((part) => part.pieces.length > 0)
if (doesSegmentHaveMiniShelf && doesSegmentHaveValidParts) {
Expand Down
1 change: 1 addition & 0 deletions src/tv2-common/parts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from './server'
export * from './invalid'
export * from './kam'
export * from './effekt'
export { Part } from '../types/part'
15 changes: 11 additions & 4 deletions src/tv2-common/parts/invalid.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
import { BlueprintResultPart, IBlueprintPart } from 'blueprints-integration'
import { BlueprintResultPart } from 'blueprints-integration'
import { PartDefinition } from 'tv2-common'
import { Invalidity } from '../types/invalidity'
import { Part } from '../types/part'

export function CreatePartInvalid(ingestPart: PartDefinition, externalIdSuffix?: string): BlueprintResultPart {
const part: IBlueprintPart = {
export function CreatePartInvalid(
ingestPart: PartDefinition,
invalidity: Invalidity,
externalIdSuffix?: string
): BlueprintResultPart {
const part: Part = {
externalId: ingestPart.externalId + (externalIdSuffix ? `_${externalIdSuffix}` : ''),
title: ingestPart.rawType || 'Unknown',
metaData: {},
invalid: true
invalid: true,
invalidity
}

return {
Expand Down
9 changes: 8 additions & 1 deletion src/tv2-common/parts/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,14 @@ export async function CreatePartServerBase<
): Promise<{ part: BlueprintResultPart; file: string; duration: number; invalid?: true }> {
if (isVideoIdMissing(partDefinition)) {
context.core.notifyUserWarning('Video ID not set!')
return { part: CreatePartInvalid(partDefinition), file: '', duration: 0, invalid: true }
return {
part: CreatePartInvalid(partDefinition, {
reason: `The part is missing a video id.`
}),
file: '',
duration: 0,
invalid: true
}
}

const file = getVideoId(partDefinition)
Expand Down
3 changes: 3 additions & 0 deletions src/tv2-common/types/invalidity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export interface Invalidity {
reason: string
}
6 changes: 6 additions & 0 deletions src/tv2-common/types/part.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { IBlueprintPart } from 'blueprints-integration'
import { Invalidity } from './invalidity'

export interface Part<T = unknown> extends IBlueprintPart<T> {
invalidity?: Invalidity
}
9 changes: 6 additions & 3 deletions src/tv2_afvd_showstyle/parts/evs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import {
HackPartMediaObjectSubscription,
IBlueprintActionManifest,
IBlueprintAdLibPiece,
IBlueprintPart,
IBlueprintPiece,
PieceLifespan,
TimelineObjectCoreExt
Expand All @@ -14,6 +13,7 @@ import {
findSourceInfo,
GetSisyfosTimelineObjForReplay,
literal,
Part,
PartDefinitionEVS,
PartTime,
PieceMetaData,
Expand All @@ -39,7 +39,7 @@ export async function CreatePartEVS(
const partTime = PartTime(context.config, partDefinition, totalWords, false)
const title = partDefinition.sourceDefinition.name

let part: IBlueprintPart = {
let part: Part = {
externalId: partDefinition.externalId,
title,
metaData: {},
Expand All @@ -55,7 +55,9 @@ export async function CreatePartEVS(

const sourceInfoReplay = findSourceInfo(context.config.sources, partDefinition.sourceDefinition)
if (sourceInfoReplay === undefined) {
return CreatePartInvalid(partDefinition)
return CreatePartInvalid(partDefinition, {
reason: `No configuration found for the replay source '${partDefinition.sourceDefinition.name}'.`
})
}
const switcherInput = sourceInfoReplay.port

Expand Down Expand Up @@ -94,6 +96,7 @@ export async function CreatePartEVS(

if (pieces.length === 0) {
part.invalid = true
part.invalidity = { reason: 'The part has no pieces.' }
}

return {
Expand Down
5 changes: 3 additions & 2 deletions src/tv2_afvd_showstyle/parts/grafik.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ import {
HackPartMediaObjectSubscription,
IBlueprintActionManifest,
IBlueprintAdLibPiece,
IBlueprintPart,
IBlueprintPiece
} from 'blueprints-integration'
import {
AddScript,
applyFullGraphicPropertiesToPart,
GraphicIsPilot,
Part,
PartDefinition,
PartTime,
ShowStyleContext
Expand All @@ -25,7 +25,7 @@ export async function CreatePartGrafik(
totalWords: number
): Promise<BlueprintResultPart> {
const partTime = PartTime(context.config, partDefinition, totalWords, false)
const part: IBlueprintPart = {
const part: Part = {
externalId: partDefinition.externalId,
title: partDefinition.type + ' - ' + partDefinition.rawType,
metaData: {}
Expand Down Expand Up @@ -61,6 +61,7 @@ export async function CreatePartGrafik(

if (pieces.length === 0) {
part.invalid = true
part.invalidity = { reason: 'The part has no pieces.' }
}

return {
Expand Down
18 changes: 10 additions & 8 deletions src/tv2_afvd_showstyle/parts/intro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import {
HackPartMediaObjectSubscription,
IBlueprintActionManifest,
IBlueprintAdLibPiece,
IBlueprintPart,
IBlueprintPiece
} from 'blueprints-integration'
import {
AddScript,
CreatePartInvalid,
CueDefinitionJingle,
GetJinglePartProperties,
Part,
PartDefinition,
PartTime,
ShowStyleContext
Expand All @@ -28,13 +28,12 @@ export async function CreatePartIntro(
const partTime = PartTime(context.config, partDefinition, totalWords, false)

const jingleCue = partDefinition.cues.find((cue) => {
const parsedCue = cue
return parsedCue.type === CueType.Jingle
return cue.type === CueType.Jingle
})

if (!jingleCue) {
context.core.notifyUserWarning(`Intro must contain a jingle`)
return CreatePartInvalid(partDefinition)
return CreatePartInvalid(partDefinition, { reason: 'Intro parts must contain a jingle.' })
}

const parsedJingle = jingleCue as CueDefinitionJingle
Expand All @@ -43,18 +42,20 @@ export async function CreatePartIntro(
jngl.BreakerName ? jngl.BreakerName.toString().toUpperCase() === parsedJingle.clip.toString().toUpperCase() : false
)
if (!jingle) {
context.core.notifyUserWarning(`Jingle ${parsedJingle.clip} is not configured`)
return CreatePartInvalid(partDefinition)
context.core.notifyUserWarning(`Jingle ${parsedJingle.clip} is not configured.`)
return CreatePartInvalid(partDefinition, {
reason: `No configuration found for the jingle '${parsedJingle.clip}'.`
})
}

const overlapFrames = jingle.EndAlpha

if (overlapFrames === undefined) {
context.core.notifyUserWarning(`Jingle ${parsedJingle.clip} does not have an out-duration set.`)
return CreatePartInvalid(partDefinition)
return CreatePartInvalid(partDefinition, { reason: `No out-duration set for the jingle '${parsedJingle.clip}'.` })
}

let part: IBlueprintPart = {
let part: Part = {
externalId: partDefinition.externalId,
title: partDefinition.type + ' - ' + partDefinition.rawType,
metaData: {}
Expand Down Expand Up @@ -89,6 +90,7 @@ export async function CreatePartIntro(

if (pieces.length === 0) {
part.invalid = true
part.invalidity = { reason: 'The part has no pieces.' }
}

return {
Expand Down
8 changes: 6 additions & 2 deletions src/tv2_afvd_showstyle/parts/kam.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
findSourceInfo,
GetSisyfosTimelineObjForCamera,
literal,
Part,
PartDefinitionKam,
PieceMetaData,
SegmentContext,
Expand All @@ -37,7 +38,7 @@ export async function CreatePartKam(
): Promise<BlueprintResultPart> {
const partKamBase = CreatePartKamBase(context, partDefinition, totalWords)

let part = partKamBase.part.part
let part: Part = partKamBase.part.part
const partTime = partKamBase.duration

const adLibPieces: IBlueprintAdLibPiece[] = []
Expand Down Expand Up @@ -80,7 +81,9 @@ export async function CreatePartKam(
const sourceInfoCam = findSourceInfo(context.config.sources, partDefinition.sourceDefinition)
if (sourceInfoCam === undefined) {
context.core.notifyUserWarning(`${partDefinition.rawType} does not exist in this studio`)
return CreatePartInvalid(partDefinition)
return CreatePartInvalid(partDefinition, {
reason: `No configuration found for the camera source '${partDefinition.rawType}'.`
})
}
const switcherInput = sourceInfoCam.port

Expand Down Expand Up @@ -136,6 +139,7 @@ export async function CreatePartKam(

if (pieces.length === 0) {
part.invalid = true
part.invalidity = { reason: 'The part has no pieces.' }
}

return {
Expand Down
34 changes: 25 additions & 9 deletions src/tv2_afvd_showstyle/parts/live.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@ import {
HackPartMediaObjectSubscription,
IBlueprintActionManifest,
IBlueprintAdLibPiece,
IBlueprintPart,
IBlueprintPiece
} from 'blueprints-integration'
import { AddScript, CueDefinitionEkstern, PartDefinition, PartTime, SegmentContext } from 'tv2-common'
import { AddScript, CueDefinition, Part, PartDefinition, PartTime, SegmentContext } from 'tv2-common'
import { CueType } from 'tv2-constants'
import { GalleryBlueprintConfig } from '../../tv2_afvd_showstyle/helpers/config'
import { EvaluateCues } from '../helpers/pieces/evaluateCues'
Expand All @@ -19,7 +18,7 @@ export async function CreatePartLive(
totalWords: number
): Promise<BlueprintResultPart> {
const partTime = PartTime(context.config, partDefinition, totalWords, false)
let part: IBlueprintPart = {
let part: Part = {
externalId: partDefinition.externalId,
title: partDefinition.title || 'Ekstern',
metaData: {},
Expand Down Expand Up @@ -48,12 +47,8 @@ export async function CreatePartLive(

part.hackListenToMediaObjectUpdates = mediaSubscriptions

const liveCue = partDefinition.cues.find((c) => c.type === CueType.Ekstern) as CueDefinitionEkstern
const livePiece = pieces.find((p) => p.sourceLayerId === SourceLayer.PgmLive)

if (pieces.length === 0 || !liveCue || !livePiece) {
part.invalid = true
}
part.invalidity = getInvalidityReasonForLivePart(partDefinition, pieces)
part.invalid = part.invalidity !== undefined

return {
part,
Expand All @@ -62,3 +57,24 @@ export async function CreatePartLive(
actions
}
}

function getInvalidityReasonForLivePart(
partDefinition: PartDefinition,
pieces: IBlueprintPiece[]
): Part['invalidity'] | undefined {
if (pieces.length === 0) {
return { reason: 'The part has no pieces.' }
}

const liveCue: CueDefinition | undefined = partDefinition.cues.find((c) => c.type === CueType.Ekstern)
if (!liveCue) {
return { reason: 'The part has no cues with a remote source.' }
}

const livePiece = pieces.find((p) => p.sourceLayerId === SourceLayer.PgmLive)
if (!livePiece) {
return { reason: 'The part has no pieces with a remote source.' }
}

return undefined
}
Loading

0 comments on commit d436793

Please sign in to comment.