Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
e56576b
chore: refactor SourceLayerItem component
Nov 13, 2024
385e884
feat: retime piece user action
Nov 15, 2024
4347c6a
feat: edit mode for drag operations
Dec 18, 2024
5e0a795
fix: add dependency to useEffect for onSetEditMode
rjmunro Dec 18, 2025
e9f66e7
feat: Allow restricting dragging to current part
rjmunro Dec 19, 2025
006296c
Only preventDefault if we are dragging
rjmunro Jan 7, 2026
fa54001
Use useCallBack for efficiency
rjmunro Jan 12, 2026
4bbaa5d
Consolidate drag event cleanup and fix listener leak
rjmunro Jan 12, 2026
99b01d6
Fix calculation of pos relative to currentTarget
rjmunro Jan 12, 2026
9cd57af
feat: change structure of ExpectedPackage documents
Julusian Sep 23, 2025
172b3ec
chore: unit tests
Julusian Jan 6, 2026
45fc8f2
feat: rework ExpectedPackages generation/management to share document…
Julusian Apr 15, 2025
3fbd39c
feat: rework ExpectedPackages generation/management to add PieceInsta…
Julusian Jan 7, 2026
62e5e50
fix: hashObj not handling null values
Julusian Jan 12, 2026
03346be
feat: rework ExpectedPackages generation/management to share packages…
Julusian Jan 7, 2026
3cd0584
fix: Track and clear drag timeout to prevent interference between drags
rjmunro Jan 14, 2026
3c79398
fix: Add missing 'part' to useCallback dependency array
rjmunro Jan 14, 2026
f22f8a2
feat: Add XBox controller support including take button
rjmunro Jan 13, 2026
986e411
doc: Document Xbox controller support
rjmunro Jan 13, 2026
75de2b7
docs: Document joycon_invertJoystick and fix typo
rjmunro Jan 13, 2026
6eed89e
chore: Update controller code with SonarQube suggestions
rjmunro Jan 15, 2026
177faef
fix: Clean up gamepad event listeners in destroy()
rjmunro Jan 15, 2026
5e74d4f
fix: remove unimplemented return type of blueprint executeAction
Julusian Jan 19, 2026
a6b3805
chore: review comments
Julusian Jan 19, 2026
90aba0d
Merge pull request #1595 from bbc/upstream/expected-packages-shared-i…
Julusian Jan 19, 2026
83b07e3
Merge pull request #1544 from bbc/feat/ui-piece-retiming
rjmunro Jan 20, 2026
14605bf
Merge pull request #1598 from Sofie-Automation/rjmunro/xbox-controller
rjmunro Jan 20, 2026
462a27a
fix: remove over-eager debug logging filtering from connectionManager…
jstarpl Jan 20, 2026
794fc9e
fix(EAV-372): settings lost on studio update (#1455)
ianshade Jan 22, 2026
1686404
chore: reconfigure tsc to run at the root, not for each package indiv…
Julusian Jan 26, 2026
6271ffd
feat: allow adlib-actions to be marked as invalid (#1609)
Julusian Jan 26, 2026
0f3ea10
chore: fix sonar typescript
Julusian Jan 26, 2026
0734349
chore: fix ci yarn command order
Julusian Jan 26, 2026
e8e13b8
feat: partInstances invalid state
Julusian Jan 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
296 changes: 172 additions & 124 deletions .github/workflows/node.yaml

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions .github/workflows/publish-libs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,14 @@ jobs:
- name: Use Node.js
uses: actions/setup-node@v6
with:
node-version-file: '.node-version'
node-version-file: ".node-version"
- name: Prepare Environment
run: |
corepack enable

cd packages
yarn install
yarn lerna run --scope \*\*/${{ matrix.package-name }} --include-dependencies --stream build
yarn build:single ${{ matrix.package-name }}/tsconfig.build.json
env:
CI: true
- name: Run typecheck and linter
Expand Down Expand Up @@ -119,7 +119,7 @@ jobs:

cd packages
yarn install
yarn lerna run --scope \*\*/${{ matrix.package-name }} --include-dependencies --stream build
yarn build:single ${{ matrix.package-name }}/tsconfig.build.json
env:
CI: true
- name: Run tests
Expand All @@ -145,7 +145,7 @@ jobs:
- name: Use Node.js
uses: actions/setup-node@v6
with:
node-version-file: '.node-version'
node-version-file: ".node-version"
- name: Prepare Environment
run: |
corepack enable
Expand Down Expand Up @@ -222,7 +222,7 @@ jobs:
- name: Use Node.js
uses: actions/setup-node@v6
with:
node-version-file: '.node-version'
node-version-file: ".node-version"

- name: Download release artifact
uses: actions/download-artifact@v6
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/sonar.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ jobs:

yarn config set cacheFolder /home/runner/lint-core-cache
yarn

# setup zodern:types. No linters are setup, so this simply installs the packages
yarn meteor lint

yarn build:packages
env:
CI: true
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ meteor-settings.json
.github/workflows/forkSync.yml
.github/workflows/forkSynk.yml

**/*.tsbuildinfo

.pnp.*
.yarn/*
!.yarn/patches
Expand Down
8 changes: 3 additions & 5 deletions meteor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@
"unit": "jest",
"unitci": "jest --maxWorkers 2 --coverage",
"unitcov": "jest --coverage",
"test": "yarn check-types && yarn unit",
"test": "yarn unit",
"watch": "jest --watch",
"update-snapshots": "jest --updateSnapshot",
"ci:lint": "yarn check-types && yarn lint",
"ci:lint": "yarn lint",
"cov-open": "open-cli coverage/lcov-report/index.html",
"cov": "yarn unitcov && yarn cov-open",
"license-validate": "node ../scripts/checkLicenses.js --allowed=\"MIT,BSD,ISC,Apache,Unlicense,CC0,LGPL,CC BY 3.0,CC BY 4.0,MPL 2.0,Python 2.0\" --excludePackages=timecode,rxjs/ajax,rxjs/fetch,rxjs/internal-compatibility,nw-pre-gyp-module-test,rxjs/operators,rxjs/testing,rxjs/webSocket,undefined,i18next-conv,@fortawesome/fontawesome-common-types,argv,indexof,custom-license,private,public-domain-module,@sofie-automation/corelib,@sofie-automation/shared-lib,@sofie-automation/job-worker",
Expand All @@ -32,9 +32,7 @@
"prepareChangelog": "run release --prerelease --release-as patch",
"validate:all-dependencies": "run validate:prod-dependencies && run validate:dev-dependencies && run license-validate",
"validate:prod-dependencies": "yarn npm audit --environment production",
"validate:dev-dependencies": "yarn npm audit --environment development --severity moderate",
"check-types": "tsc --noEmit -p tsconfig.json",
"watch-types": "run check-types --watch"
"validate:dev-dependencies": "yarn npm audit --environment development --severity moderate"
},
"dependencies": {
"@babel/runtime": "^7.26.7",
Expand Down
25 changes: 9 additions & 16 deletions meteor/server/api/__tests__/cleanup.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ async function setDefaultDatatoDB(env: DefaultEnvironment, now: number) {
startSegmentId: segmentId,
timelineObjectsString: '' as any,
}
const pieceId = await Pieces.mutableCollection.insertAsync(piece)
await Pieces.mutableCollection.insertAsync(piece)

await AdLibActions.mutableCollection.insertAsync({
_id: getRandomId(),
Expand Down Expand Up @@ -265,22 +265,15 @@ async function setDefaultDatatoDB(env: DefaultEnvironment, now: number) {
})
const packageId = await ExpectedPackages.mutableCollection.insertAsync({
_id: getRandomId(),
blueprintPackageId: '',
// @ts-expect-error bucketId is not a part of all ExpectedPackageDBs
bucketId,
content: {} as any,
contentVersionHash: '',
created: 0,
fromPieceType: '' as any,
layers: [],
pieceId,
rundownId,
segmentId,
sideEffect: {} as any,
studioId,
sources: {} as any,
type: '' as any,
version: {} as any,
rundownId,
bucketId: null,
created: 0,
package: {} as any,
ingestSources: [],
playoutSources: {
pieceInstanceIds: [],
},
})
await ExpectedPackageWorkStatuses.insertAsync({
_id: getRandomId(),
Expand Down
28 changes: 0 additions & 28 deletions meteor/server/api/ingest/debug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { QueueStudioJob } from '../../worker/worker'
import { StudioJobs } from '@sofie-automation/corelib/dist/worker/studio'
import { RundownPlaylistId, SegmentId } from '@sofie-automation/corelib/dist/dataModel/Ids'
import { MeteorDebugMethods } from '../../methods'
import { DBRundown } from '@sofie-automation/corelib/dist/dataModel/Rundown'

MeteorDebugMethods({
/**
Expand Down Expand Up @@ -47,31 +46,4 @@ MeteorDebugMethods({
segmentExternalId: segment.externalId,
})
},
/**
* Regenerate all the expected packages for all rundowns in the system.
* Additionally it will recreate any expectedPlayoutItems.
* This shouldn't be necessary as ingest will do this for each rundown as part of its workflow
*/
debug_recreateExpectedPackages: async () => {
const rundowns = (await Rundowns.findFetchAsync(
{},
{
projection: {
_id: 1,
studioId: 1,
source: 1,
},
}
)) as Array<Pick<DBRundown, '_id' | 'studioId' | 'source'>>

await Promise.all(
rundowns
.filter((rundown) => rundown.source.type !== 'snapshot')
.map(async (rundown) =>
runIngestOperation(rundown.studioId, IngestJobs.ExpectedPackagesRegenerate, {
rundownId: rundown._id,
})
)
)
},
})
65 changes: 37 additions & 28 deletions meteor/server/api/ingest/packageInfo.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import {
ExpectedPackageDBFromBucketAdLib,
ExpectedPackageDBFromBucketAdLibAction,
ExpectedPackageDBFromStudioBaselineObjects,
ExpectedPackageDBType,
ExpectedPackageFromRundown,
ExpectedPackageFromRundownBaseline,
ExpectedPackageDB,
ExpectedPackageIngestSourceBucket,
} from '@sofie-automation/corelib/dist/dataModel/ExpectedPackages'
import { PackageInfoDB } from '@sofie-automation/corelib/dist/dataModel/PackageInfos'
import { ExpectedPackages, Rundowns } from '../../collections'
Expand All @@ -28,8 +25,10 @@ export async function onUpdatedPackageInfo(packageId: ExpectedPackageId, _doc: P
return
}

