summaryrefslogtreecommitdiff
path: root/apps/web/src/components/editor/timeline/timeline-cache-indicator.tsx
blob: 7def2985be7acb059a4283e5ed2db5e38a67bf01 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
"use client";

import { cn } from "@/lib/utils";
import { TIMELINE_CONSTANTS } from "@/constants/timeline-constants";
import { TimelineTrack } from "@/types/timeline";
import { MediaFile } from "@/types/media";
import { TProject } from "@/types/project";
import { useSceneStore } from "@/stores/scene-store";

interface CacheSegment {
  startTime: number;
  endTime: number;
  cached: boolean;
}

interface TimelineCacheIndicatorProps {
  duration: number;
  zoomLevel: number;
  tracks: TimelineTrack[];
  mediaFiles: MediaFile[];
  activeProject: TProject | null;
  getRenderStatus: (
    time: number,
    tracks: TimelineTrack[],
    mediaFiles: MediaFile[],
    activeProject: TProject | null,
    sceneId?: string
  ) => "cached" | "not-cached";
}

export function TimelineCacheIndicator({
  duration,
  zoomLevel,
  tracks,
  mediaFiles,
  activeProject,
  getRenderStatus,
}: TimelineCacheIndicatorProps) {
  const { currentScene } = useSceneStore();

  // Calculate cache segments by sampling the timeline
  const calculateCacheSegments = (): CacheSegment[] => {
    const segments: CacheSegment[] = [];
    const sampleRate = 10; // Sample every 0.1 seconds
    const totalSamples = Math.ceil(duration * sampleRate);

    if (totalSamples === 0) {
      return [{ startTime: 0, endTime: duration, cached: false }];
    }

    let currentSegment: CacheSegment | null = null;

    for (let i = 0; i <= totalSamples; i++) {
      const time = i / sampleRate;
      const cached =
        getRenderStatus(
          time,
          tracks,
          mediaFiles,
          activeProject,
          currentScene?.id
        ) === "cached";

      if (!currentSegment) {
        // Start first segment
        currentSegment = {
          startTime: time,
          endTime: time,
          cached,
        };
      } else if (currentSegment.cached === cached) {
        // Extend current segment
        currentSegment.endTime = time;
      } else {
        // Finish current segment and start new one
        segments.push(currentSegment);
        currentSegment = {
          startTime: time,
          endTime: time,
          cached,
        };
      }
    }

    // Add the last segment
    if (currentSegment) {
      currentSegment.endTime = duration;
      segments.push(currentSegment);
    }

    return segments;
  };

  const cacheSegments = calculateCacheSegments();

  return (
    <div className="absolute top-0 left-0 right-0 h-px z-10 pointer-events-none">
      {cacheSegments.map((segment, index) => {
        const startX =
          segment.startTime * TIMELINE_CONSTANTS.PIXELS_PER_SECOND * zoomLevel;
        const endX =
          segment.endTime * TIMELINE_CONSTANTS.PIXELS_PER_SECOND * zoomLevel;
        const width = endX - startX;

        return (
          <div
            key={index}
            className={cn(
              "absolute top-0 h-px",
              segment.cached ? "bg-primary" : "bg-transparent"
            )}
            style={{
              left: `${startX}px`,
              width: `${width}px`,
            }}
            title={
              segment.cached
                ? "Cached (fast playback)"
                : "Not cached (will render)"
            }
          />
        );
      })}
    </div>
  );
}