lightning-pose-app 1.8.1a3__py3-none-any.whl → 1.8.1a5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. lightning_pose_app-1.8.1a5.dist-info/METADATA +23 -0
  2. lightning_pose_app-1.8.1a5.dist-info/RECORD +29 -0
  3. litpose_app/config.py +22 -0
  4. litpose_app/deps.py +77 -0
  5. litpose_app/main.py +161 -300
  6. litpose_app/ngdist/ng_app/3rdpartylicenses.txt +11 -11
  7. litpose_app/ngdist/ng_app/index.html +3 -2
  8. litpose_app/ngdist/ng_app/{main-LJHMLKBL.js → main-6XYUWDGZ.js} +370 -203
  9. litpose_app/ngdist/ng_app/main-6XYUWDGZ.js.map +1 -0
  10. litpose_app/ngdist/ng_app/{styles-4V6RXJMC.css → styles-GMK322VW.css} +32 -1
  11. litpose_app/ngdist/ng_app/styles-GMK322VW.css.map +7 -0
  12. litpose_app/{run_ffprobe.py → routes/ffprobe.py} +164 -132
  13. litpose_app/{super_rglob.py → routes/files.py} +108 -48
  14. litpose_app/routes/project.py +72 -0
  15. litpose_app/routes/transcode.py +131 -0
  16. litpose_app/tasks/__init__.py +0 -0
  17. litpose_app/tasks/management.py +20 -0
  18. litpose_app/tasks/transcode_fine.py +7 -0
  19. litpose_app/transcode_fine.py +173 -0
  20. lightning_pose_app-1.8.1a3.dist-info/METADATA +0 -15
  21. lightning_pose_app-1.8.1a3.dist-info/RECORD +0 -21
  22. litpose_app/ngdist/ng_app/main-LJHMLKBL.js.map +0 -1
  23. litpose_app/ngdist/ng_app/styles-4V6RXJMC.css.map +0 -7
  24. {lightning_pose_app-1.8.1a3.dist-info → lightning_pose_app-1.8.1a5.dist-info}/WHEEL +0 -0
  25. /litpose_app/ngdist/ng_app/{app.component-UHVEDPZR.css.map → app.component-UAQUAGNZ.css.map} +0 -0
  26. /litpose_app/ngdist/ng_app/{project-settings.component-5IRK7U7U.css.map → project-settings.component-HKHIVUJR.css.map} +0 -0
  27. /litpose_app/ngdist/ng_app/{video-tile.component-XSYKMARQ.css.map → video-tile.component-RDL4BSJ4.css.map} +0 -0
  28. /litpose_app/ngdist/ng_app/{viewer-page.component-MRTIUFL2.css.map → viewer-page.component-KDHT6XH5.css.map} +0 -0
@@ -40016,10 +40016,18 @@ var ProjectInfoService = class _ProjectInfoService {
40016
40016
  // undefined means not yet requested
40017
40017
  // null means requested and not present
40018
40018
  _projectInfo = void 0;
40019
+ // The directory that we are storing fine videos in.
40020
+ fineVideoDir = "";
40019
40021
  async loadProjectInfo() {
40020
- const response = await this.rpc.call("getProjectInfo");
40021
- this._projectInfo = response.projectInfo ? new ProjectInfo(response.projectInfo) : null;
40022
- this.setAllViews(this._projectInfo?.views ?? []);
40022
+ const promises = [];
40023
+ promises.push(this.rpc.call("getProjectInfo").then((response) => {
40024
+ this._projectInfo = response.projectInfo ? new ProjectInfo(response.projectInfo) : null;
40025
+ this.setAllViews(this._projectInfo?.views ?? []);
40026
+ }));
40027
+ promises.push(this.rpc.call("getFineVideoDir").then((response) => {
40028
+ this.fineVideoDir = response.path;
40029
+ }));
40030
+ return Promise.allSettled(promises);
40023
40031
  }
40024
40032
  get projectInfo() {
40025
40033
  return this._projectInfo;
@@ -40182,6 +40190,32 @@ var CsvParserService = class _CsvParserService {
40182
40190
  }], null, null);
40183
40191
  })();
40184
40192
 
40193
+ // src/app/utils/comparators.ts
40194
+ var compareStringArraysOrdered = (prev, curr) => {
40195
+ if (prev.length !== curr.length) {
40196
+ return false;
40197
+ }
40198
+ for (let i = 0; i < prev.length; i++) {
40199
+ if (prev[i] !== curr[i]) {
40200
+ return false;
40201
+ }
40202
+ }
40203
+ return true;
40204
+ };
40205
+ var createSessionViewComparator = (allViewsOrder) => {
40206
+ return (a, b) => {
40207
+ const indexA = allViewsOrder.indexOf(a.viewName);
40208
+ const indexB = allViewsOrder.indexOf(b.viewName);
40209
+ if (indexA === -1 && indexB === -1)
40210
+ return 0;
40211
+ if (indexA === -1)
40212
+ return 1;
40213
+ if (indexB === -1)
40214
+ return -1;
40215
+ return indexA - indexB;
40216
+ };
40217
+ };
40218
+
40185
40219
  // src/app/session.service.ts
40186
40220
  var SessionService = class _SessionService {
40187
40221
  rpc = inject(RpcService);
@@ -40189,14 +40223,23 @@ var SessionService = class _SessionService {
40189
40223
  predictionFiles = [];
40190
40224
  projectInfoService = inject(ProjectInfoService);
40191
40225
  csvParser = inject(CsvParserService);
40226
+ sessionsLoading = signal(false);
40192
40227
  _allSessions = new BehaviorSubject([]);
40193
40228
  allSessions$ = this._allSessions.asObservable();
40194
40229
  allSessions = toSignal(this.allSessions$, { requireSync: true });
40195
40230
  async loadSessions() {
40231
+ try {
40232
+ this.sessionsLoading.set(true);
40233
+ await this._loadSessions();
40234
+ } finally {
40235
+ this.sessionsLoading.set(false);
40236
+ }
40237
+ }
40238
+ async _loadSessions() {
40196
40239
  const projectInfo = this.projectInfoService.projectInfo;
40197
40240
  const response = await this.rpc.call("rglob", {
40198
40241
  baseDir: projectInfo.data_dir,
40199
- pattern: "**/*.fine.mp4",
40242
+ pattern: "**/*.mp4",
40200
40243
  //temporary
40201
40244
  noDirs: true
40202
40245
  });
@@ -40260,7 +40303,7 @@ var SessionService = class _SessionService {
40260
40303
  this.projectInfoService.setAllKeypoints(allKeypoints);
40261
40304
  }
40262
40305
  getPredictionFilesForSession(sessionKey) {
40263
- const predictionFiles = this.predictionFiles.filter((p) => p.sessionKey === sessionKey || p.sessionKey === sessionKey.replace(/\.fine$/, ""));
40306
+ const predictionFiles = this.predictionFiles.filter((p) => p.sessionKey === sessionKey);
40264
40307
  return predictionFiles;
40265
40308
  }
40266
40309
  async getPredictionFile(pfile) {
@@ -40296,9 +40339,10 @@ var SessionService = class _SessionService {
40296
40339
  videoPath: projectInfo.data_dir + "/" + filename
40297
40340
  });
40298
40341
  }
40342
+ const cmp = createSessionViewComparator(this.projectInfoService.allViews());
40299
40343
  return Array.from(sessionKeyToItsViewFiles.entries()).map(([key, sessionViews]) => ({
40300
40344
  key: key.replace(/\.mp4$/, ""),
40301
- views: sessionViews
40345
+ views: [...sessionViews].sort(cmp)
40302
40346
  }));
40303
40347
  }
40304
40348
  static \u0275fac = function SessionService_Factory(__ngFactoryType__) {
@@ -54313,12 +54357,72 @@ var ScrollingModule = class _ScrollingModule {
54313
54357
  }], null, null);
54314
54358
  })();
54315
54359
 
