From 2b931c27444e1615dab0b448eb8da7adc9d4a3fd Mon Sep 17 00:00:00 2001 From: ianshade Date: Fri, 28 Jul 2023 14:07:36 +0200 Subject: [PATCH 01/14] refactor: SOF-1500 remove rundown splitting --- src/classes/RundownWatcher.ts | 177 ++--- src/helpers/AssignRanksToSegments.ts | 86 +-- src/helpers/DiffPlaylist.ts | 207 +++--- src/helpers/GenerateCoreCalls.ts | 79 +-- src/helpers/ResolveRundownIntoPlaylist.ts | 121 +--- .../__tests__/AssignRanksToSegments.spec.ts | 19 +- src/helpers/__tests__/DiffPlaylist.spec.ts | 655 ++++-------------- .../__tests__/GenerateCoreCalls.spec.ts | 14 +- .../ResolveRundownIntoPlaylist.spec.ts | 392 ++--------- 9 files changed, 409 insertions(+), 1341 deletions(-) diff --git a/src/classes/RundownWatcher.ts b/src/classes/RundownWatcher.ts index d509268..4593d10 100755 --- a/src/classes/RundownWatcher.ts +++ b/src/classes/RundownWatcher.ts @@ -9,7 +9,7 @@ import { StatusCode } from '@sofie-automation/shared-lib/dist/lib/status' import { CoreHandler } from '../coreHandler' import { SegmentRankings, SegmentRankingsInner } from './ParsedINewsToSegments' import { IngestPlaylist, IngestRundown, IngestSegment } from '@sofie-automation/blueprints-integration' -import { ResolvedPlaylist, ResolveRundownIntoPlaylist } from '../helpers/ResolveRundownIntoPlaylist' +import { ResolvedPlaylistRundown, ResolveRundownIntoPlaylist } from '../helpers/ResolveRundownIntoPlaylist' import { DiffPlaylist } from '../helpers/DiffPlaylist' import { PlaylistId, RundownId, SegmentId } from '../helpers/id' import { Mutex } from 'async-mutex' @@ -122,10 +122,9 @@ export type ReducedRundown = Pick export type UnrankedSegment = Omit -export type PlaylistMap = Map export type RundownMap = Map -export type PlaylistCache = Map +export type PlaylistCache = Map export type RundownCache = Map export type SegmentCache = Map @@ -176,15 +175,15 @@ export class RundownWatcher extends EventEmitter { private lastForcedRankRecalculation: Map = new Map() private cachedINewsData: Map = new Map() - private cachedPlaylistAssignments: Map = new Map() - private cachedAssignedRundowns: Map> = new Map() + private cachedPlaylistAssignments: Map = new Map() + private cachedAssignedRundowns: Map = new Map() private skipCacheForRundown: Set = new Set() public playlists: PlaylistCache = new Map() public rundowns: RundownCache = new Map() public segments: SegmentCache = new Map() - private processingRundown: Mutex = new Mutex() + private processingRundownMutex: Mutex = new Mutex() /** * A Rundown watcher which will poll iNews FTP server for changes and emit events @@ -277,7 +276,7 @@ export class RundownWatcher extends EventEmitter { } public async ResyncRundown(rundownExternalId: string) { - const release = await this.processingRundown.acquire() + const release = await this.processingRundownMutex.acquire() const playlistExternalId = rundownExternalId.replace(/_\d+$/, '') const playlist = this.playlists.get(playlistExternalId) const rundown = this.rundowns.get(rundownExternalId) @@ -294,25 +293,11 @@ export class RundownWatcher extends EventEmitter { this.cachedINewsData.delete(segmentId) } this.rundowns.delete(rundownExternalId) - this.playlists.set( - playlistExternalId, - playlist.filter((r) => r !== rundownExternalId) - ) + this.playlists.delete(playlistExternalId) + + this.cachedPlaylistAssignments.delete(playlistExternalId) + this.cachedAssignedRundowns.delete(playlistExternalId) - const cachedPlaylist = this.cachedPlaylistAssignments.get(playlistExternalId) - if (cachedPlaylist) { - this.cachedPlaylistAssignments.set( - playlistExternalId, - cachedPlaylist.filter((p) => p.rundownId !== rundownExternalId) - ) - } - const cachedAssignedRundown = this.cachedAssignedRundowns.get(playlistExternalId) - if (cachedAssignedRundown) { - this.cachedAssignedRundowns.set( - playlistExternalId, - cachedAssignedRundown.filter((p) => p.externalId !== rundownExternalId) - ) - } this.lastForcedRankRecalculation.delete(rundownExternalId) this.skipCacheForRundown.add(rundownExternalId) release() @@ -327,7 +312,7 @@ export class RundownWatcher extends EventEmitter { async checkINewsRundownById(rundownId: string): Promise { const rundown = await this.rundownManager.downloadRundown(rundownId) if (rundown.gatewayVersion === this.gatewayVersion) { - const release = await this.processingRundown.acquire() + const release = await this.processingRundownMutex.acquire() try { await this.processUpdatedRundown(rundown.externalId, rundown) } catch (e) { @@ -349,13 +334,6 @@ export class RundownWatcher extends EventEmitter { const cachedPlaylist = this.playlists.get(playlistId) if (cachedPlaylist) { - const cachedRundowns: Array<{ externalId: RundownId; segmentIds: SegmentId[] }> = [] - for (const rundownId of cachedPlaylist) { - let cachedRundown = this.rundowns.get(rundownId) - if (!cachedRundown) continue - cachedRundowns.push({ externalId: rundownId, segmentIds: cachedRundown }) - } - // Fetch any segments that may have changed for (const segment of playlist.segments) { const cachedSegment = this.segments.get(segment.externalId) @@ -397,127 +375,98 @@ export class RundownWatcher extends EventEmitter { } }) - const { resolvedPlaylist: playlistAssignments, untimedSegments } = ResolveRundownIntoPlaylist( - playlistId, - segmentsToResolve - ) - if (!playlistAssignments.length) { - playlistAssignments.push({ - rundownId: `${playlistId}_1`, - segments: [], - }) - } + const { resolvedRundown, untimedSegments } = ResolveRundownIntoPlaylist(playlistId, segmentsToResolve) // Fetch ingestDataCache for segments that have been modified - const ingestDataPromises: Array>> = [] - - for (const rundown of playlistAssignments) { - if (this.skipCacheForRundown.has(rundown.rundownId)) { - this.skipCacheForRundown.delete(rundown.rundownId) - continue - } + let ingestDataPromise: Promise> | undefined = undefined + if (this.skipCacheForRundown.has(resolvedRundown.rundownId)) { + this.skipCacheForRundown.delete(resolvedRundown.rundownId) + } else { const segmentsToFetch: SegmentId[] = [] - for (const segmentId of rundown.segments) { + for (const segmentId of resolvedRundown.segments) { if (uncachedINewsData.has(segmentId)) { segmentsToFetch.push(segmentId) } } - ingestDataPromises.push(this.coreHandler.GetSegmentsCacheById(rundown.rundownId, segmentsToFetch)) + ingestDataPromise = this.coreHandler.GetSegmentsCacheById(resolvedRundown.rundownId, segmentsToFetch) } - const ingestCacheList = await Promise.all(ingestDataPromises) - - const ingestCacheData: Map = new Map() - - for (let cache of ingestCacheList) { - for (let [segmentId, data] of cache) { - ingestCacheData.set(segmentId, data) - } - } + const ingestCacheData: Map = (await ingestDataPromise) ?? new Map() const assignedRundowns: INewsRundown[] = [] - for (const playlistRundown of playlistAssignments) { - const rundownSegments: RundownSegment[] = [] - - for (const segmentId of playlistRundown.segments) { - const iNewsData = this.cachedINewsData.get(segmentId) + const rundownSegments: RundownSegment[] = [] - if (!iNewsData) { - this.logger.error( - `Failed to assign segment ${segmentId} to rundown ${playlistRundown.rundownId}. Could not find cached iNews data` - ) - continue - } + for (const segmentId of resolvedRundown.segments) { + const iNewsData = this.cachedINewsData.get(segmentId) - const rundownSegment = new RundownSegment( - playlistRundown.rundownId, - iNewsData.iNewsStory, - iNewsData.modified, - iNewsData.locator, - segmentId, - 0, - iNewsData?.name, - untimedSegments.has(segmentId) + if (!iNewsData) { + this.logger.error( + `Failed to assign segment ${segmentId} to rundown ${resolvedRundown.rundownId}. Could not find cached iNews data` ) - rundownSegments.push(rundownSegment) + continue } - const iNewsRundown: INewsRundown = new INewsRundown( - playlistRundown.rundownId, - playlistRundown.rundownId, - this.gatewayVersion, - rundownSegments, - playlistRundown.payload + const rundownSegment = new RundownSegment( + resolvedRundown.rundownId, + iNewsData.iNewsStory, + iNewsData.modified, + iNewsData.locator, + segmentId, + 0, + iNewsData?.name, + untimedSegments.has(segmentId) ) - - assignedRundowns.push(iNewsRundown) + rundownSegments.push(rundownSegment) } - const { changes, segmentChanges } = DiffPlaylist( - assignedRundowns, - this.cachedAssignedRundowns.get(playlistId) ?? [] + const iNewsRundown: INewsRundown = new INewsRundown( + resolvedRundown.rundownId, + resolvedRundown.rundownId, + this.gatewayVersion, + rundownSegments, + resolvedRundown.payload + ) + + assignedRundowns.push(iNewsRundown) + + const { playlistChanges: changes, segmentChanges } = DiffPlaylist( + iNewsRundown, + this.cachedAssignedRundowns.get(playlistId) ) - this.cachedPlaylistAssignments.set(playlistId, playlistAssignments) - this.cachedAssignedRundowns.set(playlistId, assignedRundowns) + this.cachedPlaylistAssignments.set(playlistId, resolvedRundown) + this.cachedAssignedRundowns.set(playlistId, iNewsRundown) - let segmentRanks = AssignRanksToSegments( - playlistAssignments, + let { assignedRanks, recalculatedAsIntegers } = AssignRanksToSegments( + resolvedRundown, changes, segmentChanges, this.previousRanks, this.lastForcedRankRecalculation ) - const assignedRanks: Map = new Map() - for (const rundown of segmentRanks) { - if (rundown.recalculatedAsIntegers) { - this.lastForcedRankRecalculation.set(rundown.rundownId, Date.now()) - } + if (recalculatedAsIntegers) { + this.lastForcedRankRecalculation.set(resolvedRundown.rundownId, Date.now()) + } - for (const [segmentId, rank] of rundown.assignedRanks) { - assignedRanks.set(segmentId, rank) - } - this.updatePreviousRanks(rundown.rundownId, rundown.assignedRanks) + for (const [segmentId, rank] of assignedRanks) { + assignedRanks.set(segmentId, rank) } + this.updatePreviousRanks(resolvedRundown.rundownId, assignedRanks) for (const segment of playlist.segments) { this.segments.set(segment.externalId, segment) } - for (const rundown of playlistAssignments) { - this.rundowns.set(rundown.rundownId, rundown.segments) - } - this.playlists.set( - playlistId, - playlistAssignments.map((r) => r.rundownId) - ) + this.rundowns.set(resolvedRundown.rundownId, resolvedRundown.segments) + + this.playlists.set(playlistId, resolvedRundown.rundownId) const coreCalls = GenerateCoreCalls( playlistId, changes, - playlistAssignments, + resolvedRundown, assignedRanks, this.cachedINewsData, ingestCacheData, diff --git a/src/helpers/AssignRanksToSegments.ts b/src/helpers/AssignRanksToSegments.ts index 060cfe5..90fbef9 100644 --- a/src/helpers/AssignRanksToSegments.ts +++ b/src/helpers/AssignRanksToSegments.ts @@ -1,79 +1,59 @@ import { literal } from '../helpers' import { ParsedINewsIntoSegments, SegmentRankings, SegmentRankingsInner } from '../classes/ParsedINewsToSegments' import { logger } from '../logger' -import { PlaylistChange, PlaylistChangeSegmentMoved, PlaylistChangeType, SegmentChangesMap } from './DiffPlaylist' +import { PlaylistChange, PlaylistChangeSegmentMoved, PlaylistChangeType, SegmentChanges } from './DiffPlaylist' import { RundownId, SegmentId } from './id' -import { ResolvedPlaylist, ResolvedPlaylistRundown } from './ResolveRundownIntoPlaylist' +import { ResolvedPlaylistRundown } from './ResolveRundownIntoPlaylist' const RECALCULATE_RANKS_CHANGE_THRESHOLD = 50 const MAX_TIME_BEFORE_RECALCULATE_RANKS = 60 * 60 * 1000 // One hour const MINIMUM_ALLOWED_RANK = Math.pow(1 / 2, 30) export function AssignRanksToSegments( - playlistAssignments: ResolvedPlaylist, + rundown: ResolvedPlaylistRundown, changes: PlaylistChange[], - segmentChanges: SegmentChangesMap, + segmentChanges: SegmentChanges, previousRanks: SegmentRankings, lastRankRecalculation: Map -): Array<{ rundownId: RundownId; assignedRanks: Map; recalculatedAsIntegers: boolean }> { - const ranks: Array<{ - rundownId: RundownId - assignedRanks: Map - recalculatedAsIntegers: boolean - }> = [] - - for (let rundown of playlistAssignments) { - let assignedRanks: Map = new Map() - let recalculated: boolean = false - - const changesToSegments = segmentChanges.get(rundown.rundownId) - - if (!changesToSegments) { - // Shouldn't be possible. - logger.error(`Could not find segment changes for rundown ${rundown.rundownId}.`) - continue - } +): { assignedRanks: Map; recalculatedAsIntegers: boolean } { + let assignedRanks: Map = new Map() - if (changesToSegments.movedSegments.length) { - logger.debug(`Moved Segments: ${Array.from(changesToSegments.movedSegments.values()).join(',')}`) - } - - logger.debug(`Getting ranks for ${rundown.rundownId}`) - let { segmentRanks, recalculatedAsIntegers } = ParsedINewsIntoSegments.GetRanks( - rundown.rundownId, - rundown.segments, - previousRanks, - changesToSegments, - logger - ) + if (segmentChanges.movedSegments.length) { + logger.debug(`Moved Segments: ${Array.from(segmentChanges.movedSegments.values()).join(',')}`) + } - // Check if we should recalculate ranks to integer values from scratch. - if (!recalculatedAsIntegers && shouldRecalculateRanks(changes, lastRankRecalculation, rundown, segmentRanks)) { - logger.debug(`Recalculating ranks as integers for ${rundown.rundownId}`) - segmentRanks = ParsedINewsIntoSegments.RecalculateRanksAsIntegerValues(rundown.segments).segmentRanks + logger.debug(`Getting ranks for ${rundown.rundownId}`) + let { segmentRanks, recalculatedAsIntegers } = ParsedINewsIntoSegments.GetRanks( + rundown.rundownId, + rundown.segments, + previousRanks, + segmentChanges, + logger + ) - const rundownPreviousRanks = previousRanks.get(rundown.rundownId) + // Check if we should recalculate ranks to integer values from scratch. + if (!recalculatedAsIntegers && shouldRecalculateRanks(changes, lastRankRecalculation, rundown, segmentRanks)) { + logger.debug(`Recalculating ranks as integers for ${rundown.rundownId}`) + segmentRanks = ParsedINewsIntoSegments.RecalculateRanksAsIntegerValues(rundown.segments).segmentRanks - if (rundownPreviousRanks) { - generateMoveChanges(segmentRanks, rundownPreviousRanks, changes, rundown) - } + const rundownPreviousRanks = previousRanks.get(rundown.rundownId) - recalculated = true + if (rundownPreviousRanks) { + generateMoveChanges(segmentRanks, rundownPreviousRanks, changes, rundown) } - // Store ranks - for (let [segmentId, rank] of segmentRanks) { - assignedRanks.set(segmentId, rank) - } + recalculatedAsIntegers = true + } - ranks.push({ - rundownId: rundown.rundownId, - assignedRanks, - recalculatedAsIntegers: recalculated, - }) + // Store ranks + for (let [segmentId, rank] of segmentRanks) { + assignedRanks.set(segmentId, rank) } - return ranks + return { + assignedRanks, + recalculatedAsIntegers, + } } function shouldRecalculateRanks( diff --git a/src/helpers/DiffPlaylist.ts b/src/helpers/DiffPlaylist.ts index 71b61a9..9b86259 100644 --- a/src/helpers/DiffPlaylist.ts +++ b/src/helpers/DiffPlaylist.ts @@ -2,7 +2,7 @@ import { logger } from '../logger' import { INewsRundown } from '../classes/datastructures/Rundown' import { literal } from '../helpers' import { GetMovedSegments } from './GetMovedSegments' -import { RundownId, SegmentId } from './id' +import { SegmentId } from './id' export enum PlaylistChangeType { PlaylistChangeSegmentDeleted = 'segment_deleted', @@ -43,11 +43,6 @@ export interface PlaylistChangeSegmentMoved extends PlaylistChangeBase { segmentExternalId: string } -export interface PlaylistChangeRundownDeleted extends PlaylistChangeBase { - type: PlaylistChangeType.PlaylistChangeRundownDeleted - rundownExternalId: string -} - export interface PlaylistChangeRundownCreated extends PlaylistChangeBase { type: PlaylistChangeType.PlaylistChangeRundownCreated rundownExternalId: string @@ -69,7 +64,6 @@ export type PlaylistChange = | PlaylistChangeSegmentChanged | PlaylistChangeSegmentMoved | PlaylistChangeRundownCreated - | PlaylistChangeRundownDeleted | PlaylistChangeRundownMetaDataUpdated | PlaylistChangeRundownUpdated @@ -82,153 +76,114 @@ export type SegmentChanges = { changedSegments: SegmentId[] } -export type SegmentChangesMap = Map - export function DiffPlaylist( - playlist: Array, - previous: Array + rundown: INewsRundown, + previous: INewsRundown | undefined ): { - changes: PlaylistChange[] - segmentChanges: SegmentChangesMap + playlistChanges: PlaylistChange[] + segmentChanges: SegmentChanges } { - let changes: PlaylistChange[] = [] - let segmentChanges: SegmentChangesMap = new Map() - let updatedRundownMetaData: Set = new Set() - - for (let rundown of previous) { - let newRundown = playlist.find((p) => p.externalId === rundown.externalId) - if (!newRundown) { - logger.debug(`Diff: Rundown ${rundown.externalId} deleted`) - changes.push( - literal({ - type: PlaylistChangeType.PlaylistChangeRundownDeleted, - rundownExternalId: rundown.externalId, - }) - ) - segmentChanges.set(rundown.externalId, { - movedSegments: [], - notMovedSegments: [], - insertedSegments: [], - deletedSegments: rundown.segments.map((s) => s.externalId), - changedSegments: [], - }) - continue - } - } + const changes: PlaylistChange[] = [] - for (let rundown of playlist) { - const prevRundown = previous.find((p) => p.externalId === rundown.externalId) - if (!prevRundown) { - logger.debug(`Diff: Rundown ${rundown.externalId} created`) - changes.push( - literal({ - type: PlaylistChangeType.PlaylistChangeRundownCreated, - rundownExternalId: rundown.externalId, - }) - ) - segmentChanges.set(rundown.externalId, { + if (!previous) { + logger.debug(`Diff: Rundown ${rundown.externalId} created`) + changes.push( + literal({ + type: PlaylistChangeType.PlaylistChangeRundownCreated, + rundownExternalId: rundown.externalId, + }) + ) + return { + playlistChanges: changes, + segmentChanges: { movedSegments: [], notMovedSegments: [], insertedSegments: rundown.segments.map((s) => s.externalId), deletedSegments: [], changedSegments: [], - }) - continue + }, } + } - if (prevRundown.payload?.showstyleVariant !== rundown.payload?.showstyleVariant) { - changes.push( - literal({ - type: PlaylistChangeType.PlaylistChangeRundownUpdated, - rundownExternalId: rundown.externalId, - }) - ) - updatedRundownMetaData.add(rundown.externalId) - } + let { movedSegments, notMovedSegments, insertedSegments, deletedSegments } = GetMovedSegments( + previous.segments.map((s) => s.externalId), + rundown.segments.map((s) => s.externalId) + ) + let changedSegments: SegmentId[] = [] + + movedSegments.forEach((s) => { + logger.debug(`Diff: Segment ${s} moved`) + changes.push( + literal({ + type: PlaylistChangeType.PlaylistChangeSegmentMoved, + rundownExternalId: rundown.externalId, + segmentExternalId: s, + }) + ) + }) - let { movedSegments, notMovedSegments, insertedSegments, deletedSegments } = GetMovedSegments( - prevRundown.segments.map((s) => s.externalId), - rundown.segments.map((s) => s.externalId) + insertedSegments.forEach((s) => { + logger.debug(`Diff: Segment ${s} inserted`) + changes.push( + literal({ + type: PlaylistChangeType.PlaylistChangeSegmentCreated, + rundownExternalId: rundown.externalId, + segmentExternalId: s, + }) ) - let changedSegments: SegmentId[] = [] + }) - movedSegments.forEach((s) => { - logger.debug(`Diff: Segment ${s} moved`) - changes.push( - literal({ - type: PlaylistChangeType.PlaylistChangeSegmentMoved, - rundownExternalId: rundown.externalId, - segmentExternalId: s, - }) - ) - updatedRundownMetaData.add(rundown.externalId) - }) + deletedSegments.forEach((s) => { + logger.debug(`Diff: Segment ${s} deleted`) + changes.push( + literal({ + type: PlaylistChangeType.PlaylistChangeSegmentDeleted, + rundownExternalId: rundown.externalId, + segmentExternalId: s, + }) + ) + }) - insertedSegments.forEach((s) => { - logger.debug(`Diff: Segment ${s} inserted`) - changes.push( - literal({ - type: PlaylistChangeType.PlaylistChangeSegmentCreated, - rundownExternalId: rundown.externalId, - segmentExternalId: s, - }) - ) - updatedRundownMetaData.add(rundown.externalId) - }) + // Find segments that have changed, rather than staying put / just being moved + const segmentsToCheckForChanges = [...movedSegments, ...notMovedSegments] + for (const segmentId of segmentsToCheckForChanges) { + const current = rundown.segments.find((s) => s.externalId === segmentId) + const prev = previous.segments.find((s) => s.externalId === segmentId) - deletedSegments.forEach((s) => { - logger.debug(`Diff: Segment ${s} deleted`) + if (!current || !prev) continue + + if (JSON.stringify(prev.serialize()) !== JSON.stringify(current.serialize())) { + if (!changedSegments.includes(segmentId)) { + changedSegments.push(segmentId) + } + logger.debug(`Diff: Segment ${segmentId} changed`) changes.push( - literal({ - type: PlaylistChangeType.PlaylistChangeSegmentDeleted, + literal({ + type: PlaylistChangeType.PlaylistChangeSegmentChanged, rundownExternalId: rundown.externalId, - segmentExternalId: s, + segmentExternalId: segmentId, }) ) - updatedRundownMetaData.add(rundown.externalId) - }) - - // Find segments that have changed, rather than staying put / just being moved - const segmentsToCheckForChanges = [...movedSegments, ...notMovedSegments] - for (const segmentId of segmentsToCheckForChanges) { - const current = rundown.segments.find((s) => s.externalId === segmentId) - const prev = prevRundown.segments.find((s) => s.externalId === segmentId) - - if (!current || !prev) continue - - if (JSON.stringify(prev.serialize()) !== JSON.stringify(current.serialize())) { - if (!changedSegments.includes(segmentId)) { - changedSegments.push(segmentId) - } - logger.debug(`Diff: Segment ${segmentId} changed`) - changes.push( - literal({ - type: PlaylistChangeType.PlaylistChangeSegmentChanged, - rundownExternalId: rundown.externalId, - segmentExternalId: segmentId, - }) - ) - updatedRundownMetaData.add(rundown.externalId) - } } - - segmentChanges.set(rundown.externalId, { - movedSegments, - notMovedSegments, - insertedSegments, - deletedSegments, - changedSegments, - }) } - for (const rundownId of updatedRundownMetaData) { + if (changes.length) { changes.push( literal({ type: PlaylistChangeType.PlaylistChangeRundownMetaDataUpdated, - rundownExternalId: rundownId, + rundownExternalId: rundown.externalId, }) ) } - return { changes, segmentChanges } + return { + playlistChanges: changes, + segmentChanges: { + movedSegments, + notMovedSegments, + insertedSegments, + deletedSegments, + changedSegments, + }, + } } diff --git a/src/helpers/GenerateCoreCalls.ts b/src/helpers/GenerateCoreCalls.ts index cae1409..315b857 100644 --- a/src/helpers/GenerateCoreCalls.ts +++ b/src/helpers/GenerateCoreCalls.ts @@ -12,7 +12,7 @@ import { PlaylistChangeType, } from './DiffPlaylist' import { PlaylistId, RundownId, SegmentId } from './id' -import { ResolvedPlaylist, ResolvedPlaylistRundown } from './ResolveRundownIntoPlaylist' +import { ResolvedPlaylistRundown } from './ResolveRundownIntoPlaylist' import { RundownSegment } from '../classes/datastructures/Segment' export enum CoreCallType { @@ -85,7 +85,7 @@ export type CoreCall = export function GenerateCoreCalls( playlistId: PlaylistId, changes: PlaylistChange[], - playlistAssignments: ResolvedPlaylist, + resolvedRundown: ResolvedPlaylistRundown, assignedRanks: Map, iNewsDataCache: Map, // TODO: This should probably just be a map of the previous known ranks @@ -94,12 +94,11 @@ export function GenerateCoreCalls( ): CoreCall[] { const coreCalls: CoreCall[] = [] - coreCalls.push(...createDeletedRundownCoreCalls(changes)) coreCalls.push(...createSegmentDeletedCoreCalls(changes)) coreCalls.push( ...createRundownCreateCoreCalls( changes, - playlistAssignments, + resolvedRundown, playlistId, iNewsDataCache, assignedRanks, @@ -109,7 +108,7 @@ export function GenerateCoreCalls( coreCalls.push( ...createMetaDataUpdateCoreCalls( changes, - playlistAssignments, + resolvedRundown, playlistId, iNewsDataCache, assignedRanks, @@ -126,7 +125,7 @@ export function GenerateCoreCalls( coreCalls.push( ...createRundownUpdatedCoreCalls( changes, - playlistAssignments, + resolvedRundown, playlistId, iNewsDataCache, assignedRanks, @@ -180,18 +179,6 @@ function createSegmentMovedCoreCalls( return coreCalls } -function createDeletedRundownCoreCalls(changes: PlaylistChange[]): CoreCallRundownDelete[] { - return changes - .filter((playlistChange) => playlistChange.type === PlaylistChangeType.PlaylistChangeRundownDeleted) - .map((playlistChange) => { - logger.debug(`Adding core call: Rundown delete (${playlistChange.rundownExternalId})`) - return literal({ - type: CoreCallType.dataRundownDelete, - rundownExternalId: playlistChange.rundownExternalId, - }) - }) -} - function createSegmentDeletedCoreCalls(changes: PlaylistChange[]): CoreCallSegmentDelete[] { return changes .filter((playlistChange) => playlistChange.type === PlaylistChangeType.PlaylistChangeSegmentDeleted) @@ -208,7 +195,7 @@ function createSegmentDeletedCoreCalls(changes: PlaylistChange[]): CoreCallSegme function createRundownCreateCoreCalls( changes: PlaylistChange[], - playlistAssignments: Array, + resolvedRundown: ResolvedPlaylistRundown, playlistId: string, iNewsDataCache: Map, assignedRanks: Map, @@ -218,22 +205,12 @@ function createRundownCreateCoreCalls( .filter((playlistChange) => playlistChange.type === PlaylistChangeType.PlaylistChangeRundownCreated) .map((playlistChange) => { logger.debug(`Creating rundown: ${playlistChange.rundownExternalId}`) - const assignedRundown = playlistAssignments.find( - (playlistAssignment) => playlistAssignment.rundownId === playlistChange.rundownExternalId - ) - - if (!assignedRundown) { - logger.error( - `Tried to create rundown ${playlistChange.rundownExternalId} but could not find the segments associated with this rundown.` - ) - return undefined - } const rundown = playlistRundownToIngestRundown( playlistId, - assignedRundown.rundownId, - assignedRundown.segments, - assignedRundown.payload, + resolvedRundown.rundownId, + resolvedRundown.segments, + resolvedRundown.payload, iNewsDataCache, assignedRanks, untimedSegments @@ -251,7 +228,7 @@ function createRundownCreateCoreCalls( function createMetaDataUpdateCoreCalls( changes: PlaylistChange[], - playlistAssignments: Array, + resolvedRundown: ResolvedPlaylistRundown, playlistId: string, iNewsDataCache: Map, assignedRanks: Map, @@ -259,21 +236,12 @@ function createMetaDataUpdateCoreCalls( ): CoreCallRundownMetaDataUpdate[] { return changes .filter((playlistChange) => playlistChange.type === PlaylistChangeType.PlaylistChangeRundownMetaDataUpdated) - .map((playlistChange) => { - const assignedRundown = playlistAssignments.find((r) => r.rundownId === playlistChange.rundownExternalId) - - if (!assignedRundown) { - logger.error( - `Tried to create rundown ${playlistChange.rundownExternalId} but could not find the segments associated with this rundown.` - ) - return undefined - } - + .map(() => { const rundown = playlistRundownToIngestRundown( playlistId, - assignedRundown.rundownId, - assignedRundown.segments, - assignedRundown.payload, + resolvedRundown.rundownId, + resolvedRundown.segments, + resolvedRundown.payload, iNewsDataCache, assignedRanks, untimedSegments @@ -379,7 +347,7 @@ function createSegmentCreatedCoreCalls( function createRundownUpdatedCoreCalls( changes: PlaylistChange[], - playlistAssignments: Array, + resolvedRundown: ResolvedPlaylistRundown, playlistId: string, iNewsDataCache: Map, assignedRanks: Map, @@ -387,21 +355,12 @@ function createRundownUpdatedCoreCalls( ): CoreCallRundownUpdate[] { return changes .filter((playlistChange) => playlistChange.type === PlaylistChangeType.PlaylistChangeRundownUpdated) - .map((playlistChange) => { - const assignedRundown = playlistAssignments.find((r) => r.rundownId === playlistChange.rundownExternalId) - - if (!assignedRundown) { - logger.error( - `Tried to create rundown ${playlistChange.rundownExternalId} but could not find the segments associated with this rundown.` - ) - return undefined - } - + .map(() => { const rundown = playlistRundownToIngestRundown( playlistId, - assignedRundown.rundownId, - assignedRundown.segments, - assignedRundown.payload, + resolvedRundown.rundownId, + resolvedRundown.segments, + resolvedRundown.payload, iNewsDataCache, assignedRanks, untimedSegments diff --git a/src/helpers/ResolveRundownIntoPlaylist.ts b/src/helpers/ResolveRundownIntoPlaylist.ts index 7886fc6..e831dd7 100644 --- a/src/helpers/ResolveRundownIntoPlaylist.ts +++ b/src/helpers/ResolveRundownIntoPlaylist.ts @@ -1,4 +1,3 @@ -import { UnparsedCue } from 'inews' import { UnrankedSegment } from '../classes/RundownWatcher' import { SegmentId } from './id' @@ -13,12 +12,11 @@ export type ResolvedPlaylistRundown = { export function ResolveRundownIntoPlaylist( playlistExternalId: string, segments: Array -): { resolvedPlaylist: ResolvedPlaylist; untimedSegments: Set } { - const resolvedPlaylist: ResolvedPlaylist = [] +): { resolvedRundown: ResolvedPlaylistRundown; untimedSegments: Set } { const untimedSegments: Set = new Set() let rundownIndex = 0 - let currentRundown: ResolvedPlaylistRundown = { + const resolvedRundown: ResolvedPlaylistRundown = { rundownId: `${playlistExternalId}_${rundownIndex + 1}`, // 1-index for users segments: [], payload: { @@ -26,42 +24,11 @@ export function ResolveRundownIntoPlaylist( }, } - const splitRundown = () => { - // Note: Disabling rundowns temporarily for v42.0. - return - const isAllSegmentsForCurrentRundownEmpty = currentRundown.segments - .map((segmentExternalId) => segments.find((segment) => segment.externalId === segmentExternalId)) - .filter(isSegment) - .filter((segment) => !isSegmentFloated(segment)) - .every(isSegmentEmpty) - - if (currentRundown.segments.length === 0 || isAllSegmentsForCurrentRundownEmpty) return - - resolvedPlaylist.push(currentRundown) - rundownIndex++ - currentRundown = { - rundownId: `${playlistExternalId}_${rundownIndex + 1}`, - segments: [], - payload: { - rank: rundownIndex, - }, - } - } - let continuityStoryFound = false let klarOnAirStoryFound = false for (const segment of segments) { - if (shouldLookForShowstyleVariant(segment, currentRundown)) { - const showstyleVariants = getOrderedShowstyleVariants(segment) - if (showstyleVariants.length > 0) { - splitRundown() - const showstyleVariant = showstyleVariants[0] - setShowstyleVariant(currentRundown, showstyleVariant) - } - } - - currentRundown.segments.push(segment.externalId) + resolvedRundown.segments.push(segment.externalId) const isFloated = segment.iNewsStory.meta.float ?? false if (!isFloated && !klarOnAirStoryFound && isKlarOnAir(segment)) { @@ -73,7 +40,7 @@ export function ResolveRundownIntoPlaylist( if (!continuityStoryFound && segment.name?.match(/^\s*continuity\s*$/i)) { continuityStoryFound = true if (segment.iNewsStory.fields.backTime?.match(/^@\d+$/)) { - currentRundown.backTime = segment.iNewsStory.fields.backTime + resolvedRundown.backTime = segment.iNewsStory.fields.backTime } } if (continuityStoryFound) { @@ -81,88 +48,10 @@ export function ResolveRundownIntoPlaylist( } } - if (currentRundown.segments.length) { - resolvedPlaylist.push(currentRundown) - } - - return { resolvedPlaylist, untimedSegments } -} - -function isSegment(segment: UnrankedSegment | undefined): segment is UnrankedSegment { - return segment !== undefined -} - -function isSegmentFloated(segment: UnrankedSegment): boolean { - return segment.iNewsStory.meta.float === 'float' -} - -function isSegmentEmpty(segment: UnrankedSegment): boolean { - const isCuesEmpty = segment.iNewsStory.cues.length === 0 - return isCuesEmpty && isSegmentBodyEmpty(segment) -} - -function isSegmentBodyEmpty(segment: UnrankedSegment): boolean { - if (segment.iNewsStory.body === undefined) { - return true - } - const lines = segment.iNewsStory.body.split('\r\n').filter((line) => !/

\s*<\/p>|\s*/i.test(line)) - return lines.length === 0 + return { resolvedRundown, untimedSegments } } function isKlarOnAir(segment: UnrankedSegment): boolean { const klarOnAirPattern = /klar[\s-]*on[\s-]*air/im return !!segment.name?.match(klarOnAirPattern) } - -function setShowstyleVariant(rundown: ResolvedPlaylistRundown, showstyleVariant: string) { - rundown.payload = { - ...(rundown.payload ?? null), - showstyleVariant, - } -} - -function shouldLookForShowstyleVariant(segment: UnrankedSegment, rundown: ResolvedPlaylistRundown): boolean { - const isFloated = segment.iNewsStory.meta.float ?? false - const hasShowstyleVariant = rundown.payload?.showstyleVariant !== undefined - return !isFloated && !hasShowstyleVariant -} - -function getOrderedShowstyleVariants(segment: UnrankedSegment): string[] { - const cueOrder = getCueOrder(segment) - const orderedShowstyleVariants: string[] = [] - cueOrder.forEach((cueIndex: number) => { - const parsedProfile = parseShowstyleVariant(segment.iNewsStory.cues[cueIndex]) - if (parsedProfile) { - orderedShowstyleVariants.push(parsedProfile) - } - }) - return orderedShowstyleVariants -} - -function parseShowstyleVariant(cue: UnparsedCue | undefined): string | null { - const numberOfCueLines = !!cue ? cue.length : -1 - - // Kommando cue (ignoring timing) - const showstyleVariantPattern = /^\s*SOFIE\s*=\s*SHOWSTYLEVARIANT/i - if (numberOfCueLines >= 2 && showstyleVariantPattern.test(cue![0])) { - return cue![1].trim() - } - return null -} - -/** - * - * @param segment The segment for which the cue order should be retrieved - * @returns A list of indicies representing the cue order. - */ -function getCueOrder(segment: UnrankedSegment): number[] { - const body = segment.iNewsStory.body ?? '' - const refPattern = /\d+)"\s*\/?>/gi - const order: number[] = [] - let match: RegExpExecArray | null - while ((match = refPattern.exec(body))) { - let id = parseInt(match.groups!.id, 10) - order.push(id) - } - return order -} diff --git a/src/helpers/__tests__/AssignRanksToSegments.spec.ts b/src/helpers/__tests__/AssignRanksToSegments.spec.ts index d1b49af..1f7570a 100644 --- a/src/helpers/__tests__/AssignRanksToSegments.spec.ts +++ b/src/helpers/__tests__/AssignRanksToSegments.spec.ts @@ -6,23 +6,20 @@ const segmentId0 = 'segment0' const segmentId1 = 'segment1' const segmentId2 = 'segment2' function makePlaylistAssignments() { - return [ - { - rundownId: rundownId, - segments: [segmentId0, segmentId1, segmentId2], - }, - ] + return { + rundownId: rundownId, + segments: [segmentId0, segmentId1, segmentId2], + } } function makeSegmentChangesMap() { - const segmentChanges = { + return { movedSegments: [], notMovedSegments: [segmentId0, segmentId1, segmentId2], insertedSegments: [], deletedSegments: [], changedSegments: [], } - return new Map([[rundownId, segmentChanges]]) } describe('Assign Ranks To Segments', () => { @@ -41,7 +38,7 @@ describe('Assign Ranks To Segments', () => { new Map([[rundownId, Date.now() - 60 * 1000]]) // 1 minute ago ) expect(playlistChanges.length).toEqual(0) - expect(newRanks[0].assignedRanks).toEqual( + expect(newRanks.assignedRanks).toEqual( new Map([ [segmentId0, 1000], [segmentId1, 1001.1], @@ -64,7 +61,7 @@ describe('Assign Ranks To Segments', () => { new Map([[rundownId, Date.now() - 60 * 1000]]) // 1 minute ago ) expect(playlistChanges.length).toEqual(2) - expect(newRanks[0].assignedRanks).toEqual( + expect(newRanks.assignedRanks).toEqual( new Map([ [segmentId0, 1000], [segmentId1, 2000], @@ -87,7 +84,7 @@ describe('Assign Ranks To Segments', () => { new Map([[rundownId, Date.now() - 61 * 60 * 1000]]) // 61 minutes ago ) expect(playlistChanges.length).toEqual(2) - expect(newRanks[0].assignedRanks).toEqual( + expect(newRanks.assignedRanks).toEqual( new Map([ [segmentId0, 1000], [segmentId1, 2000], diff --git a/src/helpers/__tests__/DiffPlaylist.spec.ts b/src/helpers/__tests__/DiffPlaylist.spec.ts index ff108a3..e2905a4 100644 --- a/src/helpers/__tests__/DiffPlaylist.spec.ts +++ b/src/helpers/__tests__/DiffPlaylist.spec.ts @@ -1,9 +1,7 @@ import { DiffPlaylist, PlaylistChangeRundownCreated, - PlaylistChangeRundownDeleted, PlaylistChangeRundownMetaDataUpdated, - PlaylistChangeRundownUpdated, PlaylistChangeSegmentCreated, PlaylistChangeSegmentDeleted, PlaylistChangeSegmentMoved, @@ -61,104 +59,76 @@ function makeINewsStory(id: string, backTime?: string) { describe('DiffPlaylist', () => { it('Reports no change', () => { - let newPlaylist = [ - makeINewsRundown('test-rundown_1', [ - { - _id: 'segment-01', - }, - { - _id: 'segment-02', - }, - { - _id: 'segment-03', - }, - ]), - makeINewsRundown('test-rundown_2', [ - { - _id: 'segment-04', - }, - { - _id: 'segment-05', - }, - { - _id: 'segment-06', - }, - ]), - ] + let newPlaylist = makeINewsRundown('test-rundown_1', [ + { + _id: 'segment-01', + }, + { + _id: 'segment-02', + }, + { + _id: 'segment-03', + }, + ]) let result = DiffPlaylist(newPlaylist, newPlaylist) - expect(result.changes).toEqual([]) - expect(result.segmentChanges.get('test-rundown_1')).toEqual({ + expect(result.playlistChanges).toEqual([]) + expect(result.segmentChanges).toEqual({ movedSegments: [], notMovedSegments: ['segment-01', 'segment-02', 'segment-03'], insertedSegments: [], deletedSegments: [], changedSegments: [], }) - expect(result.segmentChanges.get('test-rundown_2')).toEqual({ - movedSegments: [], - notMovedSegments: ['segment-04', 'segment-05', 'segment-06'], - insertedSegments: [], - deletedSegments: [], - changedSegments: [], - }) }) it('Reports segments moved within rundown', () => { - let newPlaylist = [ - makeINewsRundown('test-rundown_1', [ - { - _id: 'segment-01', - }, - { - _id: 'segment-02', - }, - { - _id: 'segment-03', - }, - ]), - makeINewsRundown('test-rundown_2', [ - { - _id: 'segment-04', - }, - { - _id: 'segment-05', - }, - { - _id: 'segment-06', - }, - ]), - ] + let newPlaylist = makeINewsRundown('test-rundown_1', [ + { + _id: 'segment-01', + }, + { + _id: 'segment-02', + }, + { + _id: 'segment-03', + }, + { + _id: 'segment-04', + }, + { + _id: 'segment-05', + }, + { + _id: 'segment-06', + }, + ]) - let prevPlaylist = [ - makeINewsRundown('test-rundown_1', [ - { - _id: 'segment-02', - }, - { - _id: 'segment-01', - }, - { - _id: 'segment-03', - }, - ]), - makeINewsRundown('test-rundown_2', [ - { - _id: 'segment-04', - }, - { - _id: 'segment-06', - }, - { - _id: 'segment-05', - }, - ]), - ] + let prevPlaylist = makeINewsRundown('test-rundown_1', [ + { + _id: 'segment-02', + }, + { + _id: 'segment-01', + }, + { + _id: 'segment-03', + }, + { + _id: 'segment-04', + }, + { + _id: 'segment-06', + }, + { + _id: 'segment-05', + }, + ]) let result = DiffPlaylist(newPlaylist, prevPlaylist) - expect(result.changes).toEqual([ + expect(result.playlistChanges).toEqual([ literal({ type: PlaylistChangeType.PlaylistChangeSegmentMoved, rundownExternalId: 'test-rundown_1', @@ -166,92 +136,17 @@ describe('DiffPlaylist', () => { }), literal({ type: PlaylistChangeType.PlaylistChangeSegmentMoved, - rundownExternalId: 'test-rundown_2', - segmentExternalId: 'segment-06', - }), - literal({ - type: PlaylistChangeType.PlaylistChangeRundownMetaDataUpdated, rundownExternalId: 'test-rundown_1', + segmentExternalId: 'segment-06', }), literal({ type: PlaylistChangeType.PlaylistChangeRundownMetaDataUpdated, - rundownExternalId: 'test-rundown_2', - }), - ]) - expect(result.segmentChanges.get('test-rundown_1')).toEqual({ - movedSegments: ['segment-01'], - notMovedSegments: ['segment-02', 'segment-03'], - insertedSegments: [], - deletedSegments: [], - changedSegments: [], - }) - expect(result.segmentChanges.get('test-rundown_2')).toEqual({ - movedSegments: ['segment-06'], - notMovedSegments: ['segment-04', 'segment-05'], - insertedSegments: [], - deletedSegments: [], - changedSegments: [], - }) - }) - - it('Reports deleted rundown', () => { - let prevPlaylist = [ - makeINewsRundown('test-rundown_1', [ - { - _id: 'segment-01', - }, - { - _id: 'segment-02', - }, - { - _id: 'segment-03', - }, - ]), - makeINewsRundown('test-rundown_2', [ - { - _id: 'segment-04', - }, - { - _id: 'segment-05', - }, - { - _id: 'segment-06', - }, - ]), - ] - - let newPlaylist = [ - makeINewsRundown('test-rundown_2', [ - { - _id: 'segment-04', - }, - { - _id: 'segment-05', - }, - { - _id: 'segment-06', - }, - ]), - ] - - let result = DiffPlaylist(newPlaylist, prevPlaylist) - - expect(result.changes).toEqual([ - literal({ - type: PlaylistChangeType.PlaylistChangeRundownDeleted, rundownExternalId: 'test-rundown_1', }), ]) - expect(result.segmentChanges.get('test-rundown_1')).toEqual({ - movedSegments: [], - notMovedSegments: [], - insertedSegments: [], - deletedSegments: ['segment-01', 'segment-02', 'segment-03'], - changedSegments: [], - }) - expect(result.segmentChanges.get('test-rundown_2')).toEqual({ - movedSegments: [], - notMovedSegments: ['segment-04', 'segment-05', 'segment-06'], + expect(result.segmentChanges).toEqual({ + movedSegments: ['segment-01', 'segment-06'], + notMovedSegments: ['segment-02', 'segment-03', 'segment-04', 'segment-05'], insertedSegments: [], deletedSegments: [], changedSegments: [], @@ -259,117 +154,77 @@ describe('DiffPlaylist', () => { }) it('Reports created rundown', () => { - let prevPlaylist = [ - makeINewsRundown('test-rundown_2', [ - { - _id: 'segment-04', - }, - { - _id: 'segment-05', - }, - { - _id: 'segment-06', - }, - ]), - ] + let prevPlaylist = undefined - let newPlaylist = [ - makeINewsRundown('test-rundown_1', [ - { - _id: 'segment-01', - }, - { - _id: 'segment-02', - }, - { - _id: 'segment-03', - }, - ]), - makeINewsRundown('test-rundown_2', [ - { - _id: 'segment-04', - }, - { - _id: 'segment-05', - }, - { - _id: 'segment-06', - }, - ]), - ] + let newPlaylist = makeINewsRundown('test-rundown_1', [ + { + _id: 'segment-01', + }, + { + _id: 'segment-02', + }, + { + _id: 'segment-03', + }, + ]) let result = DiffPlaylist(newPlaylist, prevPlaylist) - expect(result.changes).toEqual([ + expect(result.playlistChanges).toEqual([ literal({ type: PlaylistChangeType.PlaylistChangeRundownCreated, rundownExternalId: 'test-rundown_1', }), ]) - expect(result.segmentChanges.get('test-rundown_1')).toEqual({ + expect(result.segmentChanges).toEqual({ movedSegments: [], notMovedSegments: [], insertedSegments: ['segment-01', 'segment-02', 'segment-03'], deletedSegments: [], changedSegments: [], }) - expect(result.segmentChanges.get('test-rundown_2')).toEqual({ - movedSegments: [], - notMovedSegments: ['segment-04', 'segment-05', 'segment-06'], - insertedSegments: [], - deletedSegments: [], - changedSegments: [], - }) }) it('Reports created segment', () => { - let prevPlaylist = [ - makeINewsRundown('test-rundown_1', [ - { - _id: 'segment-01', - }, - { - _id: 'segment-03', - }, - ]), - makeINewsRundown('test-rundown_2', [ - { - _id: 'segment-05', - }, - { - _id: 'segment-06', - }, - ]), - ] + let prevPlaylist = makeINewsRundown('test-rundown_1', [ + { + _id: 'segment-01', + }, + { + _id: 'segment-03', + }, + { + _id: 'segment-05', + }, + { + _id: 'segment-06', + }, + ]) - let newPlaylist = [ - makeINewsRundown('test-rundown_1', [ - { - _id: 'segment-01', - }, - { - _id: 'segment-02', - }, - { - _id: 'segment-03', - }, - ]), - makeINewsRundown('test-rundown_2', [ - { - _id: 'segment-04', - }, - { - _id: 'segment-05', - }, - { - _id: 'segment-06', - }, - ]), - ] + let newPlaylist = makeINewsRundown('test-rundown_1', [ + { + _id: 'segment-01', + }, + { + _id: 'segment-02', + }, + { + _id: 'segment-03', + }, + { + _id: 'segment-04', + }, + { + _id: 'segment-05', + }, + { + _id: 'segment-06', + }, + ]) let result = DiffPlaylist(newPlaylist, prevPlaylist) - expect(result.changes).toEqual([ + expect(result.playlistChanges).toEqual([ literal({ type: PlaylistChangeType.PlaylistChangeSegmentCreated, rundownExternalId: 'test-rundown_1', @@ -377,82 +232,63 @@ describe('DiffPlaylist', () => { }), literal({ type: PlaylistChangeType.PlaylistChangeSegmentCreated, - rundownExternalId: 'test-rundown_2', + rundownExternalId: 'test-rundown_1', segmentExternalId: 'segment-04', }), literal({ type: PlaylistChangeType.PlaylistChangeRundownMetaDataUpdated, rundownExternalId: 'test-rundown_1', }), - literal({ - type: PlaylistChangeType.PlaylistChangeRundownMetaDataUpdated, - rundownExternalId: 'test-rundown_2', - }), ]) - expect(result.segmentChanges.get('test-rundown_1')).toEqual({ - movedSegments: [], - notMovedSegments: ['segment-01', 'segment-03'], - insertedSegments: ['segment-02'], - deletedSegments: [], - changedSegments: [], - }) - expect(result.segmentChanges.get('test-rundown_2')).toEqual({ + expect(result.segmentChanges).toEqual({ movedSegments: [], - notMovedSegments: ['segment-05', 'segment-06'], - insertedSegments: ['segment-04'], + notMovedSegments: ['segment-01', 'segment-03', 'segment-05', 'segment-06'], + insertedSegments: ['segment-02', 'segment-04'], deletedSegments: [], changedSegments: [], }) }) it('Reports deleted segment', () => { - let prevPlaylist = [ - makeINewsRundown('test-rundown_1', [ - { - _id: 'segment-01', - }, - { - _id: 'segment-02', - }, - { - _id: 'segment-03', - }, - ]), - makeINewsRundown('test-rundown_2', [ - { - _id: 'segment-04', - }, - { - _id: 'segment-05', - }, - { - _id: 'segment-06', - }, - ]), - ] + let prevPlaylist = makeINewsRundown('test-rundown_1', [ + { + _id: 'segment-01', + }, + { + _id: 'segment-02', + }, + { + _id: 'segment-03', + }, + { + _id: 'segment-04', + }, + { + _id: 'segment-05', + }, + { + _id: 'segment-06', + }, + ]) - let newPlaylist = [ - makeINewsRundown('test-rundown_1', [ - { - _id: 'segment-01', - }, - { - _id: 'segment-03', - }, - ]), - makeINewsRundown('test-rundown_2', [ - { - _id: 'segment-05', - }, - { - _id: 'segment-06', - }, - ]), - ] + let newPlaylist = makeINewsRundown('test-rundown_1', [ + { + _id: 'segment-01', + }, + { + _id: 'segment-03', + }, + { + _id: 'segment-05', + }, + { + _id: 'segment-06', + }, + ]) let result = DiffPlaylist(newPlaylist, prevPlaylist) - expect(result.changes).toEqual([ + expect(result.playlistChanges).toEqual([ literal({ type: PlaylistChangeType.PlaylistChangeSegmentDeleted, rundownExternalId: 'test-rundown_1', @@ -460,211 +296,20 @@ describe('DiffPlaylist', () => { }), literal({ type: PlaylistChangeType.PlaylistChangeSegmentDeleted, - rundownExternalId: 'test-rundown_2', - segmentExternalId: 'segment-04', - }), - literal({ - type: PlaylistChangeType.PlaylistChangeRundownMetaDataUpdated, rundownExternalId: 'test-rundown_1', + segmentExternalId: 'segment-04', }), literal({ type: PlaylistChangeType.PlaylistChangeRundownMetaDataUpdated, - rundownExternalId: 'test-rundown_2', - }), - ]) - expect(result.segmentChanges.get('test-rundown_1')).toEqual({ - movedSegments: [], - notMovedSegments: ['segment-01', 'segment-03'], - insertedSegments: [], - deletedSegments: ['segment-02'], - changedSegments: [], - }) - expect(result.segmentChanges.get('test-rundown_2')).toEqual({ - movedSegments: [], - notMovedSegments: ['segment-05', 'segment-06'], - insertedSegments: [], - deletedSegments: ['segment-04'], - changedSegments: [], - }) - }) - - it('Emits rundown create over segment create', () => { - let prevPlaylist = [ - makeINewsRundown('test-rundown_2', [ - { - _id: 'segment-04', - }, - { - _id: 'segment-05', - }, - { - _id: 'segment-06', - }, - ]), - ] - - let newPlaylist = [ - makeINewsRundown('test-rundown_1', [ - { - _id: 'segment-01', - }, - { - _id: 'segment-02', - }, - { - _id: 'segment-03', - }, - ]), - makeINewsRundown('test-rundown_2', [ - { - _id: 'segment-04', - }, - { - _id: 'segment-05', - }, - { - _id: 'segment-06', - }, - ]), - ] - - let result = DiffPlaylist(newPlaylist, prevPlaylist) - - expect(result.changes).toEqual([ - literal({ - type: PlaylistChangeType.PlaylistChangeRundownCreated, rundownExternalId: 'test-rundown_1', }), ]) - expect(result.segmentChanges.get('test-rundown_1')).toEqual({ - movedSegments: [], - notMovedSegments: [], - insertedSegments: ['segment-01', 'segment-02', 'segment-03'], - deletedSegments: [], - changedSegments: [], - }) - expect(result.segmentChanges.get('test-rundown_2')).toEqual({ - movedSegments: [], - notMovedSegments: ['segment-04', 'segment-05', 'segment-06'], - insertedSegments: [], - deletedSegments: [], - changedSegments: [], - }) - }) - - it('tests if adding a showstyle variant triggers update meta data', () => { - const prevPlaylist = createPlaylistWithDefaultSegments('test-rundown_1') - const newPlaylist = createPlaylistWithDefaultSegments('test-rundown_1', 'TV2 Nyhederne') - - const result = DiffPlaylist(newPlaylist, prevPlaylist) - - expect(result.changes).toContainEqual( - literal({ - type: PlaylistChangeType.PlaylistChangeRundownMetaDataUpdated, - rundownExternalId: 'test-rundown_1', - }) - ) - expect(result.segmentChanges.get('test-rundown_1')).toEqual({ + expect(result.segmentChanges).toEqual({ movedSegments: [], - notMovedSegments: ['segment-01', 'segment-02', 'segment-03'], - insertedSegments: [], - deletedSegments: [], - changedSegments: [], - }) - }) - - it('tests that keeping a showstyle variant does not trigger any updates.', () => { - const prevPlaylist = createPlaylistWithDefaultSegments('test-rundown_1', 'TV2 Nyhederne') - const newPlaylist = createPlaylistWithDefaultSegments('test-rundown_1', 'TV2 Nyhederne') - - const result = DiffPlaylist(newPlaylist, prevPlaylist) - - expect(result.changes).toEqual([]) - expect(result.segmentChanges.get('test-rundown_1')).toEqual({ - movedSegments: [], - notMovedSegments: ['segment-01', 'segment-02', 'segment-03'], - insertedSegments: [], - deletedSegments: [], - changedSegments: [], - }) - }) - - it('tests if changing a showstyle variant triggers update meta data', () => { - const prevPlaylist = createPlaylistWithDefaultSegments('test-rundown_1', 'TV2 Nyhederne') - const newPlaylist = createPlaylistWithDefaultSegments('test-rundown_1', 'TV2 Sporten') - - const result = DiffPlaylist(newPlaylist, prevPlaylist) - - expect(result.changes).toContainEqual( - literal({ - type: PlaylistChangeType.PlaylistChangeRundownMetaDataUpdated, - rundownExternalId: 'test-rundown_1', - }) - ) - expect(result.segmentChanges.get('test-rundown_1')).toEqual({ - movedSegments: [], - notMovedSegments: ['segment-01', 'segment-02', 'segment-03'], - insertedSegments: [], - deletedSegments: [], - changedSegments: [], - }) - }) - - it('tests if deleting a showstyle variant triggers update meta data', () => { - const prevPlaylist = createPlaylistWithDefaultSegments('test-rundown_1', 'TV2 Nyhederne') - const newPlaylist = createPlaylistWithDefaultSegments('test-rundown_1') - - const result = DiffPlaylist(newPlaylist, prevPlaylist) - - expect(result.changes).toContainEqual( - literal({ - type: PlaylistChangeType.PlaylistChangeRundownMetaDataUpdated, - rundownExternalId: 'test-rundown_1', - }) - ) - expect(result.segmentChanges.get('test-rundown_1')).toEqual({ - movedSegments: [], - notMovedSegments: ['segment-01', 'segment-02', 'segment-03'], + notMovedSegments: ['segment-01', 'segment-03', 'segment-05', 'segment-06'], insertedSegments: [], - deletedSegments: [], + deletedSegments: ['segment-02', 'segment-04'], changedSegments: [], }) }) - - it('triggers updateRundown when showStyleVariant changes', () => { - const rundownId = 'test-rundown_1' - const playlist = createPlaylistWithDefaultSegments(rundownId, 'TV2 Nyhederne') - const updatedPlaylist = createPlaylistWithDefaultSegments(rundownId, 'TV2 Sporten') - - const result = DiffPlaylist(playlist, updatedPlaylist) - - expect(result.changes).toContainEqual( - literal({ - type: PlaylistChangeType.PlaylistChangeRundownUpdated, - rundownExternalId: rundownId, - }) - ) - }) }) - -function createPlaylistWithDefaultSegments(rundownId: string, showstyleVariant?: string): INewsRundown[] { - return [ - makeINewsRundown( - rundownId, - [ - { - _id: 'segment-01', - }, - { - _id: 'segment-02', - }, - { - _id: 'segment-03', - }, - ], - { - showstyleVariant, - } - ), - ] -} diff --git a/src/helpers/__tests__/GenerateCoreCalls.spec.ts b/src/helpers/__tests__/GenerateCoreCalls.spec.ts index cee4c4b..e5603dd 100644 --- a/src/helpers/__tests__/GenerateCoreCalls.spec.ts +++ b/src/helpers/__tests__/GenerateCoreCalls.spec.ts @@ -1,7 +1,7 @@ import { PlaylistChange, PlaylistChangeType } from '../DiffPlaylist' import { CoreCall, CoreCallType, GenerateCoreCalls } from '../GenerateCoreCalls' import { PlaylistId, SegmentId } from '../id' -import { ResolvedPlaylist } from '../ResolveRundownIntoPlaylist' +import { ResolvedPlaylistRundown } from '../ResolveRundownIntoPlaylist' import { UnrankedSegment } from '../../classes/RundownWatcher' import { literal } from '../../helpers' import { INewsFields, INewsStory } from 'inews' @@ -22,12 +22,10 @@ describe('GenerateCoreCalls', () => { }, ] const playlistId: PlaylistId = 'playlistId' - const playlistAssignments: ResolvedPlaylist = [ - { - rundownId: rundownExternalId, - segments: [], - }, - ] + const resolvedRundown: ResolvedPlaylistRundown = { + rundownId: rundownExternalId, + segments: [], + } const assignedRanks: Map = new Map() assignedRanks.set(segmentExternalId, 1) @@ -45,7 +43,7 @@ describe('GenerateCoreCalls', () => { const result: CoreCall[] = GenerateCoreCalls( playlistId, changes, - playlistAssignments, + resolvedRundown, assignedRanks, iNewsDataCache, new Map(), diff --git a/src/helpers/__tests__/ResolveRundownIntoPlaylist.spec.ts b/src/helpers/__tests__/ResolveRundownIntoPlaylist.spec.ts index d2157be..87e6d5b 100644 --- a/src/helpers/__tests__/ResolveRundownIntoPlaylist.spec.ts +++ b/src/helpers/__tests__/ResolveRundownIntoPlaylist.spec.ts @@ -1,5 +1,5 @@ import { literal } from '../../helpers' -import { ResolvedPlaylist, ResolveRundownIntoPlaylist } from '../ResolveRundownIntoPlaylist' +import { ResolvedPlaylistRundown, ResolveRundownIntoPlaylist } from '../ResolveRundownIntoPlaylist' import { UnrankedSegment } from '../../classes/RundownWatcher' import { INewsStory, INewsFields } from 'inews' @@ -136,13 +136,11 @@ describe('Resolve Rundown Into Playlist', () => { const result = ResolveRundownIntoPlaylist('test-playlist', segments) expect(result).toEqual({ - resolvedPlaylist: literal([ - { - rundownId: 'test-playlist_1', - segments: ['segment-01', 'segment-02', 'segment-03'], - payload: { rank: 0 }, - }, - ]), + resolvedRundown: literal({ + rundownId: 'test-playlist_1', + segments: ['segment-01', 'segment-02', 'segment-03'], + payload: { rank: 0 }, + }), untimedSegments: new Set(), }) }) @@ -158,14 +156,12 @@ describe('Resolve Rundown Into Playlist', () => { const result = ResolveRundownIntoPlaylist('test-playlist', segments) expect(result).toEqual({ - resolvedPlaylist: literal([ - { - rundownId: 'test-playlist_1', - segments: ['segment-01', 'segment-02', 'segment-03', 'segment-04'], - backTime: '@1234', - payload: { rank: 0 }, - }, - ]), + resolvedRundown: literal({ + rundownId: 'test-playlist_1', + segments: ['segment-01', 'segment-02', 'segment-03', 'segment-04'], + backTime: '@1234', + payload: { rank: 0 }, + }), untimedSegments: new Set(['segment-04']), }) }) @@ -182,14 +178,12 @@ describe('Resolve Rundown Into Playlist', () => { const result = ResolveRundownIntoPlaylist('test-playlist', segments) expect(result).toEqual({ - resolvedPlaylist: literal([ - { - rundownId: 'test-playlist_1', - segments: ['segment-01', 'segment-02', 'segment-03', 'segment-04', 'segment-05'], - backTime: '@1234', - payload: { rank: 0 }, - }, - ]), + resolvedRundown: literal({ + rundownId: 'test-playlist_1', + segments: ['segment-01', 'segment-02', 'segment-03', 'segment-04', 'segment-05'], + backTime: '@1234', + payload: { rank: 0 }, + }), untimedSegments: new Set(['segment-04', 'segment-05']), }) }) @@ -207,14 +201,12 @@ describe('Resolve Rundown Into Playlist', () => { const result = ResolveRundownIntoPlaylist('test-playlist', segments) expect(result).toEqual({ - resolvedPlaylist: literal([ - { - rundownId: 'test-playlist_1', - segments: ['segment-01', 'segment-02', 'segment-03', 'segment-04', 'segment-05', 'segment-06'], - backTime: '@1234', - payload: { rank: 0 }, - }, - ]), + resolvedRundown: literal({ + rundownId: 'test-playlist_1', + segments: ['segment-01', 'segment-02', 'segment-03', 'segment-04', 'segment-05', 'segment-06'], + backTime: '@1234', + payload: { rank: 0 }, + }), untimedSegments: new Set(['segment-04', 'segment-05', 'segment-06']), }) }) @@ -232,13 +224,11 @@ describe('Resolve Rundown Into Playlist', () => { const result = ResolveRundownIntoPlaylist('test-playlist', segments) expect(result).toEqual({ - resolvedPlaylist: literal([ - { - rundownId: 'test-playlist_1', - segments: ['segment-01', 'segment-02', 'segment-03', 'segment-04', 'segment-05', 'segment-06'], - payload: { rank: 0 }, - }, - ]), + resolvedRundown: literal({ + rundownId: 'test-playlist_1', + segments: ['segment-01', 'segment-02', 'segment-03', 'segment-04', 'segment-05', 'segment-06'], + payload: { rank: 0 }, + }), untimedSegments: new Set(['segment-04', 'segment-05', 'segment-06']), }) }) @@ -255,13 +245,11 @@ describe('Resolve Rundown Into Playlist', () => { const result = ResolveRundownIntoPlaylist('test-playlist', segments) expect(result).toEqual({ - resolvedPlaylist: literal([ - { - rundownId: 'test-playlist_1', - segments: ['segment-01', 'segment-02', 'segment-03', 'segment-04', 'segment-05'], - payload: { rank: 0 }, - }, - ]), + resolvedRundown: literal({ + rundownId: 'test-playlist_1', + segments: ['segment-01', 'segment-02', 'segment-03', 'segment-04', 'segment-05'], + payload: { rank: 0 }, + }), untimedSegments: new Set(['segment-02']), }) }) @@ -272,13 +260,11 @@ describe('Resolve Rundown Into Playlist', () => { const result = ResolveRundownIntoPlaylist('test-playlist', segments) expect(result).toEqual({ - resolvedPlaylist: literal([ - { - rundownId: 'test-playlist_1', - segments: ['segment-01'], - payload: { rank: 0 }, - }, - ]), + resolvedRundown: literal({ + rundownId: 'test-playlist_1', + segments: ['segment-01'], + payload: { rank: 0 }, + }), untimedSegments: new Set([]), }) }) @@ -289,302 +275,12 @@ describe('Resolve Rundown Into Playlist', () => { const result = ResolveRundownIntoPlaylist('test-playlist', segments) expect(result).toEqual({ - resolvedPlaylist: literal([ - { - rundownId: 'test-playlist_1', - segments: ['segment-01'], - payload: { rank: 0 }, - }, - ]), + resolvedRundown: literal({ + rundownId: 'test-playlist_1', + segments: ['segment-01'], + payload: { rank: 0 }, + }), untimedSegments: new Set([]), }) }) - - // Note: Disabling rundowns temporarily for v42.0. - // it('tests that a KLAR ON AIR with ShowstyleVariant sets the rundown showstyleVariant', () => { - // const segments = [ - // createKlarOnAirSegment(1, { - // cues: ['SOFIE=SHOWSTYLEVARIANT\nTV2 Nyhederne'.split('\n')], - // body: '

', - // }), - // ] - // const resolvedPlayList = ResolveRundownIntoPlaylist('test-playlist', segments) - - // expect(resolvedPlayList).toEqual({ - // resolvedPlaylist: literal([ - // { - // rundownId: 'test-playlist_1', - // segments: ['segment-01'], - // payload: { showstyleVariant: 'TV2 Nyhederne', rank: 0 }, - // }, - // ]), - // untimedSegments: new Set(['segment-01']), - // }) - // }) - - // it('tests that only the first KLAR ON AIR with ShowstyleVariant sets the rundown showstyleVariant', () => { - // const segments = [ - // createKlarOnAirSegment(1, { - // cues: ['SOFIE=SHOWSTYLEVARIANT\nTV2 Nyhederne'.split('\n'), null], - // body: '

\n

', - // }), - // createUnrankedSegment(2), - // createKlarOnAirSegment(3, { - // cues: ['SOFIE=SHOWSTYLEVARIANT\nTV2 Sporten'.split('\n')], - // body: '

', - // }), - // ] - // const resolvedPlayList = ResolveRundownIntoPlaylist('test-playlist', segments) - - // expect(resolvedPlayList).toEqual({ - // resolvedPlaylist: literal([ - // { - // rundownId: 'test-playlist_1', - // segments: ['segment-01', 'segment-02'], - // payload: { showstyleVariant: 'TV2 Nyhederne', rank: 0 }, - // }, - // { - // rundownId: 'test-playlist_2', - // segments: ['segment-03'], - // payload: { showstyleVariant: 'TV2 Sporten', rank: 1 }, - // }, - // ]), - // untimedSegments: new Set(['segment-01']), - // }) - // }) - - // it('tests that we can set showstyleVariant in non KLAR-ON-AIR stories', () => { - // const segments = [ - // createUnrankedSegment(1, { - // cues: ['SOFIE=SHOWSTYLEVARIANT\nTV2 Nyhederne'.split('\n')], - // body: '

', - // }), - // createUnnamedSegment(2, '', { - // cues: ['SOFIE=SHOWSTYLEVARIANT\nTV2 Sporten'.split('\n')], - // body: '

', - // }), - // ] - // const resolvedPlayList = ResolveRundownIntoPlaylist('test-playlist', segments) - - // expect(resolvedPlayList).toEqual({ - // resolvedPlaylist: literal([ - // { - // rundownId: 'test-playlist_1', - // segments: ['segment-01'], - // payload: { showstyleVariant: 'TV2 Nyhederne', rank: 0 }, - // }, - // { - // rundownId: 'test-playlist_2', - // segments: ['segment-02'], - // payload: { showstyleVariant: 'TV2 Sporten', rank: 1 }, - // }, - // ]), - // untimedSegments: new Set([]), - // }) - // }) - - // it('tests that we only care about the first cue', () => { - // const segments = [ - // createKlarOnAirSegment(1, { - // cues: [ - // 'SOFIE=SHOWSTYLEVARIANT\nTV2 Nyhederne'.split('\n'), - // 'DVE=SOMMERFUGL\nINP1=KAM 1\nINP2=KAM 2\nBYNAVN=ODENSE/KØBENHAVN\n'.split('\n'), - // 'SOFIE=SHOWSTYLEVARIANT\nTV2 Sporten'.split('\n'), - // 'SOFIE=SHOWSTYLEVARIANT\nTV2 News'.split('\n'), - // ], - // body: '

\n

\n

\n

\n', - // }), - // createUnnamedSegment(2, '', { - // cues: ['SOFIE=SHOWSTYLEVARIANT\nTV2 Sporten'.split('\n')], - // body: '

', - // }), - // ] - // const resolvedPlayList = ResolveRundownIntoPlaylist('test-playlist', segments) - - // expect(resolvedPlayList).toEqual({ - // resolvedPlaylist: literal([ - // { - // rundownId: 'test-playlist_1', - // segments: ['segment-01'], - // payload: { showstyleVariant: 'TV2 Nyhederne', rank: 0 }, - // }, - // { - // rundownId: 'test-playlist_2', - // segments: ['segment-02'], - // payload: { showstyleVariant: 'TV2 Sporten', rank: 1 }, - // }, - // ]), - // untimedSegments: new Set(['segment-01']), - // }) - // }) - - // it('tests that we pick the first showstyle variant cue', () => { - // const segments = [ - // createKlarOnAirSegment(1, { - // cues: [ - // null, - // 'SOFIE=SHOWSTYLEVARIANT\nTV2 Nyhederne'.split('\n'), - // 'DVE=SOMMERFUGL\nINP1=KAM 1\nINP2=KAM 2\nBYNAVN=ODENSE/KØBENHAVN\n'.split('\n'), - // null, - // 'SOFIE=SHOWSTYLEVARIANT\nTV2 Sporten'.split('\n'), - // 'SOFIE=SHOWSTYLEVARIANT\nTV2 News'.split('\n'), - // ], - // body: - // '

something

\n

\n

\n

\n

\n', - // }), - // ] - // const resolvedPlayList = ResolveRundownIntoPlaylist('test-playlist', segments) - - // expect(resolvedPlayList).toEqual({ - // resolvedPlaylist: literal([ - // { - // rundownId: 'test-playlist_1', - // segments: ['segment-01'], - // payload: { showstyleVariant: 'TV2 Sporten', rank: 0 }, - // }, - // ]), - // untimedSegments: new Set(['segment-01']), - // }) - // }) - - // it('tests that we only care about non-floated segments', () => { - // const segments = [ - // createUnrankedSegment(1), - // createKlarOnAirSegment(2, { - // cues: ['SOFIE=SHOWSTYLEVARIANT\nTV2 Nyhederne'.split('\n')], - // body: '

', - // meta: { float: true }, - // }), - // createUnrankedSegment(3, { - // cues: ['SOFIE=SHOWSTYLEVARIANT\nTV2 Sporten'.split('\n')], - // body: '

', - // }), - // ] - // const resolvedPlayList = ResolveRundownIntoPlaylist('test-playlist', segments) - - // expect(resolvedPlayList).toEqual({ - // resolvedPlaylist: literal([ - // { - // rundownId: 'test-playlist_1', - // segments: ['segment-01', 'segment-02'], - // payload: { rank: 0 }, - // }, - // { - // rundownId: 'test-playlist_2', - // segments: ['segment-03'], - // payload: { showstyleVariant: 'TV2 Sporten', rank: 1 }, - // }, - // ]), - // untimedSegments: new Set(), - // }) - // }) - - // it('tests that an empty idref is not parsed.', () => { - // const segments = [ - // createUnrankedSegment(1), - // createKlarOnAirSegment(2, { - // cues: ['SOFIE=SHOWSTYLEVARIANT\nTV2 Nyhederne'.split('\n')], - // body: '

', - // meta: { float: true }, - // }), - // createKlarOnAirSegment(3, { - // cues: ['SOFIE=SHOWSTYLEVARIANT\nTV2 Sporten'.split('\n')], - // body: '

', - // }), - // ] - // const resolvedPlayList = ResolveRundownIntoPlaylist('test-playlist', segments) - - // expect(resolvedPlayList).toEqual({ - // resolvedPlaylist: literal([ - // { - // rundownId: 'test-playlist_1', - // segments: ['segment-01', 'segment-02', 'segment-03'], - // payload: { rank: 0 }, - // }, - // ]), - // untimedSegments: new Set(['segment-03']), - // }) - // }) - - // it('tests that a non-digit idref is not parsed.', () => { - // const segments = [ - // createUnrankedSegment(1), - // createKlarOnAirSegment(2, { - // cues: ['SOFIE=SHOWSTYLEVARIANT\nTV2 Nyhederne'.split('\n')], - // body: '

', - // meta: { float: true }, - // }), - // createKlarOnAirSegment(3, { - // cues: ['SOFIE=SHOWSTYLEVARIANT\nTV2 Sporten'.split('\n')], - // body: '

', - // }), - // ] - // const resolvedPlayList = ResolveRundownIntoPlaylist('test-playlist', segments) - - // expect(resolvedPlayList).toEqual({ - // resolvedPlaylist: literal([ - // { - // rundownId: 'test-playlist_1', - // segments: ['segment-01', 'segment-02', 'segment-03'], - // payload: { rank: 0 }, - // }, - // ]), - // untimedSegments: new Set(['segment-03']), - // }) - // }) - - // it('tests that a non-digit (with digits) idref is not parsed.', () => { - // const segments = [ - // createUnrankedSegment(1), - // createKlarOnAirSegment(2, { - // cues: ['SOFIE=SHOWSTYLEVARIANT\nTV2 Nyhederne'.split('\n')], - // body: '

', - // meta: { float: true }, - // }), - // createKlarOnAirSegment(3, { - // cues: ['SOFIE=SHOWSTYLEVARIANT\nTV2 Sporten'.split('\n')], - // body: '

', - // }), - // ] - // const resolvedPlayList = ResolveRundownIntoPlaylist('test-playlist', segments) - - // expect(resolvedPlayList).toEqual({ - // resolvedPlaylist: literal([ - // { - // rundownId: 'test-playlist_1', - // segments: ['segment-01', 'segment-02', 'segment-03'], - // payload: { rank: 0 }, - // }, - // ]), - // untimedSegments: new Set(['segment-03']), - // }) - // }) - - // it('tests that showstyle splits correctly even when having a non-empty floated segment prior.', () => { - // const segments = [ - // createUnrankedSegment(1, { - // body: '

', - // meta: { float: true }, - // }), - // createKlarOnAirSegment(2, { - // cues: ['SOFIE=SHOWSTYLEVARIANT\nTV2 Nyhederne'.split('\n')], - // body: '

', - // }), - // createKlarOnAirSegment(3, { - // body: '

', - // }), - // ] - // const resolvedPlayList = ResolveRundownIntoPlaylist('test-playlist', segments) - - // expect(resolvedPlayList).toEqual({ - // resolvedPlaylist: literal([ - // { - // rundownId: 'test-playlist_1', - // segments: ['segment-01', 'segment-02', 'segment-03'], - // payload: { showstyleVariant: 'TV2 Nyhederne', rank: 0 }, - // }, - // ]), - // untimedSegments: new Set(['segment-02']), - // }) - // }) }) From cb2616ff0165006e3a4244f1d5ec40f3441dae74 Mon Sep 17 00:00:00 2001 From: Rasmus Lindved Date: Fri, 13 Oct 2023 13:20:41 +0200 Subject: [PATCH 02/14] SOF-1555 Floated Segments are now treated as either inserted or deleted --- src/classes/RundownWatcher.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/classes/RundownWatcher.ts b/src/classes/RundownWatcher.ts index 4593d10..40f8896 100755 --- a/src/classes/RundownWatcher.ts +++ b/src/classes/RundownWatcher.ts @@ -371,6 +371,9 @@ export class RundownWatcher extends EventEmitter { `Could not find iNews data for segment ${s.externalId} in rundown ${playlistId}. Segment will appear out of order.` ) } else { + if (cachedData.iNewsStory.meta.float) { + return + } segmentsToResolve.push(cachedData) } }) From 09d1f539c161a6d875edff86b687331261bc3404 Mon Sep 17 00:00:00 2001 From: Rasmus Date: Mon, 30 Oct 2023 13:59:06 +0100 Subject: [PATCH 03/14] feat: adds koa endpoint for accessing the resycing of a rundown --- package.json | 6 +- src/connector.ts | 24 +++ yarn.lock | 418 ++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 445 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index b8d97fc..a55bc0d 100755 --- a/package.json +++ b/package.json @@ -74,15 +74,19 @@ "dependencies": { "@sofie-automation/blueprints-integration": "npm:@tv2media/blueprints-integration@46.2.0", "@sofie-automation/server-core-integration": "npm:@tv2media/server-core-integration@46.2.0", - "@tv2media/logger": "^1.2.4", "@sofie-automation/shared-lib": "npm:@tv2media/shared-lib@46.2.0", + "@tv2media/logger": "^1.2.4", "@types/dotenv": "^6.1.1", + "@types/koa": "^2.13.10", + "@types/koa-router": "^7.4.6", "@types/xml2js": "^0.4.4", "async-mutex": "^0.3.1", "cross-env": "^7.0.3", "dotenv": "^8.0.0", "inews": "npm:@tv2media/inews@1.3.4", "jobs-queue": "git+https://github.com/sparkpunkd/node-jobs-queue.git#master", + "koa": "^2.14.2", + "koa-router": "^12.0.1", "lodash.clonedeep": "^4.5.0", "tslib": "^2.0.0", "underscore": "^1.9.1", diff --git a/src/connector.ts b/src/connector.ts index c5c0b7f..f68d910 100755 --- a/src/connector.ts +++ b/src/connector.ts @@ -6,6 +6,8 @@ import { Observer } from '@sofie-automation/server-core-integration' import { ensureLogLevel, setLogLevel } from './logger' import { ILogger as Logger } from '@tv2media/logger' import { unprotectString } from '@sofie-automation/shared-lib/dist/lib/protectedString' +import * as Koa from 'koa' +import * as KoaRouter from 'koa-router' export interface Config { process: ProcessConfig @@ -31,6 +33,9 @@ export class Connector { private _process: Process private _settings?: INewsDeviceSettings private _debug: boolean + private koaApp: Koa + private koaRouter: KoaRouter + constructor(logger: Logger, config: Config, debug: boolean) { this._logger = logger.tag(this.constructor.name) @@ -40,6 +45,8 @@ export class Connector { this.coreHandler = new CoreHandler(this._logger, this._config.device) this.iNewsFTPHandler = new InewsFTPHandler(this._logger, this.coreHandler) this.coreHandler.iNewsHandler = this.iNewsFTPHandler + this.koaApp = new Koa() + this.koaRouter = new KoaRouter() } async init(): Promise { @@ -52,6 +59,7 @@ export class Connector { this._logger.info('Core is initialized') this.setupObserver() this._logger.info('Initialization of FTP-monitor done') + this.setupReloadDataKoaEndpoint() } catch (err) { this._logger.data(err).error(`Error during initialization:`) @@ -134,4 +142,20 @@ export class Connector { addedChanged(unprotectString(this.coreHandler.core.deviceId)) } + + private setupReloadDataKoaEndpoint(): void { + this.koaRouter.get('/reloadData/:rundownName', async (ctx, next): Promise => { + const RUNDOWN_EXTERNAL_ID_SUFFIX = '_1' + if (!this.iNewsFTPHandler.iNewsWatcher) { + return + } + this.iNewsFTPHandler.iNewsWatcher?.ResyncRundown(ctx.params.rundownName + RUNDOWN_EXTERNAL_ID_SUFFIX) + + await next() + }) + + this.koaApp.use(this.koaRouter.routes()).use(this.koaRouter.allowedMethods()) + + this.koaApp.listen(3007, () => {}) + } } diff --git a/yarn.lock b/yarn.lock index 8760078..11b8bf5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -459,6 +459,13 @@ resolved "https://registry.yarnpkg.com/@tv2media/logger/-/logger-1.2.4.tgz#44a3c36e76da34c3c6501d08f33ab563e11cb9ea" integrity sha512-6IE5U2dZEsmHYvpdSMnoS0HHArEDBfAJELkvBwSEKQNFo05SJh26VcG4b7UB+w35+dO6bKkmJjbMwaZ80NBF5Q== +"@types/accepts@*": + version "1.3.6" + resolved "https://registry.yarnpkg.com/@types/accepts/-/accepts-1.3.6.tgz#5e33830c8ed7b6025976738c88540df2e85b6d91" + integrity sha512-6+qlUg57yfE9OO63wnsJXLeq9cG3gSHBBIxNMOjNrbDRlDnm/NaR7RctfYcVCPq+j7d+MwOxqVEludH5+FKrlg== + dependencies: + "@types/node" "*" + "@types/babel__core@^7.1.0": version "7.1.18" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.18.tgz#1a29abcc411a9c05e2094c98f9a1b7da6cdf49f8" @@ -492,6 +499,14 @@ dependencies: "@babel/types" "^7.3.0" +"@types/body-parser@*": + version "1.19.4" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.4.tgz#78ad68f1f79eb851aa3634db0c7f57f6f601b462" + integrity sha512-N7UDG0/xiPQa2D/XrVJXjkWbpqHCd2sBaB32ggRF2l83RhPfamgKGF8gwwqyksS95qUS5ZYF9aF+lLPRlwI2UA== + dependencies: + "@types/connect" "*" + "@types/node" "*" + "@types/cacheable-request@^6.0.1": version "6.0.2" resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.2.tgz#c324da0197de0a98a2312156536ae262429ff6b9" @@ -502,6 +517,28 @@ "@types/node" "*" "@types/responselike" "*" +"@types/connect@*": + version "3.4.37" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.37.tgz#c66a96689fd3127c8772eb3e9e5c6028ec1a9af5" + integrity sha512-zBUSRqkfZ59OcwXon4HVxhx5oWCJmc0OtBTK05M+p0dYjgN6iTwIL2T/WbsQZrEsdnwaF9cWQ+azOnpPvIqY3Q== + dependencies: + "@types/node" "*" + +"@types/content-disposition@*": + version "0.5.7" + resolved "https://registry.yarnpkg.com/@types/content-disposition/-/content-disposition-0.5.7.tgz#3b98d4bf8c80640f93b042511acb5aad18139748" + integrity sha512-V9/5u21RHFR1zfdm3rQ6pJUKV+zSSVQt+yq16i1YhdivVzWgPEoKedc3GdT8aFjsqQbakdxuy3FnEdePUQOamQ== + +"@types/cookies@*": + version "0.7.9" + resolved "https://registry.yarnpkg.com/@types/cookies/-/cookies-0.7.9.tgz#d03e9af454b8e23718c9bf0d522649a64d5e67cf" + integrity sha512-SrGYvhKohd/WSOII0WpflC73RgdJhQoqpwq9q+n/qugNGiDSGYXfHy3QvB4+X+J/gYe27j2fSRnK4B+1A3nvsw== + dependencies: + "@types/connect" "*" + "@types/express" "*" + "@types/keygrip" "*" + "@types/node" "*" + "@types/dotenv@^6.1.1": version "6.1.1" resolved "https://registry.yarnpkg.com/@types/dotenv/-/dotenv-6.1.1.tgz#f7ce1cc4fe34f0a4373ba99fefa437b0bec54b46" @@ -509,11 +546,41 @@ dependencies: "@types/node" "*" +"@types/express-serve-static-core@^4.17.33": + version "4.17.39" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.39.tgz#2107afc0a4b035e6cb00accac3bdf2d76ae408c8" + integrity sha512-BiEUfAiGCOllomsRAZOiMFP7LAnrifHpt56pc4Z7l9K6ACyN06Ns1JLMBxwkfLOjJRlSf06NwWsT7yzfpaVpyQ== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + "@types/send" "*" + +"@types/express@*": + version "4.17.20" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.20.tgz#e7c9b40276d29e38a4e3564d7a3d65911e2aa433" + integrity sha512-rOaqlkgEvOW495xErXMsmyX3WKBInbhG5eqojXYi3cGUaLoRDlXa5d52fkfWZT963AZ3v2eZ4MbKE6WpDAGVsw== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^4.17.33" + "@types/qs" "*" + "@types/serve-static" "*" + +"@types/http-assert@*": + version "1.5.4" + resolved "https://registry.yarnpkg.com/@types/http-assert/-/http-assert-1.5.4.tgz#2ee41bf930d871a76cdbee5c385ccbb3604302e6" + integrity sha512-/6M9aaVk+avzCsrv1lt39AlFw4faCNI6aGll91Rxj38ZE5JI8AxApyQIRy+i1McjiJiuQ0sfuoMLxqq374ZIbA== + "@types/http-cache-semantics@*": version "4.0.1" resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz#0ea7b61496902b95890dc4c3a116b60cb8dae812" integrity sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ== +"@types/http-errors@*": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.3.tgz#c54e61f79b3947d040f150abd58f71efb422ff62" + integrity sha512-pP0P/9BnCj1OVvQR2lF41EkDG/lWWnDyA203b/4Fmi2eTyORnBtcDoKDwjWQthELrBvWkMOrvSOnZ8OVlW6tXA== + "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0": version "2.0.4" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz#8467d4b3c087805d63580480890791277ce35c44" @@ -553,6 +620,11 @@ resolved "https://registry.yarnpkg.com/@types/json-buffer/-/json-buffer-3.0.0.tgz#85c1ff0f0948fc159810d4b5be35bf8c20875f64" integrity sha512-3YP80IxxFJB4b5tYC2SUPwkg0XQLiu0nWvhRgEatgjf+29IcWO9X1k8xRv5DGssJ/lCrjYTjQPcobJr2yWIVuQ== +"@types/keygrip@*": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@types/keygrip/-/keygrip-1.0.4.tgz#c4015f09e66633bdea5d26916810001272b1ac6a" + integrity sha512-/tjWYD8StMrINelsrHNmpXceo9s3/Y22AzePH1qCvXIgmz/aQp2YFFr6HqhNQVIOdcvaVyp5GS+yjHGuF7Rwsg== + "@types/keyv@*": version "3.1.4" resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.4.tgz#3ccdb1c6751b0c7e52300bcdacd5bcbf8faa75b6" @@ -560,6 +632,34 @@ dependencies: "@types/node" "*" +"@types/koa-compose@*": + version "3.2.7" + resolved "https://registry.yarnpkg.com/@types/koa-compose/-/koa-compose-3.2.7.tgz#7bbb084acc58836590eb11d3c2600c0ff695d3dc" + integrity sha512-smtvSL/oLICPuenxy73OmxKGh42VVfn2o2eutReH1yjij0LmxADBpGcAJbp4N+yJjPapPN7jAX9p7Ue0JMQ/Ag== + dependencies: + "@types/koa" "*" + +"@types/koa-router@^7.4.6": + version "7.4.6" + resolved "https://registry.yarnpkg.com/@types/koa-router/-/koa-router-7.4.6.tgz#8489faddecd744f8272ec12d20337d7509b05fe4" + integrity sha512-elqZpjdH8bsi4HNSKwjY4nRVi65dd7kJoVEYXHMyI84R3wStzGnFSpYbilzqcUzPeZHiEhL0chfM593NeojqzQ== + dependencies: + "@types/koa" "*" + +"@types/koa@*", "@types/koa@^2.13.10": + version "2.13.10" + resolved "https://registry.yarnpkg.com/@types/koa/-/koa-2.13.10.tgz#2c2a1cdf1252d654b05f444194328a3d23a880c4" + integrity sha512-weKc5IBeORLDGwD1FMgPjaZIg0/mtP7KxXAXEzPRCN78k274D9U2acmccDNPL1MwyV40Jj+hQQ5N2eaV6O0z8g== + dependencies: + "@types/accepts" "*" + "@types/content-disposition" "*" + "@types/cookies" "*" + "@types/http-assert" "*" + "@types/http-errors" "*" + "@types/keygrip" "*" + "@types/koa-compose" "*" + "@types/node" "*" + "@types/lodash.clonedeep@^4.5.6": version "4.5.6" resolved "https://registry.yarnpkg.com/@types/lodash.clonedeep/-/lodash.clonedeep-4.5.6.tgz#3b6c40a0affe0799a2ce823b440a6cf33571d32b" @@ -572,6 +672,16 @@ resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.172.tgz#aad774c28e7bfd7a67de25408e03ee5a8c3d028a" integrity sha512-/BHF5HAx3em7/KkzVKm3LrsD6HZAXuXO1AJZQ3cRRBZj4oHZDviWPYu0aEplAqDFNHZPW6d3G7KN+ONcCCC7pw== +"@types/mime@*": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.3.tgz#886674659ce55fe7c6c06ec5ca7c0eb276a08f91" + integrity sha512-i8MBln35l856k5iOhKk2XJ4SeAWg75mLIpZB4v6imOagKL6twsukBZGDMNhdOVk7yRFTMPpfILocMos59Q1otQ== + +"@types/mime@^1": + version "1.3.4" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.4.tgz#a4ed836e069491414bab92c31fdea9e557aca0d9" + integrity sha512-1Gjee59G25MrQGk8bsNvC6fxNiRgUlGn2wlhGf95a59DrprnnHk80FIMMFG9XHMdrfsuA119ht06QPDXA1Z7tw== + "@types/minimist@^1.2.0", "@types/minimist@^1.2.2": version "1.2.2" resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.2.tgz#ee771e2ba4b3dc5b372935d549fd9617bf345b8c" @@ -592,6 +702,16 @@ resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" integrity sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw== +"@types/qs@*": + version "6.9.9" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.9.tgz#66f7b26288f6799d279edf13da7ccd40d2fa9197" + integrity sha512-wYLxw35euwqGvTDx6zfY1vokBFnsK0HNrzc6xNHchxfO2hpuRg74GbkEW7e3sSmPvj0TjCDT1VCa6OtHXnubsg== + +"@types/range-parser@*": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.6.tgz#7cb33992049fd7340d5b10c0098e104184dfcd2a" + integrity sha512-+0autS93xyXizIYiyL02FCY8N+KkKPhILhcUSA276HxzreZ16kl+cmwvV2qAM/PuCCwPXzOXOWhiPcw20uSFcA== + "@types/responselike@*", "@types/responselike@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29" @@ -599,6 +719,23 @@ dependencies: "@types/node" "*" +"@types/send@*": + version "0.17.3" + resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.3.tgz#81b2ea5a3a18aad357405af2d643ccbe5a09020b" + integrity sha512-/7fKxvKUoETxjFUsuFlPB9YndePpxxRAOfGC/yJdc9kTjTeP5kRCTzfnE8kPUKCeyiyIZu0YQ76s50hCedI1ug== + dependencies: + "@types/mime" "^1" + "@types/node" "*" + +"@types/serve-static@*": + version "1.15.4" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.4.tgz#44b5895a68ca637f06c229119e1c774ca88f81b2" + integrity sha512-aqqNfs1XTF0HDrFdlY//+SGUxmdSUbjeRXb5iaZc3x0/vMbYmdw9qvOgHWOyyLFxSSRnUuP5+724zBgfw8/WAw== + dependencies: + "@types/http-errors" "*" + "@types/mime" "*" + "@types/node" "*" + "@types/stack-utils@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e" @@ -653,6 +790,14 @@ abab@^2.0.0: resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== +accepts@^1.3.5: + version "1.3.8" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" + integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== + dependencies: + mime-types "~2.1.34" + negotiator "0.6.3" + acorn-globals@^4.1.0: version "4.3.4" resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.3.4.tgz#9fa1926addc11c97308c4e66d7add0d40c3272e7" @@ -1051,6 +1196,14 @@ cache-base@^1.0.1: union-value "^1.0.0" unset-value "^1.0.0" +cache-content-type@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-content-type/-/cache-content-type-1.0.1.tgz#035cde2b08ee2129f4a8315ea8f00a00dba1453c" + integrity sha512-IKufZ1o4Ut42YUrZSo8+qnMTrFuKkvyoLXUywKz9GJ5BrhOFGhLdkx9sG4KAnVvbY6kEcSFjLQul+DVmBm2bgA== + dependencies: + mime-types "^2.1.18" + ylru "^1.2.0" + cacheable-lookup@^5.0.3: version "5.0.4" resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005" @@ -1317,6 +1470,18 @@ concat-stream@^2.0.0: readable-stream "^3.0.2" typedarray "^0.0.6" +content-disposition@~0.5.2: + version "0.5.4" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" + integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== + dependencies: + safe-buffer "5.2.1" + +content-type@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" + integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== + conventional-changelog-angular@^5.0.12: version "5.0.12" resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-5.0.12.tgz#c979b8b921cbfe26402eb3da5bbfda02d865a2b9" @@ -1497,6 +1662,14 @@ convert-source-map@^1.4.0, convert-source-map@^1.7.0: dependencies: safe-buffer "~5.1.1" +cookies@~0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/cookies/-/cookies-0.8.0.tgz#1293ce4b391740a8406e3c9870e828c4b54f3f90" + integrity sha512-8aPsApQfebXnuI+537McwYsDtjVxGm8gTIzQI3FDW6t5t/DAhERxtnbEPN/8RX+uZthoz4eCOgloXaE5cYyNow== + dependencies: + depd "~2.0.0" + keygrip "~1.1.0" + copy-descriptor@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" @@ -1612,6 +1785,13 @@ debug@^4.1.0, debug@^4.1.1: dependencies: ms "2.1.2" +debug@^4.3.2, debug@^4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + decamelize-keys@^1.0.0, decamelize-keys@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9" @@ -1642,6 +1822,11 @@ decompress-response@^6.0.0: dependencies: mimic-response "^3.1.0" +deep-equal@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" + integrity sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw== + deep-is@~0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" @@ -1686,6 +1871,26 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== + +depd@2.0.0, depd@^2.0.0, depd@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== + +destroy@^1.0.4: + version "1.2.0" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" + integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== + detect-indent@^6.0.0: version "6.1.0" resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6" @@ -1753,6 +1958,11 @@ ecc-jsbn@~0.1.1: jsbn "~0.1.0" safer-buffer "^2.1.0" +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== + ejson@^2.2.3: version "2.2.3" resolved "https://registry.yarnpkg.com/ejson/-/ejson-2.2.3.tgz#2b18c2d8f5d61a5cfc6e3eab72c3343909e7afd2" @@ -1778,6 +1988,11 @@ emoji-regex@^8.0.0: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== +encodeurl@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== + end-of-stream@^1.1.0: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" @@ -1832,6 +2047,11 @@ escalade@^3.1.1: resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== +escape-html@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== + escape-string-regexp@5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz#4683126b500b61762f2dbebace1806e8be31b1c8" @@ -2159,6 +2379,11 @@ fragment-cache@^0.2.1: dependencies: map-cache "^0.2.2" +fresh@~0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== + fs-access@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/fs-access/-/fs-access-1.0.1.tgz#d6a87f262271cefebec30c553407fb995da8777a" @@ -2539,11 +2764,41 @@ htmlparser@1.7.x: resolved "https://registry.yarnpkg.com/htmlparser/-/htmlparser-1.7.7.tgz#19e7b3997ff6fbac99ae5a7d2766489efe7e2d0e" integrity sha1-GeezmX/2+6yZrlp9J2ZInv5+LQ4= +http-assert@^1.3.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/http-assert/-/http-assert-1.5.0.tgz#c389ccd87ac16ed2dfa6246fd73b926aa00e6b8f" + integrity sha512-uPpH7OKX4H25hBmU6G1jWNaqJGpTXxey+YOUizJUAgu0AjLUeC8D73hTrhvDS5D+GJN1DN1+hhc/eF/wpxtp0w== + dependencies: + deep-equal "~1.0.1" + http-errors "~1.8.0" + http-cache-semantics@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== +http-errors@^1.6.3, http-errors@~1.8.0: + version "1.8.1" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.1.tgz#7c3f28577cbc8a207388455dbd62295ed07bd68c" + integrity sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.1" + +http-errors@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" + integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== + dependencies: + depd "2.0.0" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses "2.0.1" + toidentifier "1.0.1" + http-parser-js@>=0.5.1: version "0.5.3" resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.3.tgz#01d2709c79d41698bb01d4decc5e9da4e4a033d9" @@ -2663,7 +2918,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: +inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -2830,6 +3085,13 @@ is-generator-fn@^2.0.0: resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== +is-generator-function@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" + integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== + dependencies: + has-tostringtag "^1.0.0" + is-negative-zero@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" @@ -3534,6 +3796,13 @@ jsprim@^1.2.2: json-schema "0.4.0" verror "1.10.0" +keygrip@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/keygrip/-/keygrip-1.1.0.tgz#871b1681d5e159c62a445b0c74b615e0917e7226" + integrity sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ== + dependencies: + tsscmp "1.0.6" + keyv@^4.0.0: version "4.3.2" resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.3.2.tgz#e839df676a0c7ee594c8835e7c1c83742558e5c2" @@ -3571,6 +3840,59 @@ kleur@^3.0.3: resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== +koa-compose@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/koa-compose/-/koa-compose-4.1.0.tgz#507306b9371901db41121c812e923d0d67d3e877" + integrity sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw== + +koa-convert@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/koa-convert/-/koa-convert-2.0.0.tgz#86a0c44d81d40551bae22fee6709904573eea4f5" + integrity sha512-asOvN6bFlSnxewce2e/DK3p4tltyfC4VM7ZwuTuepI7dEQVcvpyFuBcEARu1+Hxg8DIwytce2n7jrZtRlPrARA== + dependencies: + co "^4.6.0" + koa-compose "^4.1.0" + +koa-router@^12.0.1: + version "12.0.1" + resolved "https://registry.yarnpkg.com/koa-router/-/koa-router-12.0.1.tgz#a3c1c331032d442da786f0631d23e74d51b6882e" + integrity sha512-gaDdj3GtzoLoeosacd50kBBTnnh3B9AYxDThQUo4sfUyXdOhY6ku1qyZKW88tQCRgc3Sw6ChXYXWZwwgjOxE0w== + dependencies: + debug "^4.3.4" + http-errors "^2.0.0" + koa-compose "^4.1.0" + methods "^1.1.2" + path-to-regexp "^6.2.1" + +koa@^2.14.2: + version "2.14.2" + resolved "https://registry.yarnpkg.com/koa/-/koa-2.14.2.tgz#a57f925c03931c2b4d94b19d2ebf76d3244863fc" + integrity sha512-VFI2bpJaodz6P7x2uyLiX6RLYpZmOJqNmoCst/Yyd7hQlszyPwG/I9CQJ63nOtKSxpt5M7NH67V6nJL2BwCl7g== + dependencies: + accepts "^1.3.5" + cache-content-type "^1.0.0" + content-disposition "~0.5.2" + content-type "^1.0.4" + cookies "~0.8.0" + debug "^4.3.2" + delegates "^1.0.0" + depd "^2.0.0" + destroy "^1.0.4" + encodeurl "^1.0.2" + escape-html "^1.0.3" + fresh "~0.5.2" + http-assert "^1.3.0" + http-errors "^1.6.3" + is-generator-function "^1.0.7" + koa-compose "^4.1.0" + koa-convert "^2.0.0" + on-finished "^2.3.0" + only "~0.0.2" + parseurl "^1.3.2" + statuses "^1.5.0" + type-is "^1.6.16" + vary "^1.1.2" + left-pad@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/left-pad/-/left-pad-1.3.0.tgz#5b8a3a7765dfe001261dde915589e782f8c94d1e" @@ -3788,6 +4110,11 @@ marked@^1.1.1: resolved "https://registry.yarnpkg.com/marked/-/marked-1.2.9.tgz#53786f8b05d4c01a2a5a76b7d1ec9943d29d72dc" integrity sha512-H8lIX2SvyitGX+TRdtS06m1jHMijKN/XjfH6Ooii9fvxMlh8QdqBfBDkGUpMWH2kQNrtixjzYUa3SH8ROTgRRw== +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== + meow@^10.1.2: version "10.1.5" resolved "https://registry.yarnpkg.com/meow/-/meow-10.1.5.tgz#be52a1d87b5f5698602b0f32875ee5940904aa7f" @@ -3877,6 +4204,11 @@ merge-stream@^2.0.0: resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== +methods@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== + micromatch@^3.1.10, micromatch@^3.1.4: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" @@ -3909,6 +4241,11 @@ mime-db@1.51.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.51.0.tgz#d9ff62451859b18342d960850dc3cfb77e63fb0c" integrity sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g== +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + mime-types@^2.1.12, mime-types@~2.1.19: version "2.1.34" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.34.tgz#5a712f9ec1503511a945803640fafe09d3793c24" @@ -3916,6 +4253,13 @@ mime-types@^2.1.12, mime-types@~2.1.19: dependencies: mime-db "1.51.0" +mime-types@^2.1.18, mime-types@~2.1.24, mime-types@~2.1.34: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" @@ -4063,6 +4407,11 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + neo-async@^2.6.0: version "2.6.2" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" @@ -4270,6 +4619,13 @@ object.pick@^1.3.0: dependencies: isobject "^3.0.1" +on-finished@^2.3.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" + integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== + dependencies: + ee-first "1.1.1" + once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -4284,6 +4640,11 @@ onetime@^5.1.0: dependencies: mimic-fn "^2.1.0" +only@~0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/only/-/only-0.0.2.tgz#2afde84d03e50b9a8edc444e30610a70295edfb4" + integrity sha512-Fvw+Jemq5fjjyWz6CpKx6w9s7xxqo3+JCyM0WXWeCSOboZ8ABkyvP8ID4CZuChA/wxSx+XSJmdOm8rGVyJ1hdQ== + open-cli@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/open-cli/-/open-cli-6.0.1.tgz#adcee24967dc12c65d8cb8bf994e7dc40aed7a8e" @@ -4436,6 +4797,11 @@ parse5@4.0.0: resolved "https://registry.yarnpkg.com/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608" integrity sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA== +parseurl@^1.3.2: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + pascalcase@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" @@ -4476,6 +4842,11 @@ path-parse@^1.0.6, path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== +path-to-regexp@^6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-6.2.1.tgz#d54934d6798eb9e5ef14e7af7962c945906918e5" + integrity sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw== + path-type@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" @@ -4965,7 +5336,7 @@ rsvp@^4.8.4: resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== -safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: +safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -5037,6 +5408,11 @@ set-value@^2.0.0, set-value@^2.0.1, set-value@^4.0.1: is-plain-object "^2.0.4" is-primitive "^3.0.1" +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" @@ -5362,6 +5738,16 @@ static-extend@^0.1.1: define-property "^0.2.5" object-copy "^0.1.0" +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== + +"statuses@>= 1.5.0 < 2", statuses@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== + stealthy-require@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" @@ -5679,6 +6065,11 @@ to-regex@^3.0.1, to-regex@^3.0.2: regex-not "^1.0.2" safe-regex "^1.1.0" +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + token-types@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/token-types/-/token-types-2.1.1.tgz#bd585d64902aaf720b8979d257b4b850b4d45c45" @@ -5820,6 +6211,11 @@ tslint@^5.11.0: tslib "^1.8.0" tsutils "^2.29.0" +tsscmp@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/tsscmp/-/tsscmp-1.0.6.tgz#85b99583ac3589ec4bfef825b5000aa911d605eb" + integrity sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA== + tsutils@^2.29.0: version "2.29.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.29.0.tgz#32b488501467acbedd4b85498673a0812aca0b99" @@ -5876,6 +6272,14 @@ type-fest@^2.19.0: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.19.0.tgz#88068015bb33036a598b952e55e9311a60fd3a9b" integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA== +type-is@^1.6.16: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + typedarray-to-buffer@^3.1.5: version "3.1.5" resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" @@ -6059,6 +6463,11 @@ validate-npm-package-name@^3.0.0: dependencies: builtins "^1.0.3" +vary@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== + verror@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" @@ -6392,6 +6801,11 @@ yargs@~1.2.6: dependencies: minimist "^0.1.0" +ylru@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/ylru/-/ylru-1.3.2.tgz#0de48017473275a4cbdfc83a1eaf67c01af8a785" + integrity sha512-RXRJzMiK6U2ye0BlGGZnmpwJDPgakn6aNQ0A7gHRbD4I0uvK4TW6UqkK1V0pp9jskjJBAXd3dRrbzWkqJ+6cxA== + yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" From d0a17c9bd1434c8133e54867488129de6e0054ac Mon Sep 17 00:00:00 2001 From: Rasmus Date: Tue, 31 Oct 2023 08:26:45 +0100 Subject: [PATCH 04/14] fix: changes port to a constant --- src/connector.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/connector.ts b/src/connector.ts index f68d910..6db7ed6 100755 --- a/src/connector.ts +++ b/src/connector.ts @@ -144,18 +144,20 @@ export class Connector { } private setupReloadDataKoaEndpoint(): void { + const KOA_PORT: number = 3007 + const RUNDOWN_EXTERNAL_ID_SUFFIX: string = '_1' + this.koaRouter.get('/reloadData/:rundownName', async (ctx, next): Promise => { - const RUNDOWN_EXTERNAL_ID_SUFFIX = '_1' if (!this.iNewsFTPHandler.iNewsWatcher) { return } - this.iNewsFTPHandler.iNewsWatcher?.ResyncRundown(ctx.params.rundownName + RUNDOWN_EXTERNAL_ID_SUFFIX) + await this.iNewsFTPHandler.iNewsWatcher.ResyncRundown(ctx.params.rundownName + RUNDOWN_EXTERNAL_ID_SUFFIX) await next() }) this.koaApp.use(this.koaRouter.routes()).use(this.koaRouter.allowedMethods()) - this.koaApp.listen(3007, () => {}) + this.koaApp.listen(KOA_PORT, () => {}) } } From 94f95d9f46fd3e7948b066e150f8fb97ca60cd33 Mon Sep 17 00:00:00 2001 From: Rasmus Date: Tue, 31 Oct 2023 09:53:14 +0100 Subject: [PATCH 05/14] refactor: adds more error handling and changes the GET to a POST --- src/connector.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/connector.ts b/src/connector.ts index 6db7ed6..a85d191 100755 --- a/src/connector.ts +++ b/src/connector.ts @@ -147,13 +147,19 @@ export class Connector { const KOA_PORT: number = 3007 const RUNDOWN_EXTERNAL_ID_SUFFIX: string = '_1' - this.koaRouter.get('/reloadData/:rundownName', async (ctx, next): Promise => { + this.koaRouter.post('/reloadData/:rundownName', async (context, next): Promise => { if (!this.iNewsFTPHandler.iNewsWatcher) { + context.status = 503 + context.response.body = 'Error: iNewsWatcher is undefined' return } - await this.iNewsFTPHandler.iNewsWatcher.ResyncRundown(ctx.params.rundownName + RUNDOWN_EXTERNAL_ID_SUFFIX) - - await next() + try { + await this.iNewsFTPHandler.iNewsWatcher.ResyncRundown(context.params.rundownName + RUNDOWN_EXTERNAL_ID_SUFFIX) + await next() + } catch (error) { + context.status = 500 + context.response.body = 'Error: ' + error + } }) this.koaApp.use(this.koaRouter.routes()).use(this.koaRouter.allowedMethods()) From 0c4095b05a34286952e2d602efaaf97c118301c9 Mon Sep 17 00:00:00 2001 From: Rasmus Date: Tue, 31 Oct 2023 09:55:44 +0100 Subject: [PATCH 06/14] fix: adds message to succesful call --- src/connector.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/connector.ts b/src/connector.ts index a85d191..c80e05d 100755 --- a/src/connector.ts +++ b/src/connector.ts @@ -155,6 +155,7 @@ export class Connector { } try { await this.iNewsFTPHandler.iNewsWatcher.ResyncRundown(context.params.rundownName + RUNDOWN_EXTERNAL_ID_SUFFIX) + context.body = 'Rundown reloaded!' await next() } catch (error) { context.status = 500 From 7e1d1da7ffac8efddf7971c44a5a763d7dc531a8 Mon Sep 17 00:00:00 2001 From: Rasmus Date: Tue, 31 Oct 2023 13:38:23 +0100 Subject: [PATCH 07/14] fix: remove misleading message since we cannot handle whether resync is succesfull in case of wrong rundown name --- src/connector.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/connector.ts b/src/connector.ts index c80e05d..a85d191 100755 --- a/src/connector.ts +++ b/src/connector.ts @@ -155,7 +155,6 @@ export class Connector { } try { await this.iNewsFTPHandler.iNewsWatcher.ResyncRundown(context.params.rundownName + RUNDOWN_EXTERNAL_ID_SUFFIX) - context.body = 'Rundown reloaded!' await next() } catch (error) { context.status = 500 From eb17bc31236a0672e36878e77f32950f49f8a101 Mon Sep 17 00:00:00 2001 From: Rasmus Date: Tue, 31 Oct 2023 13:42:24 +0100 Subject: [PATCH 08/14] refactor: moves rundown name to a constant and rearranges some code --- src/connector.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/connector.ts b/src/connector.ts index a85d191..343bcca 100755 --- a/src/connector.ts +++ b/src/connector.ts @@ -148,22 +148,24 @@ export class Connector { const RUNDOWN_EXTERNAL_ID_SUFFIX: string = '_1' this.koaRouter.post('/reloadData/:rundownName', async (context, next): Promise => { + const rundownName: string = context.params.rundownName if (!this.iNewsFTPHandler.iNewsWatcher) { context.status = 503 context.response.body = 'Error: iNewsWatcher is undefined' return } + try { - await this.iNewsFTPHandler.iNewsWatcher.ResyncRundown(context.params.rundownName + RUNDOWN_EXTERNAL_ID_SUFFIX) + context.response.body = `Attempting to reload rundown with name ${rundownName}` + await this.iNewsFTPHandler.iNewsWatcher.ResyncRundown( rundownName + RUNDOWN_EXTERNAL_ID_SUFFIX) await next() } catch (error) { context.status = 500 - context.response.body = 'Error: ' + error + context.response.body = `Error: ${error}` } }) this.koaApp.use(this.koaRouter.routes()).use(this.koaRouter.allowedMethods()) - this.koaApp.listen(KOA_PORT, () => {}) } } From 0b715e6950c7c4d39a0e0b212b453ecaf50a4e34 Mon Sep 17 00:00:00 2001 From: Rasmus Date: Wed, 1 Nov 2023 08:15:04 +0100 Subject: [PATCH 09/14] lint: lint fix --- src/connector.ts | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/src/connector.ts b/src/connector.ts index 343bcca..8cd4e47 100755 --- a/src/connector.ts +++ b/src/connector.ts @@ -6,7 +6,7 @@ import { Observer } from '@sofie-automation/server-core-integration' import { ensureLogLevel, setLogLevel } from './logger' import { ILogger as Logger } from '@tv2media/logger' import { unprotectString } from '@sofie-automation/shared-lib/dist/lib/protectedString' -import * as Koa from 'koa' +import * as Koa from 'koa' import * as KoaRouter from 'koa-router' export interface Config { @@ -36,7 +36,6 @@ export class Connector { private koaApp: Koa private koaRouter: KoaRouter - constructor(logger: Logger, config: Config, debug: boolean) { this._logger = logger.tag(this.constructor.name) this._config = config @@ -147,23 +146,26 @@ export class Connector { const KOA_PORT: number = 3007 const RUNDOWN_EXTERNAL_ID_SUFFIX: string = '_1' - this.koaRouter.post('/reloadData/:rundownName', async (context, next): Promise => { - const rundownName: string = context.params.rundownName - if (!this.iNewsFTPHandler.iNewsWatcher) { - context.status = 503 - context.response.body = 'Error: iNewsWatcher is undefined' - return - } + this.koaRouter.post( + '/reloadData/:rundownName', + async (context, next): Promise => { + const rundownName: string = context.params.rundownName + if (!this.iNewsFTPHandler.iNewsWatcher) { + context.status = 503 + context.response.body = 'Error: iNewsWatcher is undefined' + return + } - try { - context.response.body = `Attempting to reload rundown with name ${rundownName}` - await this.iNewsFTPHandler.iNewsWatcher.ResyncRundown( rundownName + RUNDOWN_EXTERNAL_ID_SUFFIX) - await next() - } catch (error) { - context.status = 500 - context.response.body = `Error: ${error}` + try { + context.response.body = `Attempting to reload rundown with name ${rundownName}` + await this.iNewsFTPHandler.iNewsWatcher.ResyncRundown(rundownName + RUNDOWN_EXTERNAL_ID_SUFFIX) + await next() + } catch (error) { + context.status = 500 + context.response.body = `Error: ${error}` + } } - }) + ) this.koaApp.use(this.koaRouter.routes()).use(this.koaRouter.allowedMethods()) this.koaApp.listen(KOA_PORT, () => {}) From 052dda7433a10b69613b391d3d6d6d5275172680 Mon Sep 17 00:00:00 2001 From: Rasmus Date: Wed, 1 Nov 2023 08:38:45 +0100 Subject: [PATCH 10/14] refactor: changes url to follow REST --- src/connector.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/connector.ts b/src/connector.ts index 8cd4e47..47fff12 100755 --- a/src/connector.ts +++ b/src/connector.ts @@ -147,7 +147,7 @@ export class Connector { const RUNDOWN_EXTERNAL_ID_SUFFIX: string = '_1' this.koaRouter.post( - '/reloadData/:rundownName', + '/reloadData/rundowns/:rundownName', async (context, next): Promise => { const rundownName: string = context.params.rundownName if (!this.iNewsFTPHandler.iNewsWatcher) { From 07159a020496ca2373fc2ada2254cb01e4f12540 Mon Sep 17 00:00:00 2001 From: Rasmus Date: Thu, 2 Nov 2023 10:16:32 +0100 Subject: [PATCH 11/14] refactor: Now correctly handles errors and has typed context --- src/classes/RundownWatcher.ts | 3 +-- src/connector.ts | 16 ++++++++++++---- src/coreHandler.ts | 1 + 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/classes/RundownWatcher.ts b/src/classes/RundownWatcher.ts index 40f8896..adec26f 100755 --- a/src/classes/RundownWatcher.ts +++ b/src/classes/RundownWatcher.ts @@ -282,9 +282,8 @@ export class RundownWatcher extends EventEmitter { const rundown = this.rundowns.get(rundownExternalId) if (!playlist || !rundown) { - this.logger.error(`Rundown ${rundownExternalId} does not exist in playlist ${playlistExternalId}`) release() - return + throw new Error(`Rundown ${rundownExternalId} does not exist in playlist ${playlistExternalId}`) } // Delete cached data for this rundown diff --git a/src/connector.ts b/src/connector.ts index 47fff12..c705c1c 100755 --- a/src/connector.ts +++ b/src/connector.ts @@ -24,6 +24,15 @@ export interface DeviceConfig { deviceId: string deviceToken: string } + +interface KoaContext { + params: Record + status: number + response: { + body: string + } +} + export class Connector { private iNewsFTPHandler: InewsFTPHandler private _observers: Array = [] @@ -147,8 +156,8 @@ export class Connector { const RUNDOWN_EXTERNAL_ID_SUFFIX: string = '_1' this.koaRouter.post( - '/reloadData/rundowns/:rundownName', - async (context, next): Promise => { + '/rundowns/:rundownName/reingest-data', + async (context: KoaContext ): Promise => { const rundownName: string = context.params.rundownName if (!this.iNewsFTPHandler.iNewsWatcher) { context.status = 503 @@ -157,9 +166,8 @@ export class Connector { } try { - context.response.body = `Attempting to reload rundown with name ${rundownName}` await this.iNewsFTPHandler.iNewsWatcher.ResyncRundown(rundownName + RUNDOWN_EXTERNAL_ID_SUFFIX) - await next() + context.response.body = `Reingested data for rundown with name ${rundownName}` } catch (error) { context.status = 500 context.response.body = `Error: ${error}` diff --git a/src/coreHandler.ts b/src/coreHandler.ts index f743177..4e5219c 100755 --- a/src/coreHandler.ts +++ b/src/coreHandler.ts @@ -401,6 +401,7 @@ export class CoreHandler { this.logger.info(`Reloading rundown: ${rundownId}`) if (this.iNewsHandler?.iNewsWatcher) { await this.iNewsHandler.iNewsWatcher.ResyncRundown(rundownId) + .catch(error => this.logger.data(error).error(`Failed reloading rundown with rundown id '${rundownId}'.`)) } return null } From 6d7ed674286ca4191ddbd0b0edb503aa6af8b335 Mon Sep 17 00:00:00 2001 From: Rasmus Date: Thu, 2 Nov 2023 14:35:56 +0100 Subject: [PATCH 12/14] refactor: renames the url to be just reingest rather than reingest-data --- src/connector.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/connector.ts b/src/connector.ts index c705c1c..c577899 100755 --- a/src/connector.ts +++ b/src/connector.ts @@ -156,7 +156,7 @@ export class Connector { const RUNDOWN_EXTERNAL_ID_SUFFIX: string = '_1' this.koaRouter.post( - '/rundowns/:rundownName/reingest-data', + '/rundowns/:rundownName/reingest', async (context: KoaContext ): Promise => { const rundownName: string = context.params.rundownName if (!this.iNewsFTPHandler.iNewsWatcher) { From a8426d80e46da1b361902eadff176ddf5f9f5caf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Steenhoff=20Madsen?= Date: Wed, 15 Nov 2023 13:57:47 +0100 Subject: [PATCH 13/14] refactor: removes possibility for arguments being optional as the are always supplied. fix: sets status in core when failing to download iNews rundown. --- src/classes/RundownManager.ts | 21 +++++++++++---------- src/classes/RundownWatcher.ts | 2 +- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/classes/RundownManager.ts b/src/classes/RundownManager.ts index 83cdaf0..b15c3df 100755 --- a/src/classes/RundownManager.ts +++ b/src/classes/RundownManager.ts @@ -5,7 +5,9 @@ import { ReducedRundown, ReducedSegment, UnrankedSegment } from './RundownWatche import { literal, parseModifiedDateFromInewsStoryWithFallbackToNow, ReflectPromise } from '../helpers' import { VERSION } from '../version' import { SegmentId } from '../helpers/id' -import { ILogger as Logger } from '@tv2media/logger' +import { ILogger } from '@tv2media/logger' +import { CoreHandler } from "../coreHandler"; +import { StatusCode } from "@sofie-automation/shared-lib/dist/lib/status"; function isFile(f: INewsDirItem): f is INewsFile { return f.filetype === 'file' @@ -15,11 +17,9 @@ export class RundownManager { private _listStories!: (queueName: string) => Promise> private _getStory!: (queueName: string, story: string) => Promise - constructor(private _logger?: Logger, private inewsConnection?: INewsClient) { - if (this.inewsConnection) { - this._listStories = promisify(this.inewsConnection.list).bind(this.inewsConnection) - this._getStory = promisify(this.inewsConnection.story).bind(this.inewsConnection) - } + constructor(private _logger: ILogger, private inewsConnection: INewsClient, private coreHandler: CoreHandler) { + this._listStories = promisify(this.inewsConnection.list).bind(this.inewsConnection) + this._getStory = promisify(this.inewsConnection.story).bind(this.inewsConnection) } /** @@ -57,7 +57,8 @@ export class RundownManager { } }) } catch (error) { - this._logger?.data(error).error('Error downloading iNews rundown:') + this._logger.data(error).error('Error downloading iNews rundown:') + await this.coreHandler.setStatus(StatusCode.FATAL, ['Error downloading iNews rundown', (error as Error).message]) } return rundown } @@ -110,15 +111,15 @@ export class RundownManager { locator: (storyFile as INewsFile).locator, } } catch (err) { - this._logger?.error(`Error downloading iNews story: ${err}`) + this._logger.error(`Error downloading iNews story: ${err}`) return undefined } - this._logger?.debug('Downloaded : ' + queueName + ' : ' + (storyFile as INewsFile).identifier) + this._logger.debug('Downloaded : ' + queueName + ' : ' + (storyFile as INewsFile).identifier) /* Add fileId and update modifyDate to ftp reference in storyFile */ story.fields.modifyDate = `${storyFile.modified ? storyFile.modified.getTime() / 1000 : 0}` - this._logger?.debug(`Queue: ${queueName} Story: ${isFile(storyFile) ? storyFile.storyName : storyFile.file}`) + this._logger.debug(`Queue: ${queueName} Story: ${isFile(storyFile) ? storyFile.storyName : storyFile.file}`) return story } diff --git a/src/classes/RundownWatcher.ts b/src/classes/RundownWatcher.ts index adec26f..c772af6 100755 --- a/src/classes/RundownWatcher.ts +++ b/src/classes/RundownWatcher.ts @@ -205,7 +205,7 @@ export class RundownWatcher extends EventEmitter { super() this._logger = this.logger.tag(this.constructor.name) - this.rundownManager = new RundownManager(this._logger, this.iNewsConnection) + this.rundownManager = new RundownManager(this._logger, this.iNewsConnection, this.coreHandler) if (!delayStart) { this.startWatcher() From 0b964548ce48b468f8b92d7f5f51ea0a368d6ebb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Steenhoff=20Madsen?= Date: Wed, 15 Nov 2023 14:11:19 +0100 Subject: [PATCH 14/14] lint: applies linting --- src/classes/RundownManager.ts | 4 ++-- src/connector.ts | 2 +- src/coreHandler.ts | 5 +++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/classes/RundownManager.ts b/src/classes/RundownManager.ts index b15c3df..fa5e0ea 100755 --- a/src/classes/RundownManager.ts +++ b/src/classes/RundownManager.ts @@ -6,8 +6,8 @@ import { literal, parseModifiedDateFromInewsStoryWithFallbackToNow, ReflectPromi import { VERSION } from '../version' import { SegmentId } from '../helpers/id' import { ILogger } from '@tv2media/logger' -import { CoreHandler } from "../coreHandler"; -import { StatusCode } from "@sofie-automation/shared-lib/dist/lib/status"; +import { CoreHandler } from '../coreHandler' +import { StatusCode } from '@sofie-automation/shared-lib/dist/lib/status' function isFile(f: INewsDirItem): f is INewsFile { return f.filetype === 'file' diff --git a/src/connector.ts b/src/connector.ts index c577899..1f91f0a 100755 --- a/src/connector.ts +++ b/src/connector.ts @@ -157,7 +157,7 @@ export class Connector { this.koaRouter.post( '/rundowns/:rundownName/reingest', - async (context: KoaContext ): Promise => { + async (context: KoaContext): Promise => { const rundownName: string = context.params.rundownName if (!this.iNewsFTPHandler.iNewsWatcher) { context.status = 503 diff --git a/src/coreHandler.ts b/src/coreHandler.ts index 4e5219c..5770290 100755 --- a/src/coreHandler.ts +++ b/src/coreHandler.ts @@ -400,8 +400,9 @@ export class CoreHandler { async triggerReloadRundown(rundownId: string): Promise { this.logger.info(`Reloading rundown: ${rundownId}`) if (this.iNewsHandler?.iNewsWatcher) { - await this.iNewsHandler.iNewsWatcher.ResyncRundown(rundownId) - .catch(error => this.logger.data(error).error(`Failed reloading rundown with rundown id '${rundownId}'.`)) + await this.iNewsHandler.iNewsWatcher + .ResyncRundown(rundownId) + .catch((error) => this.logger.data(error).error(`Failed reloading rundown with rundown id '${rundownId}'.`)) } return null }