if (pkg.listenToPackageInfoUpdates) {
switch (pkg.fromPieceType) {
for (const source of pkg.ingestSources) {
if (!source.listenToPackageInfoUpdates) continue

switch (source.fromPieceType) {
case ExpectedPackageDBType.PIECE:
case ExpectedPackageDBType.ADLIB_PIECE:
case ExpectedPackageDBType.ADLIB_ACTION:
Expand All @@ -41,39 +40,44 @@ export async function onUpdatedPackageInfo(packageId: ExpectedPackageId, _doc: P
break
case ExpectedPackageDBType.BUCKET_ADLIB:
case ExpectedPackageDBType.BUCKET_ADLIB_ACTION:
onUpdatedPackageInfoForBucketItemDebounce(pkg)
onUpdatedPackageInfoForBucketItemDebounce(pkg, source)
break
case ExpectedPackageDBType.STUDIO_BASELINE_OBJECTS:
onUpdatedPackageInfoForStudioBaselineDebounce(pkg)
break
default:
assertNever(pkg)
assertNever(source)
break
}
}
}

const pendingRundownPackageUpdates = new Map<RundownId, Array<ExpectedPackageId>>()
function onUpdatedPackageInfoForRundownDebounce(pkg: ExpectedPackageFromRundown | ExpectedPackageFromRundownBaseline) {
const existingEntry = pendingRundownPackageUpdates.get(pkg.rundownId)
function onUpdatedPackageInfoForRundownDebounce(pkg: ExpectedPackageDB) {
if (!pkg.rundownId) {
logger.error(`Updating ExpectedPackage "${pkg._id}" not possibe: missing rundownId`)
return
}

const rundownId = pkg.rundownId

const existingEntry = pendingRundownPackageUpdates.get(rundownId)
if (existingEntry) {
// already queued, add to the batch
existingEntry.push(pkg._id)
} else {
pendingRundownPackageUpdates.set(pkg.rundownId, [pkg._id])
pendingRundownPackageUpdates.set(rundownId, [pkg._id])
}

// TODO: Scaling - this won't batch correctly if package manager directs calls to multiple instances
lazyIgnore(
`onUpdatedPackageInfoForRundown_${pkg.rundownId}`,
`onUpdatedPackageInfoForRundown_${rundownId}`,
() => {
const packageIds = pendingRundownPackageUpdates.get(pkg.rundownId)
const packageIds = pendingRundownPackageUpdates.get(rundownId)
if (packageIds) {
pendingRundownPackageUpdates.delete(pkg.rundownId)
onUpdatedPackageInfoForRundown(pkg.rundownId, packageIds).catch((e) => {
logger.error(
`Updating ExpectedPackages for Rundown "${pkg.rundownId}" failed: ${stringifyError(e)}`
)
pendingRundownPackageUpdates.delete(rundownId)
onUpdatedPackageInfoForRundown(rundownId, packageIds).catch((e) => {
logger.error(`Updating ExpectedPackages for Rundown "${rundownId}" failed: ${stringifyError(e)}`)
})
}
},
Expand Down Expand Up @@ -108,19 +112,24 @@ async function onUpdatedPackageInfoForRundown(
})
}

function onUpdatedPackageInfoForBucketItemDebounce(
pkg: ExpectedPackageDBFromBucketAdLib | ExpectedPackageDBFromBucketAdLibAction
) {
function onUpdatedPackageInfoForBucketItemDebounce(pkg: ExpectedPackageDB, source: ExpectedPackageIngestSourceBucket) {
if (!pkg.bucketId) {
logger.error(`Updating ExpectedPackage "${pkg._id}" for Bucket "${pkg.bucketId}" not possible`)
return
}

const bucketId = pkg.bucketId

lazyIgnore(
`onUpdatedPackageInfoForBucket_${pkg.studioId}_${pkg.bucketId}_${pkg.pieceExternalId}`,
`onUpdatedPackageInfoForBucket_${pkg.studioId}_${bucketId}_${source.pieceExternalId}`,
() => {
runIngestOperation(pkg.studioId, IngestJobs.BucketItemRegenerate, {
bucketId: pkg.bucketId,
externalId: pkg.pieceExternalId,
bucketId: bucketId,
externalId: source.pieceExternalId,
}).catch((err) => {
logger.error(
`Updating ExpectedPackages for Bucket "${pkg.bucketId}" Item "${
pkg.pieceExternalId
`Updating ExpectedPackages for Bucket "${bucketId}" Item "${
source.pieceExternalId
}" failed: ${stringifyError(err)}`
)
})
Expand All @@ -129,7 +138,7 @@ function onUpdatedPackageInfoForBucketItemDebounce(
)
}

function onUpdatedPackageInfoForStudioBaselineDebounce(pkg: ExpectedPackageDBFromStudioBaselineObjects) {
function onUpdatedPackageInfoForStudioBaselineDebounce(pkg: ExpectedPackageDB) {
lazyIgnore(
`onUpdatedPackageInfoForStudioBaseline_${pkg.studioId}`,
() => {
Expand Down
15 changes: 12 additions & 3 deletions meteor/server/api/integration/expectedPackages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
} from '../../collections'
import { logger } from '../../logging'
import _ from 'underscore'
import { ExpectedPackageDB } from '@sofie-automation/corelib/dist/dataModel/ExpectedPackages'

export namespace PackageManagerIntegration {
export async function updateExpectedPackageWorkStatuses(
Expand Down Expand Up @@ -99,9 +100,17 @@ export namespace PackageManagerIntegration {
const fromPackageIds = workStatus.fromPackages.map((p) => p.id)
if (fromPackageIds.length) {
ps.push(
ExpectedPackages.findOneAsync({
_id: { $in: fromPackageIds },
}).then((expPackage) => {
ExpectedPackages.findOneAsync(
{
_id: { $in: fromPackageIds },
},
{
projection: {
_id: 1,
studioId: 1,
},
}
).then((expPackage: Pick<ExpectedPackageDB, '_id' | 'studioId'> | undefined) => {
if (!expPackage)
throw new Meteor.Error(404, `ExpectedPackages "${fromPackageIds}" not found`)

Expand Down
Loading
Loading