54360
+ // src/app/utils/sserx.ts
54361
+ function getEventStream(endpoint) {
54362
+ return new Observable((observer) => {
54363
+ if (typeof EventSource === "undefined") {
54364
+ observer.error("EventSource is not supported by this browser.");
54365
+ return;
54366
+ }
54367
+ const eventSource = new EventSource(`${endpoint}`);
54368
+ eventSource.onmessage = (event) => {
54369
+ console.log("Received SSE message:", event.data);
54370
+ observer.next(JSON.parse(event.data));
54371
+ };
54372
+ eventSource.onerror = (error) => {
54373
+ console.error("SSE Error:", error);
54374
+ if (eventSource.readyState === EventSource.CLOSED) {
54375
+ observer.error("SSE connection closed by server.");
54376
+ } else {
54377
+ observer.error(error);
54378
+ }
54379
+ };
54380
+ return () => {
54381
+ console.log("Closing SSE connection.");
54382
+ eventSource.close();
54383
+ };
54384
+ });
54385
+ }
54386
+
54387
+ // src/app/utils/fine-video.service.ts
54388
+ var FineVideoService = class _FineVideoService {
54389
+ projectInfoService = inject(ProjectInfoService);
54390
+ fineVideoStatus$ = getEventStream("/app/v0/rpc/getFineVideoStatus");
54391
+ numPendingTranscodeTasks = toSignal(this.fineVideoStatus$, {
54392
+ initialValue: { pending: 0 }
54393
+ });
54394
+ fineVideoPath(videoPath) {
54395
+ if (!this.projectInfoService.fineVideoDir) {
54396
+ throw new Error("ProjectInfoService.fineVideoDir called but not yet initialized");
54397
+ }
54398
+ const filename = videoPath.split("/").pop();
54399
+ return "/app/v0/files/" + this.projectInfoService.fineVideoDir + "/" + filename;
54400
+ }
54401
+ static \u0275fac = function FineVideoService_Factory(__ngFactoryType__) {
54402
+ return new (__ngFactoryType__ || _FineVideoService)();
54403
+ };
54404
+ static \u0275prov = /* @__PURE__ */ \u0275\u0275defineInjectable({ token: _FineVideoService, factory: _FineVideoService.\u0275fac, providedIn: "root" });
54405
+ };
54406
+ (() => {
54407
+ (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(FineVideoService, [{
54408
+ type: Injectable,
54409
+ args: [{
54410
+ providedIn: "root"
54411
+ }]
54412
+ }], null, null);
54413
+ })();
54414
+
54316
54415
  // src/app/viewer/viewer-left-panel/viewer-sessions-panel.component.ts
54317
54416
  var _c03 = (a0) => ["/viewer", a0];
54318
54417
  var _forTrack0 = ($index, $item) => $item.key;
54319
- function ViewerSessionsPanelComponent_For_16_Template(rf, ctx) {
54418
+ function ViewerSessionsPanelComponent_Conditional_5_Template(rf, ctx) {
54419
+ if (rf & 1) {
54420
+ \u0275\u0275element(0, "progress", 3);
54421
+ }
54422
+ }
54423
+ function ViewerSessionsPanelComponent_Conditional_6_For_3_Template(rf, ctx) {
54320
54424
  if (rf & 1) {
54321
- \u0275\u0275elementStart(0, "li", 9)(1, "a", 10);
54425
+ \u0275\u0275elementStart(0, "li", 7)(1, "a", 8);
54322
54426
  \u0275\u0275text(2);
54323
54427
  \u0275\u0275elementEnd()();
54324
54428
  }
@@ -54330,29 +54434,51 @@ function ViewerSessionsPanelComponent_For_16_Template(rf, ctx) {
54330
54434
  \u0275\u0275textInterpolate1(" ", session_r1.key, " ");
54331
54435
  }
54332
54436
  }
54437
+ function ViewerSessionsPanelComponent_Conditional_6_Template(rf, ctx) {
54438
+ if (rf & 1) {
54439
+ \u0275\u0275elementStart(0, "div", 4)(1, "ul", 6);
54440
+ \u0275\u0275repeaterCreate(2, ViewerSessionsPanelComponent_Conditional_6_For_3_Template, 3, 4, "li", 7, _forTrack0);
54441
+ \u0275\u0275elementEnd()();
54442
+ }
54443
+ if (rf & 2) {
54444
+ const ctx_r1 = \u0275\u0275nextContext();
54445
+ \u0275\u0275advance(2);
54446
+ \u0275\u0275repeater(ctx_r1.sessionService.allSessions());
54447
+ }
54448
+ }
54449
+ function ViewerSessionsPanelComponent_Conditional_7_Template(rf, ctx) {
54450
+ if (rf & 1) {
54451
+ \u0275\u0275elementStart(0, "div", 5)(1, "span", 9);
54452
+ \u0275\u0275text(2, "info_i");
54453
+ \u0275\u0275elementEnd();
54454
+ \u0275\u0275elementStart(3, "span");
54455
+ \u0275\u0275text(4, "Videos will populate once they are transcoded.");
54456
+ \u0275\u0275elementEnd()();
54457
+ }
54458
+ }
54333
54459
  var ViewerSessionsPanelComponent = class _ViewerSessionsPanelComponent {
54334
54460
  sessionService = inject(SessionService);
54461
+ fineVideoService = inject(FineVideoService);
54335
54462
  static \u0275fac = function ViewerSessionsPanelComponent_Factory(__ngFactoryType__) {
54336
54463
  return new (__ngFactoryType__ || _ViewerSessionsPanelComponent)();
54337
54464
  };
54338
- static \u0275cmp = /* @__PURE__ */ \u0275\u0275defineComponent({ type: _ViewerSessionsPanelComponent, selectors: [["app-sessions-panel"]], decls: 17, vars: 0, consts: [[1, "flex", "justify-between"], [1, "px-2"], [1, "dropdown", "dropdown-end"], [1, "btn", "btn-xs", "btn-ghost"], [1, "material-icons", "text-base!"], ["tabindex", "0", 1, "dropdown-content", "menu", "menu-sm", "bg-base-100", "z-40", "w-52", "p-2", "shadow-sm"], [1, "panel-content", "inset-shadow-xs", "inset-shadow-black/30"], [1, "overflow-x-auto"], ["tabindex", "0", "role", "listbox", 1, "w-fit", "relative"], ["routerLinkActive", "bg-sky-700", 1, "text-nowrap"], ["role", "option", 1, "p-1", "block", "hover:bg-base-content/10", "rounded-sm", 3, "routerLink"]], template: function ViewerSessionsPanelComponent_Template(rf, ctx) {
54465
+ static \u0275cmp = /* @__PURE__ */ \u0275\u0275defineComponent({ type: _ViewerSessionsPanelComponent, selectors: [["app-sessions-panel"]], decls: 8, vars: 2, consts: [[1, "flex", "justify-between"], [1, "px-2"], [1, "panel-content", "inset-shadow-xs", "inset-shadow-black/30", "overflow-y-auto", "h-120"], [1, "progress", "w-full"], [1, "overflow-x-auto"], [1, "text-sm", "flex"], ["tabindex", "0", "role", "listbox", 1, "w-fit", "relative"], ["routerLinkActive", "bg-sky-700", 1, "text-nowrap"], ["role", "option", 1, "p-1", "block", "hover:bg-base-content/10", "rounded-sm", 3, "routerLink"], [1, "material-icons", "!text-sm", "max-w-4"]], template: function ViewerSessionsPanelComponent_Template(rf, ctx) {
54339
54466
  if (rf & 1) {
54340
54467
  \u0275\u0275elementStart(0, "div", 0)(1, "h1", 1);
54341
54468
  \u0275\u0275text(2, "Sessions");
54342
54469
  \u0275\u0275elementEnd();
54343
- \u0275\u0275elementStart(3, "div")(4, "div", 2)(5, "button", 3)(6, "span", 4);
54344
- \u0275\u0275text(7, "more_vert");
54345
- \u0275\u0275elementEnd()();
54346
- \u0275\u0275elementStart(8, "ul", 5)(9, "li")(10, "a");
54347
- \u0275\u0275text(11, "Change grouping rule");
54348
- \u0275\u0275elementEnd()()()()()();
54349
- \u0275\u0275elementStart(12, "div", 6)(13, "div", 7)(14, "ul", 8);
54350
- \u0275\u0275repeaterCreate(15, ViewerSessionsPanelComponent_For_16_Template, 3, 4, "li", 9, _forTrack0);
54351
- \u0275\u0275elementEnd()()();
54470
+ \u0275\u0275element(3, "div");
54471
+ \u0275\u0275elementEnd();
54472
+ \u0275\u0275elementStart(4, "div", 2);
54473
+ \u0275\u0275template(5, ViewerSessionsPanelComponent_Conditional_5_Template, 1, 0, "progress", 3)(6, ViewerSessionsPanelComponent_Conditional_6_Template, 4, 0, "div", 4);
54474
+ \u0275\u0275elementEnd();
54475
+ \u0275\u0275template(7, ViewerSessionsPanelComponent_Conditional_7_Template, 5, 0, "div", 5);
54352
54476
  }
54353
54477
  if (rf & 2) {
54354
- \u0275\u0275advance(15);
54355
- \u0275\u0275repeater(ctx.sessionService.allSessions());
54478
+ \u0275\u0275advance(5);
54479
+ \u0275\u0275conditional(ctx.sessionService.sessionsLoading() ? 5 : 6);
54480
+ \u0275\u0275advance(2);
54481
+ \u0275\u0275conditional(ctx.fineVideoService.numPendingTranscodeTasks().pending > 0 ? 7 : -1);
54356
54482
  }
54357
54483
  }, dependencies: [RouterLink, RouterLinkActive, MatListModule, ScrollingModule], encapsulation: 2, changeDetection: 0 });
54358
54484
  };
@@ -54364,7 +54490,9 @@ var ViewerSessionsPanelComponent = class _ViewerSessionsPanelComponent {
54364
54490
  <!--header row-->
54365
54491
  <h1 class="px-2">Sessions</h1>
54366
54492
  <div>
54367
- <!-- right side of section -->
54493
+ <!-- right side of sessions header -->
54494
+ <!-- Add three dots dropdown with "change grouping rule". Stashed. -->
54495
+ <!--
54368
54496
  <div class="dropdown dropdown-end">
54369
54497
  <button class="btn btn-xs btn-ghost">
54370
54498
  <span class="material-icons text-base!">more_vert</span>
@@ -54376,46 +54504,46 @@ var ViewerSessionsPanelComponent = class _ViewerSessionsPanelComponent {
54376
54504
  <li><a>Change grouping rule</a></li>
54377
54505
  </ul>
54378
54506
  </div>
54507
+ -->
54379
54508
  </div>
54380
54509
  </div>
54381
- <div class="panel-content inset-shadow-xs inset-shadow-black/30">
54382
- <!--panel content-->
54383
- <div class="overflow-x-auto">
54384
- <ul class="w-fit relative" tabindex="0" role="listbox">
54385
- @for (session of sessionService.allSessions(); track session.key) {
54386
- <li class="text-nowrap" routerLinkActive="bg-sky-700">
54387
- <a
54388
- role="option"
54389
- class="p-1 block hover:bg-base-content/10 rounded-sm"
54390
- [routerLink]="['/viewer', session.key]"
54391
- >
54392
- {{ session.key }}
54393
- </a>
54394
- </li>
54395
- }
54396
- </ul>
54397
- </div>
54510
+ <div
54511
+ class="panel-content inset-shadow-xs inset-shadow-black/30 overflow-y-auto h-120"
54512
+ >
54513
+ @if (sessionService.sessionsLoading()) {
54514
+ <progress class="progress w-full"></progress>
54515
+ } @else {
54516
+ <!--panel content-->
54517
+ <div class="overflow-x-auto">
54518
+ <ul class="w-fit relative" tabindex="0" role="listbox">
54519
+ @for (session of sessionService.allSessions(); track session.key) {
54520
+ <li class="text-nowrap" routerLinkActive="bg-sky-700">
54521
+ <a
54522
+ role="option"
54523
+ class="p-1 block hover:bg-base-content/10 rounded-sm"
54524
+ [routerLink]="['/viewer', session.key]"
54525
+ >
54526
+ {{ session.key }}
54527
+ </a>
54528
+ </li>
54529
+ }
54530
+ </ul>
54531
+ </div>
54532
+ }
54398
54533
  </div>
54534
+ @if (fineVideoService.numPendingTranscodeTasks().pending > 0) {
54535
+ <div class="text-sm flex">
54536
+ <span class="material-icons !text-sm max-w-4">info_i</span>
54537
+ <span>Videos will populate once they are transcoded.</span>
54538
+ </div>
54539
+ }
54399
54540
  ` }]
54400
54541
  }], null, null);
54401
54542
  })();
54402
54543
  (() => {
54403
- (typeof ngDevMode === "undefined" || ngDevMode) && \u0275setClassDebugInfo(ViewerSessionsPanelComponent, { className: "ViewerSessionsPanelComponent", filePath: "src/app/viewer/viewer-left-panel/viewer-sessions-panel.component.ts", lineNumber: 24 });
54544
+ (typeof ngDevMode === "undefined" || ngDevMode) && \u0275setClassDebugInfo(ViewerSessionsPanelComponent, { className: "ViewerSessionsPanelComponent", filePath: "src/app/viewer/viewer-left-panel/viewer-sessions-panel.component.ts", lineNumber: 25 });
54404
54545
  })();
54405
54546
 
54406
- // src/app/utils/comparators.ts
54407
- var compareStringArraysOrdered = (prev, curr) => {
54408
- if (prev.length !== curr.length) {
54409
- return false;
54410
- }
54411
- for (let i = 0; i < prev.length; i++) {
54412
- if (prev[i] !== curr[i]) {
54413
- return false;
54414
- }
54415
- }
54416
- return true;
54417
- };
54418
-
54419
54547
  // src/app/view-settings.model.ts
54420
54548
  var ViewSettings = class _ViewSettings {
54421
54549
  _viewsShown = new BehaviorSubject([]);
@@ -54663,12 +54791,22 @@ var VideoPlayerControlsComponent = class _VideoPlayerControlsComponent {
54663
54791
  // src/app/components/video-player/video-tile/video-tile.component.ts
54664
54792
  var _c04 = ["videoEl"];
54665
54793
  var _c13 = ["*"];
54794
+ var videoErrorMessages = {
54795
+ [MediaError.MEDIA_ERR_ABORTED]: "Error: aborted.",
54796
+ [MediaError.MEDIA_ERR_NETWORK]: "Error: network error.",
54797
+ [MediaError.MEDIA_ERR_DECODE]: "Error: decoding error.",
54798
+ [MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED]: "Error: 404 or unsupported video format."
54799
+ };
54666
54800
  var VideoTileComponent = class _VideoTileComponent {
54667
54801
  host;
54668
54802
  videoElement = null;
54669
54803
  src = input("");
54670
54804
  // Current time of the video element, for displaying for debug info.
54671
54805
  localCurrentTime = signal(0);
54806
+ videoErrorMessage = linkedSignal(() => {
54807
+ this.src();
54808
+ return "";
54809
+ });
54672
54810
  // the main state, injected from parent so it can be easily shared across video players
54673
54811
  videoPlayerState = inject(VideoPlayerState);
54674
54812
  contentEnd = new EventEmitter();
@@ -54699,6 +54837,11 @@ var VideoTileComponent = class _VideoTileComponent {
54699
54837
  scaleFactor = signal(1);
54700
54838
  // signal to hide content until video inited
54701
54839
  showProjectedContent = signal(false);
54840
+ onVideoTagError() {
54841
+ const code = this.videoElement?.nativeElement.error?.code;
54842
+ const videoErrorMessage = code ? videoErrorMessages[code] : "Unknown error, check dev logs.";
54843
+ this.videoErrorMessage.set(videoErrorMessage);
54844
+ }
54702
54845
  onLoadedMetadata() {
54703
54846
  this.videoMetadata.next({
54704
54847
  height: this.videoElement?.nativeElement.videoHeight ?? 1,
@@ -54729,7 +54872,7 @@ var VideoTileComponent = class _VideoTileComponent {
54729
54872
  let _t;
54730
54873
  \u0275\u0275queryRefresh(_t = \u0275\u0275loadQuery()) && (ctx.videoElement = _t.first);
54731
54874
  }
54732
- }, inputs: { src: [1, "src"] }, outputs: { contentEnd: "contentEnd" }, ngContentSelectors: _c13, decls: 6, vars: 3, consts: [["videoEl", ""], [1, "relative", "shadow-lg", "shadow-black/20", "bg-base-200"], ["preload", "auto", "muted", "", 3, "loadedmetadata", "timeupdate", "ended"], ["type", "video/mp4", 3, "src"]], template: function VideoTileComponent_Template(rf, ctx) {
54875
+ }, inputs: { src: [1, "src"] }, outputs: { contentEnd: "contentEnd" }, ngContentSelectors: _c13, decls: 10, vars: 7, consts: [["videoEl", ""], [1, "relative", "shadow-lg", "shadow-black/20", "bg-base-200"], ["preload", "auto", "muted", "", 3, "loadedmetadata", "timeupdate", "ended", "error", "src"], [1, "absolute", "inset-0", "overflow-auto", "p-2"], [1, "text-sm", "text-gray-400", "mt-2"]], template: function VideoTileComponent_Template(rf, ctx) {
54733
54876
  if (rf & 1) {
54734
54877
  const _r1 = \u0275\u0275getCurrentView();
54735
54878
  \u0275\u0275projectionDef();
@@ -54743,25 +54886,39 @@ var VideoTileComponent = class _VideoTileComponent {
54743
54886
  })("ended", function VideoTileComponent_Template_video_ended_1_listener() {
54744
54887
  \u0275\u0275restoreView(_r1);
54745
54888
  return \u0275\u0275resetView(ctx.onEnd());
54889
+ })("error", function VideoTileComponent_Template_video_error_1_listener() {
54890
+ \u0275\u0275restoreView(_r1);
54891
+ return \u0275\u0275resetView(ctx.onVideoTagError());
54746
54892
  });
54747
- \u0275\u0275element(3, "source", 3);
54893
+ \u0275\u0275text(3, " Your browser does not support the video tag. ");
54748
54894
  \u0275\u0275elementEnd();
54749
54895
  \u0275\u0275elementStart(4, "div");
54750
54896
  \u0275\u0275projection(5);
54751
- \u0275\u0275elementEnd()();
54897
+ \u0275\u0275elementEnd();
54898
+ \u0275\u0275elementStart(6, "div", 3);
54899
+ \u0275\u0275text(7);
54900
+ \u0275\u0275elementStart(8, "p", 4);
54901
+ \u0275\u0275text(9);
54902
+ \u0275\u0275elementEnd()()();
54752
54903
  }
54753
54904
  if (rf & 2) {
54754
- \u0275\u0275advance(3);
54755
- \u0275\u0275property("src", ctx.src(), \u0275\u0275sanitizeUrl);
54756
54905
  \u0275\u0275advance();
54906
+ \u0275\u0275property("src", ctx.src(), \u0275\u0275sanitizeUrl);
54907
+ \u0275\u0275advance(3);
54757
54908
  \u0275\u0275classProp("invisible", !ctx.showProjectedContent());
54909
+ \u0275\u0275advance(2);
54910
+ \u0275\u0275classProp("invisible", !ctx.videoErrorMessage());
54911
+ \u0275\u0275advance();
54912
+ \u0275\u0275textInterpolate1(" ", ctx.videoErrorMessage(), " ");
54913
+ \u0275\u0275advance(2);
54914
+ \u0275\u0275textInterpolate1("Source: ", ctx.src(), "");
54758
54915
  }
54759
- }, styles: ["\n\n[_nghost-%COMP%] {\n display: block;\n}\n/*# sourceMappingURL=/static/video-tile.component-XSYKMARQ.css.map */"], changeDetection: 0 });
54916
+ }, styles: ["\n\n[_nghost-%COMP%] {\n display: block;\n}\n/*# sourceMappingURL=/static/video-tile.component-RDL4BSJ4.css.map */"], changeDetection: 0 });
54760
54917
  };
54761
54918
  (() => {
54762
54919
  (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(VideoTileComponent, [{
54763
54920
  type: Component,
54764
- args: [{ selector: "app-video-tile", imports: [], changeDetection: ChangeDetectionStrategy.OnPush, template: '<style>\r\n :host {\r\n /* Display for angular elements is inline by default.\r\n This is a problem when querying the host height, because it ends up being just 0.\r\n */\r\n display: block;\r\n }\r\n</style>\r\n\r\n<div class="relative shadow-lg shadow-black/20 bg-base-200">\r\n <!-- bg is visible during loading state -->\r\n <!-- wrapper to contain video + keypoints -->\r\n <video\r\n #videoEl\r\n preload="auto"\r\n (loadedmetadata)="onLoadedMetadata()"\r\n muted\r\n (timeupdate)="updateLocalCurrentTime()"\r\n (ended)="onEnd()"\r\n >\r\n <source [src]="src()" type="video/mp4" />\r\n </video>\r\n <div [class.invisible]="!showProjectedContent()">\r\n <!-- wrapper to hide content until video inited -->\r\n <ng-content></ng-content>\r\n </div>\r\n</div>\r\n<!--<p>T: {{ localCurrentTime() }}</p>-->\r\n', styles: ["/* angular:styles/component:css;cca0ce7e24de60b661844ed36e56ee3734a21f5d38966246b6b69d25889a81d9;/home/ksikka/lightning-pose-app/web_ui/src/app/components/video-player/video-tile/video-tile.component.html */\n:host {\n display: block;\n}\n/*# sourceMappingURL=/static/video-tile.component-XSYKMARQ.css.map */\n"] }]
54921
+ args: [{ selector: "app-video-tile", imports: [], changeDetection: ChangeDetectionStrategy.OnPush, template: '<style>\n :host {\n /* Display for angular elements is inline by default.\n This is a problem when querying the host height, because it ends up being just 0.\n */\n display: block;\n }\n</style>\n\n<div class="relative shadow-lg shadow-black/20 bg-base-200">\n <!-- bg is visible during loading state -->\n <!-- wrapper to contain video + keypoints -->\n <video\n #videoEl\n preload="auto"\n (loadedmetadata)="onLoadedMetadata()"\n muted\n (timeupdate)="updateLocalCurrentTime()"\n (ended)="onEnd()"\n (error)="onVideoTagError()"\n [src]="src()"\n >\n Your browser does not support the video tag.\n </video>\n <div [class.invisible]="!showProjectedContent()">\n <!-- wrapper to hide content until video inited -->\n <ng-content></ng-content>\n </div>\n\n <div\n class="absolute inset-0 overflow-auto p-2"\n [class.invisible]="!videoErrorMessage()"\n >\n {{ videoErrorMessage() }}\n <p class="text-sm text-gray-400 mt-2">Source: {{ src() }}</p>\n </div>\n</div>\n<!--<p>T: {{ localCurrentTime() }}</p>-->\n', styles: ["/* angular:styles/component:css;cca0ce7e24de60b661844ed36e56ee3734a21f5d38966246b6b69d25889a81d9;/home/ksikka/work/lightning-pose-app/web_ui/src/app/components/video-player/video-tile/video-tile.component.html */\n:host {\n display: block;\n}\n/*# sourceMappingURL=/static/video-tile.component-RDL4BSJ4.css.map */\n"] }]
54765
54922
  }], () => [{ type: ElementRef }], { videoElement: [{
54766
54923
  type: ViewChild,
54767
54924
  args: ["videoEl", { static: true }]
@@ -54770,7 +54927,7 @@ var VideoTileComponent = class _VideoTileComponent {
54770
54927
  }] });
54771
54928
  })();
54772
54929
  (() => {
54773
- (typeof ngDevMode === "undefined" || ngDevMode) && \u0275setClassDebugInfo(VideoTileComponent, { className: "VideoTileComponent", filePath: "src/app/components/video-player/video-tile/video-tile.component.ts", lineNumber: 39 });
54930
+ (typeof ngDevMode === "undefined" || ngDevMode) && \u0275setClassDebugInfo(VideoTileComponent, { className: "VideoTileComponent", filePath: "src/app/components/video-player/video-tile/video-tile.component.ts", lineNumber: 49 });
54774
54931
  })();
54775
54932
 
54776
54933
  // node_modules/@angular/cdk/fesm2022/drag-drop.mjs
@@ -59034,6 +59191,7 @@ var ViewerCenterPanelComponent = class _ViewerCenterPanelComponent {
59034
59191
  csvParser = inject(CsvParserService);
59035
59192
  projectInfoService = inject(ProjectInfoService);
59036
59193
  loadingService = inject(LoadingService);
59194
+ fineVideoService = inject(FineVideoService);
59037
59195
  get currentFrame() {
59038
59196
  return this.videoPlayerState.currentFrameSignal;
59039
59197
  }
@@ -59075,7 +59233,7 @@ var ViewerCenterPanelComponent = class _ViewerCenterPanelComponent {
59075
59233
  });
59076
59234
  return {
59077
59235
  id: sessionView.viewName,
59078
- videoSrc: this.getVideoSrc(sessionView),
59236
+ videoSrc: this.fineVideoService.fineVideoPath(sessionView.videoPath),
59079
59237
  keypoints: filteredKeypoints
59080
59238
  };
59081
59239
  }
@@ -59112,9 +59270,6 @@ var ViewerCenterPanelComponent = class _ViewerCenterPanelComponent {
59112
59270
  const dataDir = this.projectInfoService.projectInfo?.data_dir;
59113
59271
  return dataDir + "/" + sessionKey.replace(/\*/g, view) + ".mp4";
59114
59272
  }
59115
- getVideoSrc(sessionView) {
59116
- return "/app/v0/files/" + sessionView.videoPath;
59117
- }
59118
59273
  async loadSession(sessionKey) {
59119
59274
  this.sessionKey.set(sessionKey);
59120
59275
  this.widgetModels.set([]);
@@ -59194,7 +59349,7 @@ var ViewerCenterPanelComponent = class _ViewerCenterPanelComponent {
59194
59349
  VideoPlayerControlsComponent,
59195
59350
  VideoTileComponent,
59196
59351
  KeypointContainerComponent
59197
- ], changeDetection: ChangeDetectionStrategy.OnPush, template: '<div class="flex flex-col h-full">\r\n <!-- Video content area -->\r\n <div\r\n class="w-full grow flex flex-wrap overflow-y-auto gap-4 *:max-w-xs justify-start items-start content-start p-4"\r\n >\r\n <!-- `track videosrc` makes the widget component get\r\n destroyed and recreated if videoSrc changes. Reactively\r\n changing videoSrc of an existing video does not work unfortunately. -->\r\n @for (w of filteredWidgetModels(); track w.videoSrc) {\r\n <div class="panel !border-none">\r\n <div class="px-2 py-1 text-sm flex justify-between">\r\n <h3>{{ w.id }}</h3>\r\n <span\r\n class="btn btn-circle btn-xs material-icons !text-sm"\r\n (click)="onWidgetCloseClick(w)"\r\n >close</span\r\n >\r\n </div>\r\n <app-video-tile [src]="w.videoSrc" class="w-auto">\r\n <app-keypoint-container\r\n [labelerMode]="false"\r\n [keypointModels]="w.keypoints()"\r\n ></app-keypoint-container>\r\n </app-video-tile>\r\n </div>\r\n }\r\n </div>\r\n\r\n <app-video-player-controls></app-video-player-controls>\r\n</div>\r\n' }]
59352
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: '<div class="flex flex-col h-full">\n <!-- Video content area -->\n <div\n class="w-full grow flex flex-wrap overflow-y-auto gap-4 *:max-w-xs justify-start items-start content-start p-4"\n >\n <!-- `track videosrc` makes the widget component get\n destroyed and recreated if videoSrc changes. Reactively\n changing videoSrc of an existing video does not work unfortunately. -->\n @for (w of filteredWidgetModels(); track w.videoSrc) {\n <div class="panel !border-none">\n <div class="px-2 py-1 text-sm flex justify-between">\n <h3>{{ w.id }}</h3>\n <span\n class="btn btn-circle btn-xs material-icons !text-sm"\n (click)="onWidgetCloseClick(w)"\n >close</span\n >\n </div>\n <app-video-tile [src]="w.videoSrc" class="w-auto">\n <app-keypoint-container\n [labelerMode]="false"\n [keypointModels]="w.keypoints()"\n ></app-keypoint-container>\n </app-video-tile>\n </div>\n }\n </div>\n\n <app-video-player-controls></app-video-player-controls>\n</div>\n' }]
59198
59353
  }], null, { viewSettings: [{
59199
59354
  type: Input
59200
59355
  }] });
@@ -59491,7 +59646,7 @@ var ViewerPageComponent = class _ViewerPageComponent {
59491
59646
  ViewerSessionsPanelComponent,
59492
59647
  ViewerCenterPanelComponent,
59493
59648
  LoadingBarComponent
59494
- ], styles: ["\n\n[_nghost-%COMP%] {\n flex-grow: 1;\n display: flex;\n}\n/*# sourceMappingURL=/static/viewer-page.component-MRTIUFL2.css.map */"], changeDetection: 0 });
59649
+ ], styles: ["\n\n[_nghost-%COMP%] {\n flex-grow: 1;\n display: flex;\n}\n/*# sourceMappingURL=/static/viewer-page.component-KDHT6XH5.css.map */"], changeDetection: 0 });
59495
59650
  };
59496
59651
  (() => {
59497
59652
  (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(ViewerPageComponent, [{
@@ -59500,116 +59655,116 @@ var ViewerPageComponent = class _ViewerPageComponent {
59500
59655
  ViewerSessionsPanelComponent,
59501
59656
  ViewerCenterPanelComponent,
59502
59657
  LoadingBarComponent
59503
- ], changeDetection: ChangeDetectionStrategy.OnPush, providers: [VideoPlayerState, ViewSettings], template: `<style>\r
59504
- :host {\r
59505
- flex-grow: 1;\r
59506
- display: flex;\r
59507
- }\r
59508
- </style>\r
59509
- \r
59510
- @if (!isIniting()) {\r
59511
- <div class="grow flex flex-row items-stretch">\r
59512
- <!-- Left pane -->\r
59513
- <div\r
59514
- class="bg-base-200 w-60 xl:w-80 flex flex-col justify-between shadow-lg"\r
59515
- >\r
59516
- <div class="p-4">\r
59517
- <app-sessions-panel></app-sessions-panel>\r
59518
- </div>\r
59519
- <app-loading-bar class="h-20 p-4"></app-loading-bar>\r
59520
- </div>\r
59521
- \r
59522
- <!-- Center content -->\r
59523
- <app-viewer-center-panel\r
59524
- class="grow z-30"\r
59525
- [viewSettings]="viewSettings"\r
59526
- ></app-viewer-center-panel>\r
59527
- \r
59528
- <!-- Right pane -->\r
59529
- <div class="shrink-0 w-40 xl:w-60 h-full">\r
59530
- <div class="panel m-4">\r
59531
- <div>\r
59532
- <!-- panel header -->\r
59533
- <h1 class="px-2 mb-1">Views</h1>\r
59534
- </div>\r
59535
- <div class="panel-content max-h-60 overflow-y-auto">\r
59536
- <ul>\r
59537
- @for (cam of allViews; track cam) {\r
59538
- <li class="p-1 flex justify-between items-center">\r
59539
- <span>{{ cam }}</span>\r
59540
- <input\r
59541
- type="checkbox"\r
59542
- [checked]="viewSelectionModel.isSelected(cam)"\r
59543
- class="checkbox checkbox-xs"\r
59544
- (change)="onViewCheckboxChange($event, cam)"\r
59545
- />\r
59546
- </li>\r
59547
- }\r
59548
- </ul>\r
59549
- </div>\r
59550
- </div>\r
59551
- \r
59552
- <div class="panel m-4">\r
59553
- <div>\r
59554
- <!-- panel header -->\r
59555
- <h1 class="px-2 mb-1">Keypoints</h1>\r
59556
- </div>\r
59557
- <div class="panel-content max-h-60 overflow-y-auto">\r
59558
- <ul>\r
59559
- @for (kp of projectInfoService.allKeypoints(); track kp) {\r
59560
- <li class="p-1 flex justify-between items-center">\r
59561
- <span>{{ kp }}</span>\r
59562
- <input\r
59563
- type="checkbox"\r
59564
- [checked]="keypointSelectionModel.isSelected(kp)"\r
59565
- class="checkbox checkbox-xs"\r
59566
- (change)="onKeypointCheckboxChange($event, kp)"\r
59567
- />\r
59568
- </li>\r
59569
- }\r
59570
- </ul>\r
59571
- </div>\r
59572
- </div>\r
59573
- \r
59574
- <div class="panel m-4">\r
59575
- <div>\r
59576
- <!-- panel header -->\r
59577
- <h1 class="px-2 mb-1">Models</h1>\r
59578
- </div>\r
59579
- <div class="panel-content max-h-60 overflow-y-visible">\r
59580
- <ul>\r
59581
- @let modelSelectors =\r
59582
- viewSettings.modelsShown().length < 2\r
59583
- ? viewSettings.modelsShown().concat([noneOption])\r
59584
- : viewSettings.modelsShown();\r
59585
- <!-- it's important to track index, not model.\r
59586
- if we put model here, something weird happens when you clear all models and then set a model. -->\r
59587
- @for (model of modelSelectors; track $index) {\r
59588
- @let modelIndex = $index;\r
59589
- <li class="p-1 w-full">\r
59590
- <select\r
59591
- class="select select-sm w-full flex-grow"\r
59592
- [class.border-red-300]="modelIndex === 0"\r
59593
- [class.border-green-300]="modelIndex === 1"\r
59594
- (change)="onModelDropdownItemClick(modelIndex, $event)"\r
59595
- >\r
59596
- @let options =\r
59597
- [noneOption].concat(projectInfoService.allModels());\r
59598
- @for (dropdownItem of options; track dropdownItem) {\r
59599
- <option [selected]="dropdownItem === model">\r
59600
- {{ dropdownItem }}\r
59601
- </option>\r
59602
- }\r
59603
- </select>\r
59604
- </li>\r
59605
- }\r
59606
- </ul>\r
59607
- </div>\r
59608
- </div>\r
59609
- </div>\r
59610
- </div>\r
59611
- }\r
59612
- `, styles: ["/* angular:styles/component:css;1fa15f7a0ae1b0259233282b2d49fb361834bf7fd726c58584c186240f9d8f04;/home/ksikka/lightning-pose-app/web_ui/src/app/viewer/viewer-page/viewer-page.component.html */\n:host {\n flex-grow: 1;\n display: flex;\n}\n/*# sourceMappingURL=/static/viewer-page.component-MRTIUFL2.css.map */\n"] }]
59658
+ ], changeDetection: ChangeDetectionStrategy.OnPush, providers: [VideoPlayerState, ViewSettings], template: `<style>
59659
+ :host {
59660
+ flex-grow: 1;
59661
+ display: flex;
59662
+ }
59663
+ </style>
59664
+
59665
+ @if (!isIniting()) {
59666
+ <div class="grow flex flex-row items-stretch">
59667
+ <!-- Left pane -->
59668
+ <div
59669
+ class="bg-base-200 w-60 xl:w-80 flex flex-col justify-between shadow-lg"
59670
+ >
59671
+ <div class="p-4">
59672
+ <app-sessions-panel></app-sessions-panel>
59673
+ </div>
59674
+ <app-loading-bar class="h-20 p-4"></app-loading-bar>
59675
+ </div>
59676
+
59677
+ <!-- Center content -->
59678
+ <app-viewer-center-panel
59679
+ class="grow z-30"
59680
+ [viewSettings]="viewSettings"
59681
+ ></app-viewer-center-panel>
59682
+
59683
+ <!-- Right pane -->
59684
+ <div class="shrink-0 w-40 xl:w-60 h-full">
59685
+ <div class="panel m-4">
59686
+ <div>
59687
+ <!-- panel header -->
59688
+ <h1 class="px-2 mb-1">Views</h1>
59689
+ </div>
59690
+ <div class="panel-content max-h-60 overflow-y-auto">
59691
+ <ul>
59692
+ @for (cam of allViews; track cam) {
59693
+ <li class="p-1 flex justify-between items-center">
59694
+ <span>{{ cam }}</span>
59695
+ <input
59696
+ type="checkbox"
59697
+ [checked]="viewSelectionModel.isSelected(cam)"
59698
+ class="checkbox checkbox-xs"
59699
+ (change)="onViewCheckboxChange($event, cam)"
59700
+ />
59701
+ </li>
59702
+ }
59703
+ </ul>
59704
+ </div>
59705
+ </div>
59706
+
59707
+ <div class="panel m-4">
59708
+ <div>
59709
+ <!-- panel header -->
59710
+ <h1 class="px-2 mb-1">Keypoints</h1>
59711
+ </div>
59712
+ <div class="panel-content max-h-60 overflow-y-auto">
59713
+ <ul>
59714
+ @for (kp of projectInfoService.allKeypoints(); track kp) {
59715
+ <li class="p-1 flex justify-between items-center">
59716
+ <span>{{ kp }}</span>
59717
+ <input
59718
+ type="checkbox"
59719
+ [checked]="keypointSelectionModel.isSelected(kp)"
59720
+ class="checkbox checkbox-xs"
59721
+ (change)="onKeypointCheckboxChange($event, kp)"
59722
+ />
59723
+ </li>
59724
+ }
59725
+ </ul>
59726
+ </div>
59727
+ </div>
59728
+
59729
+ <div class="panel m-4">
59730
+ <div>
59731
+ <!-- panel header -->
59732
+ <h1 class="px-2 mb-1">Models</h1>
59733
+ </div>
59734
+ <div class="panel-content max-h-60 overflow-y-visible">
59735
+ <ul>
59736
+ @let modelSelectors =
59737
+ viewSettings.modelsShown().length < 2
59738
+ ? viewSettings.modelsShown().concat([noneOption])
59739
+ : viewSettings.modelsShown();
59740
+ <!-- it's important to track index, not model.
59741
+ if we put model here, something weird happens when you clear all models and then set a model. -->
59742
+ @for (model of modelSelectors; track $index) {
59743
+ @let modelIndex = $index;
59744
+ <li class="p-1 w-full">
59745
+ <select
59746
+ class="select select-sm w-full flex-grow"
59747
+ [class.border-red-300]="modelIndex === 0"
59748
+ [class.border-green-300]="modelIndex === 1"
59749
+ (change)="onModelDropdownItemClick(modelIndex, $event)"
59750
+ >
59751
+ @let options =
59752
+ [noneOption].concat(projectInfoService.allModels());
59753
+ @for (dropdownItem of options; track dropdownItem) {
59754
+ <option [selected]="dropdownItem === model">
59755
+ {{ dropdownItem }}
59756
+ </option>
59757
+ }
59758
+ </select>
59759
+ </li>
59760
+ }
59761
+ </ul>
59762
+ </div>
59763
+ </div>
59764
+ </div>
59765
+ </div>
59766
+ }
59767
+ `, styles: ["/* angular:styles/component:css;1fa15f7a0ae1b0259233282b2d49fb361834bf7fd726c58584c186240f9d8f04;/home/ksikka/work/lightning-pose-app/web_ui/src/app/viewer/viewer-page/viewer-page.component.html */\n:host {\n flex-grow: 1;\n display: flex;\n}\n/*# sourceMappingURL=/static/viewer-page.component-KDHT6XH5.css.map */\n"] }]
59613
59768
  }], () => [], { sessionKey: [{
59614
59769
  type: Input
59615
59770
  }] });
@@ -66177,7 +66332,7 @@ view2
66177
66332
  \u0275\u0275advance();
66178
66333
  \u0275\u0275property("disabled", !ctx.projectInfoForm.dirty);
66179
66334
  }
66180
- }, dependencies: [ReactiveFormsModule, \u0275NgNoValidate, DefaultValueAccessor, NgControlStatus, NgControlStatusGroup, FormGroupDirective, FormControlName], styles: ["\n\n[_ngcontent-%COMP%]::placeholder {\n color: color-mix(in oklch, currentColor 50%, #0000) !important;\n}\n/*# sourceMappingURL=/static/project-settings.component-5IRK7U7U.css.map */"], changeDetection: 0 });
66335
+ }, dependencies: [ReactiveFormsModule, \u0275NgNoValidate, DefaultValueAccessor, NgControlStatus, NgControlStatusGroup, FormGroupDirective, FormControlName], styles: ["\n\n[_ngcontent-%COMP%]::placeholder {\n color: color-mix(in oklch, currentColor 50%, #0000) !important;\n}\n/*# sourceMappingURL=/static/project-settings.component-HKHIVUJR.css.map */"], changeDetection: 0 });
66181
66336
  };
66182
66337
  (() => {
66183
66338
  (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(ProjectSettingsComponent, [{
@@ -66261,7 +66416,7 @@ view2
66261
66416
  </button>
66262
66417
  </div>
66263
66418
  </form>
66264
- `, styles: ["/* angular:styles/component:css;5c3484fa86afc1ad339d983c727d8801928706c064dd49a4001f42c8eac3d364;/home/ksikka/lightning-pose-app/web_ui/src/app/project-settings/project-settings.component.html */\n::placeholder {\n color: color-mix(in oklch, currentColor 50%, #0000) !important;\n}\n/*# sourceMappingURL=/static/project-settings.component-5IRK7U7U.css.map */\n"] }]
66419
+ `, styles: ["/* angular:styles/component:css;5c3484fa86afc1ad339d983c727d8801928706c064dd49a4001f42c8eac3d364;/home/ksikka/work/lightning-pose-app/web_ui/src/app/project-settings/project-settings.component.html */\n::placeholder {\n color: color-mix(in oklch, currentColor 50%, #0000) !important;\n}\n/*# sourceMappingURL=/static/project-settings.component-HKHIVUJR.css.map */\n"] }]
66265
66420
  }], () => [], null);
66266
66421
  })();
66267
66422
  (() => {
@@ -66270,31 +66425,38 @@ view2
66270
66425
 
66271
66426
  // src/app/app.component.ts
66272
66427
  var _c08 = ["settingsDialog"];
66273
- function AppComponent_Conditional_18_Template(rf, ctx) {
66428
+ function AppComponent_Conditional_14_Template(rf, ctx) {
66274
66429
  if (rf & 1) {
66275
- \u0275\u0275element(0, "router-outlet");
66430
+ \u0275\u0275elementStart(0, "div", 9);
66431
+ \u0275\u0275element(1, "span", 15);
66432
+ \u0275\u0275elementEnd();
66276
66433
  }
66277
66434
  }
66278
66435
  function AppComponent_Conditional_19_Template(rf, ctx) {
66279
66436
  if (rf & 1) {
66280
- \u0275\u0275element(0, "app-project-settings", 12);
66437
+ \u0275\u0275element(0, "router-outlet");
66438
+ }
66439
+ }
66440
+ function AppComponent_Conditional_20_Template(rf, ctx) {
66441
+ if (rf & 1) {
66442
+ \u0275\u0275element(0, "app-project-settings", 13);
66281
66443
  }
66282
66444
  if (rf & 2) {
66283
66445
  \u0275\u0275property("setupMode", true);
66284
66446
  }
66285
66447
  }
66286
- function AppComponent_Conditional_22_Template(rf, ctx) {
66448
+ function AppComponent_Conditional_23_Template(rf, ctx) {
66287
66449
  if (rf & 1) {
66288
66450
  const _r2 = \u0275\u0275getCurrentView();
66289
- \u0275\u0275elementStart(0, "app-project-settings", 14);
66290
- \u0275\u0275listener("done", function AppComponent_Conditional_22_Template_app_project_settings_done_0_listener() {
66451
+ \u0275\u0275elementStart(0, "app-project-settings", 16);
66452
+ \u0275\u0275listener("done", function AppComponent_Conditional_23_Template_app_project_settings_done_0_listener() {
66291
66453
  \u0275\u0275restoreView(_r2);
66292
66454
  const ctx_r2 = \u0275\u0275nextContext();
66293
66455
  return \u0275\u0275resetView(ctx_r2.settingsDialogOpen.set(false));
66294
66456
  });
66295
66457
  \u0275\u0275elementEnd();
66296
- \u0275\u0275elementStart(1, "div", 15)(2, "button", 16);
66297
- \u0275\u0275listener("click", function AppComponent_Conditional_22_Template_button_click_2_listener() {
66458
+ \u0275\u0275elementStart(1, "div", 17)(2, "button", 18);
66459
+ \u0275\u0275listener("click", function AppComponent_Conditional_23_Template_button_click_2_listener() {
66298
66460
  \u0275\u0275restoreView(_r2);
66299
66461
  const ctx_r2 = \u0275\u0275nextContext();
66300
66462
  return \u0275\u0275resetView(ctx_r2.settingsDialogOpen.set(false));
@@ -66305,6 +66467,7 @@ function AppComponent_Conditional_22_Template(rf, ctx) {
66305
66467
  }
66306
66468
  var AppComponent = class _AppComponent {
66307
66469
  projectInfoService = inject(ProjectInfoService);
66470
+ fineVideoService = inject(FineVideoService);
66308
66471
  // Whether the required initial setup has been done.
66309
66472
  // (Setting data directory, model directory, views).
66310
66473
  projectInfoRequestCompleted = signal(false);
@@ -66343,7 +66506,7 @@ var AppComponent = class _AppComponent {
66343
66506
  if (rf & 2) {
66344
66507
  \u0275\u0275queryAdvance();
66345
66508
  }
66346
- }, decls: 23, vars: 4, consts: [["settingsDialog", ""], [1, "navbar", "bg-base-200", "shadow-lg"], [1, "navbar-start"], [1, "text-lg", "font-semibold"], [1, "navbar-center"], [1, "menu", "menu-horizontal", "p-0"], ["routerLink", "/viewer", "routerLinkActive", "menu-active", "ariaCurrentWhenActive", "page"], ["routerLink", "/labeler", "routerLinkActive", "menu-active", "ariaCurrentWhenActive", "page"], [1, "navbar-end"], ["tabindex", "0", 1, "btn", "btn-ghost", 3, "click", "keydown.enter"], [1, "material-icons"], [1, "flex-grow", "flex", "min-h-0"], [1, "w-lg", "mt-8", 3, "setupMode"], [1, "modal"], [1, "modal-box", 3, "done"], [1, "modal-backdrop"], [3, "click"]], template: function AppComponent_Template(rf, ctx) {
66509
+ }, decls: 24, vars: 5, consts: [["settingsDialog", ""], [1, "navbar", "bg-base-200", "shadow-lg"], [1, "navbar-start"], [1, "text-lg", "font-semibold"], [1, "navbar-center"], [1, "menu", "menu-horizontal", "p-0"], ["routerLink", "/viewer", "routerLinkActive", "menu-active", "ariaCurrentWhenActive", "page"], ["routerLink", "/labeler", "routerLinkActive", "menu-active", "ariaCurrentWhenActive", "page"], [1, "navbar-end"], ["data-tip", "Transcoding videos...", 1, "tooltip", "tooltip-left"], ["tabindex", "0", 1, "btn", "btn-ghost", 3, "click", "keydown.enter"], [1, "material-icons"], [1, "flex-grow", "flex", "min-h-0"], [1, "w-lg", "mt-8", 3, "setupMode"], [1, "modal"], [1, "loading", "loading-bars", "loading-xs"], [1, "modal-box", 3, "done"], [1, "modal-backdrop"], [3, "click"]], template: function AppComponent_Template(rf, ctx) {
66347
66510
  if (rf & 1) {
66348
66511
  const _r1 = \u0275\u0275getCurrentView();
66349
66512
  \u0275\u0275elementStart(0, "header")(1, "nav", 1)(2, "div", 2)(3, "span", 3);
@@ -66355,38 +66518,42 @@ var AppComponent = class _AppComponent {
66355
66518
  \u0275\u0275elementStart(10, "li")(11, "a", 7);
66356
66519
  \u0275\u0275text(12, "Labeler");
66357
66520
  \u0275\u0275elementEnd()()()();
66358
- \u0275\u0275elementStart(13, "div", 8)(14, "button", 9);
66359
- \u0275\u0275listener("click", function AppComponent_Template_button_click_14_listener() {
66521
+ \u0275\u0275elementStart(13, "div", 8);
66522
+ \u0275\u0275template(14, AppComponent_Conditional_14_Template, 2, 0, "div", 9);
66523
+ \u0275\u0275elementStart(15, "button", 10);
66524
+ \u0275\u0275listener("click", function AppComponent_Template_button_click_15_listener() {
66360
66525
  \u0275\u0275restoreView(_r1);
66361
66526
  return \u0275\u0275resetView(ctx.settingsDialogOpen.set(true));
66362
- })("keydown.enter", function AppComponent_Template_button_keydown_enter_14_listener() {
66527
+ })("keydown.enter", function AppComponent_Template_button_keydown_enter_15_listener() {
66363
66528
  \u0275\u0275restoreView(_r1);
66364
66529
  return \u0275\u0275resetView(ctx.settingsDialogOpen.set(true));
66365
66530
  });
66366
- \u0275\u0275elementStart(15, "span", 10);
66367
- \u0275\u0275text(16, "settings");
66531
+ \u0275\u0275elementStart(16, "span", 11);
66532
+ \u0275\u0275text(17, "settings");
66368
66533
  \u0275\u0275elementEnd()()()()();
66369
- \u0275\u0275elementStart(17, "main", 11);
66370
- \u0275\u0275template(18, AppComponent_Conditional_18_Template, 1, 0, "router-outlet")(19, AppComponent_Conditional_19_Template, 1, 1, "app-project-settings", 12);
66534
+ \u0275\u0275elementStart(18, "main", 12);
66535
+ \u0275\u0275template(19, AppComponent_Conditional_19_Template, 1, 0, "router-outlet")(20, AppComponent_Conditional_20_Template, 1, 1, "app-project-settings", 13);
66371
66536
  \u0275\u0275elementEnd();
66372
- \u0275\u0275elementStart(20, "dialog", 13, 0);
66373
- \u0275\u0275template(22, AppComponent_Conditional_22_Template, 4, 0);
66537
+ \u0275\u0275elementStart(21, "dialog", 14, 0);
66538
+ \u0275\u0275template(23, AppComponent_Conditional_23_Template, 4, 0);
66374
66539
  \u0275\u0275elementEnd();
66375
66540
  }
66376
66541
  if (rf & 2) {
66377
- \u0275\u0275advance(17);
66542
+ \u0275\u0275advance(14);
66543
+ \u0275\u0275conditional(ctx.fineVideoService.numPendingTranscodeTasks().pending > 0 ? 14 : -1);
66544
+ \u0275\u0275advance(4);
66378
66545
  \u0275\u0275classProp("justify-center", !ctx.hasBeenSetup());
66379
66546
  \u0275\u0275advance();
66380
- \u0275\u0275conditional(ctx.hasBeenSetup() ? 18 : 19);
66547
+ \u0275\u0275conditional(ctx.hasBeenSetup() ? 19 : 20);
66381
66548
  \u0275\u0275advance(4);
66382
- \u0275\u0275conditional(ctx.settingsDialogOpen() ? 22 : -1);
66549
+ \u0275\u0275conditional(ctx.settingsDialogOpen() ? 23 : -1);
66383
66550
  }
66384
66551
  }, dependencies: [
66385
66552
  RouterOutlet,
66386
66553
  RouterLink,
66387
66554
  RouterLinkActive,
66388
66555
  ProjectSettingsComponent
66389
- ], styles: ["\n\n[_nghost-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 100vh;\n}\n.navbar[_ngcontent-%COMP%] {\n min-height: 3rem;\n}\n/*# sourceMappingURL=/static/app.component-UHVEDPZR.css.map */"], changeDetection: 0 });
66556
+ ], styles: ["\n\n[_nghost-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 100vh;\n}\n.navbar[_ngcontent-%COMP%] {\n min-height: 3rem;\n}\n/*# sourceMappingURL=/static/app.component-UAQUAGNZ.css.map */"], changeDetection: 0 });
66390
66557
  };
66391
66558
  (() => {
66392
66559
  (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(AppComponent, [{
@@ -66396,13 +66563,13 @@ var AppComponent = class _AppComponent {
66396
66563
  RouterLink,
66397
66564
  RouterLinkActive,
66398
66565
  ProjectSettingsComponent
66399
- ], changeDetection: ChangeDetectionStrategy.OnPush, template: '<style>\n :host {\n display: flex;\n flex-direction: column;\n height: 100vh;\n }\n .navbar {\n min-height: 3rem; /* reduce from default of 4rem */\n }\n</style>\n<header>\n <nav class="navbar bg-base-200 shadow-lg">\n <div class="navbar-start">\n <span class="text-lg font-semibold">Lightning Pose</span>\n </div>\n\n <div class="navbar-center">\n <ul class="menu menu-horizontal p-0">\n <li>\n <a\n routerLink="/viewer"\n routerLinkActive="menu-active"\n ariaCurrentWhenActive="page"\n >Viewer</a\n >\n </li>\n <li>\n <a\n routerLink="/labeler"\n routerLinkActive="menu-active"\n ariaCurrentWhenActive="page"\n >Labeler</a\n >\n </li>\n </ul>\n </div>\n\n <div class="navbar-end">\n <button\n class="btn btn-ghost"\n tabindex="0"\n (click)="settingsDialogOpen.set(true)"\n (keydown.enter)="settingsDialogOpen.set(true)"\n >\n <span class="material-icons">settings</span>\n </button>\n </div>\n </nav>\n</header>\n\n<!-- Set min-height: 0 to allow it to shrink if its content is too large.\n Default min-height for flex items is auto. -->\n<main class="flex-grow flex min-h-0" [class.justify-center]="!hasBeenSetup()">\n @if (hasBeenSetup()) {\n <router-outlet />\n } @else {\n <app-project-settings\n [setupMode]="true"\n class="w-lg mt-8"\n ></app-project-settings>\n }\n</main>\n\n<!-- Settings dialog -->\n<dialog #settingsDialog class="modal">\n @if (settingsDialogOpen()) {\n <app-project-settings\n class="modal-box"\n (done)="settingsDialogOpen.set(false)"\n ></app-project-settings>\n\n <!-- Makes the dialog close when clicked from outside. -->\n <div class="modal-backdrop">\n <button (click)="settingsDialogOpen.set(false)">close</button>\n </div>\n }\n</dialog>\n', styles: ["/* angular:styles/component:css;22d8514f1dd5b50f33b3fb93fdb69668f78eeb349bd672e238e3ac9acfbbda19;/home/ksikka/lightning-pose-app/web_ui/src/app/app.component.html */\n:host {\n display: flex;\n flex-direction: column;\n height: 100vh;\n}\n.navbar {\n min-height: 3rem;\n}\n/*# sourceMappingURL=/static/app.component-UHVEDPZR.css.map */\n"] }]
66566
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: '<style>\n :host {\n display: flex;\n flex-direction: column;\n height: 100vh;\n }\n .navbar {\n min-height: 3rem; /* reduce from default of 4rem */\n }\n</style>\n<header>\n <nav class="navbar bg-base-200 shadow-lg">\n <div class="navbar-start">\n <span class="text-lg font-semibold">Lightning Pose</span>\n </div>\n\n <div class="navbar-center">\n <ul class="menu menu-horizontal p-0">\n <li>\n <a\n routerLink="/viewer"\n routerLinkActive="menu-active"\n ariaCurrentWhenActive="page"\n >Viewer</a\n >\n </li>\n <li>\n <a\n routerLink="/labeler"\n routerLinkActive="menu-active"\n ariaCurrentWhenActive="page"\n >Labeler</a\n >\n </li>\n </ul>\n </div>\n\n <div class="navbar-end">\n @if (fineVideoService.numPendingTranscodeTasks().pending > 0) {\n <div class="tooltip tooltip-left" data-tip="Transcoding videos...">\n <span class="loading loading-bars loading-xs"></span>\n </div>\n }\n\n <button\n class="btn btn-ghost"\n tabindex="0"\n (click)="settingsDialogOpen.set(true)"\n (keydown.enter)="settingsDialogOpen.set(true)"\n >\n <span class="material-icons">settings</span>\n </button>\n </div>\n </nav>\n</header>\n\n<!-- Set min-height: 0 to allow it to shrink if its content is too large.\n Default min-height for flex items is auto. -->\n<main class="flex-grow flex min-h-0" [class.justify-center]="!hasBeenSetup()">\n @if (hasBeenSetup()) {\n <router-outlet />\n } @else {\n <app-project-settings\n [setupMode]="true"\n class="w-lg mt-8"\n ></app-project-settings>\n }\n</main>\n\n<!-- Settings dialog -->\n<dialog #settingsDialog class="modal">\n @if (settingsDialogOpen()) {\n <app-project-settings\n class="modal-box"\n (done)="settingsDialogOpen.set(false)"\n ></app-project-settings>\n\n <!-- Makes the dialog close when clicked from outside. -->\n <div class="modal-backdrop">\n <button (click)="settingsDialogOpen.set(false)">close</button>\n </div>\n }\n</dialog>\n', styles: ["/* angular:styles/component:css;22d8514f1dd5b50f33b3fb93fdb69668f78eeb349bd672e238e3ac9acfbbda19;/home/ksikka/work/lightning-pose-app/web_ui/src/app/app.component.html */\n:host {\n display: flex;\n flex-direction: column;\n height: 100vh;\n}\n.navbar {\n min-height: 3rem;\n}\n/*# sourceMappingURL=/static/app.component-UAQUAGNZ.css.map */\n"] }]
66400
66567
  }], () => [], null);
66401
66568
  })();
66402
66569
  (() => {
66403
- (typeof ngDevMode === "undefined" || ngDevMode) && \u0275setClassDebugInfo(AppComponent, { className: "AppComponent", filePath: "src/app/app.component.ts", lineNumber: 27 });
66570
+ (typeof ngDevMode === "undefined" || ngDevMode) && \u0275setClassDebugInfo(AppComponent, { className: "AppComponent", filePath: "src/app/app.component.ts", lineNumber: 28 });
66404
66571
  })();
66405
66572
 
66406
66573
  // src/main.ts
66407
66574
  bootstrapApplication(AppComponent, appConfig).catch((err) => console.error(err));
66408
- //# sourceMappingURL=main-LJHMLKBL.js.map
66575
+ //# sourceMappingURL=main-6XYUWDGZ.js.map