Compare commits

...

4 Commits

Author SHA1 Message Date
594c89ab76 Select the detection audit payload on getTableState
All checks were successful
Tests / Tests (pull_request) Successful in 3m9s
Adds identifierToBox to TableStateGQL and selects it, alongside the
homography that was already in the schema but never requested, so the
photo path can draw a detection back over the image it came from. The
live and shot queries keep the layout-only selection: their frame lives
inside a video the app never holds, and one of them polls every five
seconds.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 18:55:19 -07:00
d7b0b21430 Merge pull request 'Use BigInt for video export file sizes' (#310) from agent/video-export-bigint into master
Reviewed-on: #310
2026-07-27 19:35:17 +00:00
f173466b2f Merge pull request 'Add createShotShareLink for short share links' (#311) from dean/short-share-links-gql into master
Reviewed-on: #311
2026-07-27 19:35:11 +00:00
3e6dabe12a Use BigInt for video export file size
All checks were successful
Tests / Tests (pull_request) Successful in 6m3s
2026-07-24 20:09:19 -07:00
3 changed files with 281 additions and 43 deletions

View File

@@ -4078,6 +4078,7 @@ export type SyncAppleSubscriptionResultGql = {
export type TableStateGql = {
__typename?: "TableStateGQL";
homography?: Maybe<HomographyInfoGql>;
identifierToBox: Array<BoundingBoxGql>;
identifierToColor: Array<Maybe<RgbColorGql>>;
identifierToPosition: Array<Array<Scalars["Float"]["output"]>>;
};
@@ -4318,7 +4319,7 @@ export type VideoExportJobGql = {
createdAt?: Maybe<Scalars["DateTime"]["output"]>;
downloadUrl?: Maybe<Scalars["String"]["output"]>;
expiresAt?: Maybe<Scalars["DateTime"]["output"]>;
fileSizeBytes?: Maybe<Scalars["Int"]["output"]>;
fileSizeBytes?: Maybe<Scalars["BigInt"]["output"]>;
id: Scalars["Int"]["output"];
mode: VideoExportModeEnum;
runId?: Maybe<Scalars["Int"]["output"]>;
@@ -7480,6 +7481,76 @@ export type FinalizePlayerAssignmentsMutation = {
}>;
};
export type TableStateLayoutFieldsFragment = {
__typename?: "TableStateGQL";
identifierToPosition: Array<Array<number>>;
identifierToColor: Array<{ __typename?: "RGBColorGQL"; hex: string } | null>;
};
export type DetectionBoxFieldsFragment = {
__typename?: "BoundingBoxGQL";
left: number;
top: number;
width: number;
height: number;
};
export type DetectionPocketPointFieldsFragment = {
__typename?: "PocketPointsGQL";
topLeft: { __typename?: "IntPoint2D"; x: number; y: number };
topSide: { __typename?: "IntPoint2D"; x: number; y: number };
topRight: { __typename?: "IntPoint2D"; x: number; y: number };
bottomLeft: { __typename?: "IntPoint2D"; x: number; y: number };
bottomSide: { __typename?: "IntPoint2D"; x: number; y: number };
bottomRight: { __typename?: "IntPoint2D"; x: number; y: number };
};
export type TableStateDetectionFieldsFragment = {
__typename?: "TableStateGQL";
identifierToBox: Array<{
__typename?: "BoundingBoxGQL";
left: number;
top: number;
width: number;
height: number;
}>;
homography?: {
__typename?: "HomographyInfoGQL";
crop: {
__typename?: "BoundingBoxGQL";
left: number;
top: number;
width: number;
height: number;
};
pockets: Array<{
__typename?: "BoundingBoxGQL";
left: number;
top: number;
width: number;
height: number;
}>;
sourcePoints: {
__typename?: "PocketPointsGQL";
topLeft: { __typename?: "IntPoint2D"; x: number; y: number };
topSide: { __typename?: "IntPoint2D"; x: number; y: number };
topRight: { __typename?: "IntPoint2D"; x: number; y: number };
bottomLeft: { __typename?: "IntPoint2D"; x: number; y: number };
bottomSide: { __typename?: "IntPoint2D"; x: number; y: number };
bottomRight: { __typename?: "IntPoint2D"; x: number; y: number };
};
destPoints: {
__typename?: "PocketPointsGQL";
topLeft: { __typename?: "IntPoint2D"; x: number; y: number };
topSide: { __typename?: "IntPoint2D"; x: number; y: number };
topRight: { __typename?: "IntPoint2D"; x: number; y: number };
bottomLeft: { __typename?: "IntPoint2D"; x: number; y: number };
bottomSide: { __typename?: "IntPoint2D"; x: number; y: number };
bottomRight: { __typename?: "IntPoint2D"; x: number; y: number };
};
} | null;
};
export type GetTableStateQueryVariables = Exact<{
b64Image: Scalars["String"]["input"];
tableSize?: InputMaybe<Scalars["Float"]["input"]>;
@@ -7495,6 +7566,48 @@ export type GetTableStateQuery = {
__typename?: "RGBColorGQL";
hex: string;
} | null>;
identifierToBox: Array<{
__typename?: "BoundingBoxGQL";
left: number;
top: number;
width: number;
height: number;
}>;
homography?: {
__typename?: "HomographyInfoGQL";
crop: {
__typename?: "BoundingBoxGQL";
left: number;
top: number;
width: number;
height: number;
};
pockets: Array<{
__typename?: "BoundingBoxGQL";
left: number;
top: number;
width: number;
height: number;
}>;
sourcePoints: {
__typename?: "PocketPointsGQL";
topLeft: { __typename?: "IntPoint2D"; x: number; y: number };
topSide: { __typename?: "IntPoint2D"; x: number; y: number };
topRight: { __typename?: "IntPoint2D"; x: number; y: number };
bottomLeft: { __typename?: "IntPoint2D"; x: number; y: number };
bottomSide: { __typename?: "IntPoint2D"; x: number; y: number };
bottomRight: { __typename?: "IntPoint2D"; x: number; y: number };
};
destPoints: {
__typename?: "PocketPointsGQL";
topLeft: { __typename?: "IntPoint2D"; x: number; y: number };
topSide: { __typename?: "IntPoint2D"; x: number; y: number };
topRight: { __typename?: "IntPoint2D"; x: number; y: number };
bottomLeft: { __typename?: "IntPoint2D"; x: number; y: number };
bottomSide: { __typename?: "IntPoint2D"; x: number; y: number };
bottomRight: { __typename?: "IntPoint2D"; x: number; y: number };
};
} | null;
};
};
@@ -9281,7 +9394,7 @@ export type VideoExportJobFieldsFragment = {
shotIds?: Array<number> | null;
runId?: number | null;
downloadUrl?: string | null;
fileSizeBytes?: number | null;
fileSizeBytes?: any | null;
expiresAt?: any | null;
createdAt?: any | null;
};
@@ -9303,7 +9416,7 @@ export type RequestVideoExportMutation = {
shotIds?: Array<number> | null;
runId?: number | null;
downloadUrl?: string | null;
fileSizeBytes?: number | null;
fileSizeBytes?: any | null;
expiresAt?: any | null;
createdAt?: any | null;
};
@@ -9335,7 +9448,7 @@ export type VideoExportJobQuery = {
shotIds?: Array<number> | null;
runId?: number | null;
downloadUrl?: string | null;
fileSizeBytes?: number | null;
fileSizeBytes?: any | null;
expiresAt?: any | null;
createdAt?: any | null;
} | null;
@@ -9359,7 +9472,7 @@ export type MyVideoExportsQuery = {
shotIds?: Array<number> | null;
runId?: number | null;
downloadUrl?: string | null;
fileSizeBytes?: number | null;
fileSizeBytes?: any | null;
expiresAt?: any | null;
createdAt?: any | null;
}>;
@@ -9980,6 +10093,73 @@ export const PlayerClusterFieldsFragmentDoc = gql`
}
${PlayerClusterShotFieldsFragmentDoc}
`;
export const TableStateLayoutFieldsFragmentDoc = gql`
fragment TableStateLayoutFields on TableStateGQL {
identifierToPosition
identifierToColor {
hex
}
}
`;
export const DetectionBoxFieldsFragmentDoc = gql`
fragment DetectionBoxFields on BoundingBoxGQL {
left
top
width
height
}
`;
export const DetectionPocketPointFieldsFragmentDoc = gql`
fragment DetectionPocketPointFields on PocketPointsGQL {
topLeft {
x
y
}
topSide {
x
y
}
topRight {
x
y
}
bottomLeft {
x
y
}
bottomSide {
x
y
}
bottomRight {
x
y
}
}
`;
export const TableStateDetectionFieldsFragmentDoc = gql`
fragment TableStateDetectionFields on TableStateGQL {
identifierToBox {
...DetectionBoxFields
}
homography {
crop {
...DetectionBoxFields
}
pockets {
...DetectionBoxFields
}
sourcePoints {
...DetectionPocketPointFields
}
destPoints {
...DetectionPocketPointFields
}
}
}
${DetectionBoxFieldsFragmentDoc}
${DetectionPocketPointFieldsFragmentDoc}
`;
export const ShotClipRangeFragmentDoc = gql`
fragment ShotClipRange on ShotGQL {
id
@@ -15931,12 +16111,12 @@ export const GetTableStateDocument = gql`
tableSize: $tableSize
useHomography: $useHomography
) {
identifierToPosition
identifierToColor {
hex
}
...TableStateLayoutFields
...TableStateDetectionFields
}
}
${TableStateLayoutFieldsFragmentDoc}
${TableStateDetectionFieldsFragmentDoc}
`;
/**
@@ -16012,13 +16192,11 @@ export const GetLiveTableStateDocument = gql`
videoId
frameIndex
tableState {
identifierToPosition
identifierToColor {
hex
}
...TableStateLayoutFields
}
}
}
${TableStateLayoutFieldsFragmentDoc}
`;
/**
@@ -16093,13 +16271,11 @@ export const GetShotTableStateDocument = gql`
videoId
frameIndex
tableState {
identifierToPosition
identifierToColor {
hex
}
...TableStateLayoutFields
}
}
}
${TableStateLayoutFieldsFragmentDoc}
`;
/**

View File

@@ -1,3 +1,72 @@
fragment TableStateLayoutFields on TableStateGQL {
identifierToPosition
identifierToColor {
hex
}
}
fragment DetectionBoxFields on BoundingBoxGQL {
left
top
width
height
}
fragment DetectionPocketPointFields on PocketPointsGQL {
topLeft {
x
y
}
topSide {
x
y
}
topRight {
x
y
}
bottomLeft {
x
y
}
bottomSide {
x
y
}
bottomRight {
x
y
}
}
# Everything needed to redraw a detection over the image it came from:
# the detector's boxes plus the homography that turned them into plane
# 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.
fragment TableStateDetectionFields on TableStateGQL {
identifierToBox {
...DetectionBoxFields
}
homography {
crop {
...DetectionBoxFields
}
pockets {
...DetectionBoxFields
}
sourcePoints {
...DetectionPocketPointFields
}
destPoints {
...DetectionPocketPointFields
}
}
}
query GetTableState(
$b64Image: String!
$tableSize: Float
@@ -8,10 +77,8 @@ query GetTableState(
tableSize: $tableSize
useHomography: $useHomography
) {
identifierToPosition
identifierToColor {
hex
}
...TableStateLayoutFields
...TableStateDetectionFields
}
}
@@ -20,10 +87,7 @@ query GetLiveTableState($videoId: Int!) {
videoId
frameIndex
tableState {
identifierToPosition
identifierToColor {
hex
}
...TableStateLayoutFields
}
}
}
@@ -34,10 +98,7 @@ query GetShotTableState($shotId: Int!) {
videoId
frameIndex
tableState {
identifierToPosition
identifierToColor {
hex
}
...TableStateLayoutFields
}
}
}

View File

@@ -536,19 +536,6 @@ type GetUploadLinksReturn {
stream: UploadStreamGQL
}
type SegmentUploadLink {
segmentIndex: Int!
uploadUrl: String!
headers: [Header]!
}
type UploadLinkBatch {
links: [SegmentUploadLink!]!
alreadyUploaded: [Int!]!
}
union UploadLinkBatchGetUploadLinkErrors = UploadLinkBatch | GetUploadLinkErrors
type HLSPlaylistGQL {
videoId: Int!
m3u8Text: String!
@@ -1314,6 +1301,12 @@ type SegmentAlreadyUploadedErr {
segmentId: Int!
}
type SegmentUploadLink {
segmentIndex: Int!
uploadUrl: String!
headers: [Header]!
}
type SerializedShotPathsGQL {
b64EncodedBuffer: String
}
@@ -1715,6 +1708,7 @@ type TableStateGQL {
identifierToPosition: [[Float!]!]!
homography: HomographyInfoGQL
identifierToColor: [RGBColorGQL]!
identifierToBox: [BoundingBoxGQL!]!
}
type TagClassGQL {
@@ -1800,6 +1794,13 @@ type UploadLink {
headers: [Header]!
}
type UploadLinkBatch {
links: [SegmentUploadLink!]!
alreadyUploaded: [Int!]!
}
union UploadLinkBatchGetUploadLinkErrors = UploadLinkBatch | GetUploadLinkErrors
union UploadLinkGetProfileUploadLinkErrors =
UploadLink
| GetProfileUploadLinkErrors
@@ -1934,7 +1935,7 @@ type VideoExportJobGQL {
shotIds: [Int!]
runId: Int
downloadUrl: String
fileSizeBytes: Int
fileSizeBytes: BigInt
expiresAt: DateTime
createdAt: DateTime
}