Compare commits

..

3 Commits

Author SHA1 Message Date
Dean Wenstrand
901f4222a4 Let shot filters select a single player within a video
All checks were successful
Tests / Tests (pull_request) Successful in 11s
Adds playerIndex to FilterInput, backing ShooterIdentity.player_index --
the cluster-assigned shooter, 0-based and stable only within one video.

Deliberately separate from userId, which is populated only once a cluster
has been confirmed against a registered account. Multiplayer sessions are
usually played against someone without a Railbird login, so filtering by
userId cannot express "the other player" in the common case; the cluster
index always can.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-06 13:14:00 -07:00
0f6e7b28d4 Merge pull request 'Return the shot's start frame image from getShotTableState' (#321) from ivan/shot-table-state-frame-image into master 2026-08-06 19:11:59 +00:00
9b6b08c1ec Return the shot's start frame image from getShotTableState
All checks were successful
Tests / Tests (pull_request) Successful in 12s
Shot Lab was showing the video's single thumbnail next to every shot's
layout. The backend now returns the frame the layout was detected in as
a base64 JPEG (frameImage), selected by the GetShotTableState operation.
Also dedupes ShareLinkGQL/CreateShotShareLinkInput, which appeared twice
in the committed schema after a bad merge.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-06 09:56:38 -07:00
3 changed files with 11 additions and 13 deletions

View File

@@ -3969,6 +3969,7 @@ export type ShotProjectionGql = {
export type ShotTableStateGql = {
__typename?: "ShotTableStateGQL";
frameImage?: Maybe<Scalars["String"]["output"]>;
frameIndex: Scalars["Int"]["output"];
shotId: Scalars["Int"]["output"];
tableState: TableStateGql;
@@ -7822,6 +7823,7 @@ export type GetShotTableStateQuery = {
shotId: number;
videoId: number;
frameIndex: number;
frameImage?: string | null;
tableState: {
__typename?: "TableStateGQL";
identifierToPosition: Array<Array<number>>;
@@ -16602,6 +16604,7 @@ export const GetShotTableStateDocument = gql`
shotId
videoId
frameIndex
frameImage
tableState {
...TableStateLayoutFields
}

View File

@@ -44,9 +44,10 @@ fragment DetectionPocketPointFields on PocketPointsGQL {
# positions. Boxes, pocket boxes, and source points share the crop's
# coordinate space; the crop locates that space in the full frame.
#
# Only the photo query selects this. The live and shot queries read a
# frame out of a video the app never holds, so there is no image to
# overlay, and getLiveTableState is polled every five seconds.
# Only the photo query selects this. The shot query returns its source
# frame (frameImage) but positions there come from the stored homography,
# so there are no fresh boxes to draw; the live query returns no image at
# all and is polled every five seconds.
fragment TableStateDetectionFields on TableStateGQL {
identifierToBox {
...DetectionBoxFields
@@ -97,6 +98,9 @@ query GetShotTableState($shotId: Int!) {
shotId
videoId
frameIndex
# The b64 JPEG frame the layout was detected in, so the client can show
# the shot's actual starting frame instead of the video's thumbnail.
frameImage
tableState {
...TableStateLayoutFields
}

View File

@@ -250,11 +250,6 @@ input CreatePoolHallCameraInput {
claimTermsText: String = null
}
input CreateShotShareLinkInput {
shotIds: [Int!]!
paddingSeconds: Float = null
}
input CreatePoolHallInput {
name: String!
address: String = null
@@ -1574,6 +1569,7 @@ type ShotTableStateGQL {
videoId: Int!
frameIndex: Int!
tableState: TableStateGQL!
frameImage: String
}
input ShotsOrderingComponent @oneOf {
@@ -1967,11 +1963,6 @@ type UserSubscriptionStatusGQL {
stripeSubscriptionId: String
}
type ShareLinkGQL {
slug: String!
url: String!
}
type VideoExportJobGQL {
id: Int!
videoId: Int!