diff options
Diffstat (limited to 'apps/web/src/timeline/components/timeline-element.tsx')
| -rw-r--r-- | apps/web/src/timeline/components/timeline-element.tsx | 78 |
1 files changed, 69 insertions, 9 deletions
diff --git a/apps/web/src/timeline/components/timeline-element.tsx b/apps/web/src/timeline/components/timeline-element.tsx index 9dfeab8b..b25a7a28 100644 --- a/apps/web/src/timeline/components/timeline-element.tsx +++ b/apps/web/src/timeline/components/timeline-element.tsx @@ -5,6 +5,8 @@ import { useEditor } from "@/editor/use-editor"; import { FEATURES } from "@/features"; import { useAssetsPanelStore } from "@/components/editor/panels/assets/assets-panel-store"; import { AudioWaveform, WAVEFORM_GAIN_SAMPLE_COUNT } from "./audio-waveform"; +import { VideoFilmstrip } from "./video-filmstrip"; +import { MEDIA_TILE_ASPECT_RATIO } from "@/services/thumbnail-cache/tiling"; import { AudioVolumeLine } from "./audio-volume-line"; import { useElementPreview } from "@/timeline/hooks/use-element-preview"; import { @@ -95,7 +97,6 @@ const KEYFRAME_INDICATOR_MIN_WIDTH_PX = 40; const ELEMENT_RING_WIDTH_PX = 1.5; const PixelsPerSecondContext = createContext<number | null>(null); -const THUMBNAIL_ASPECT_RATIO = 16 / 9; interface KeyframeIndicator { time: MediaTime; @@ -1082,20 +1083,78 @@ function EffectsButton({ ); } -function TiledMediaContent({ +function VideoElementContent({ element, track, }: { - element: VideoElement | ImageElement; + element: VideoElement; + track: TimelineTrack; +}) { + const pixelsPerSecond = useContext(PixelsPerSecondContext); + if (pixelsPerSecond === null) { + throw new Error( + "VideoElementContent must be rendered inside PixelsPerSecondContext.Provider", + ); + } + const mediaAssets = useEditor((e) => e.media.getAssets()); + const mediaAsset = mediaAssets.find((asset) => asset.id === element.mediaId); + + if (!mediaAsset) { + return ( + <span className="text-foreground/80 truncate text-xs"> + {element.name} + </span> + ); + } + + const trackHeight = getTrackHeight({ type: track.type }); + const fallbackTileWidth = trackHeight * MEDIA_TILE_ASPECT_RATIO; + + return ( + <> + {mediaAsset.thumbnailUrl && ( + <div + className="absolute inset-0" + style={{ + backgroundColor: "var(--muted)", + backgroundImage: `url(${mediaAsset.thumbnailUrl})`, + backgroundRepeat: "repeat-x", + backgroundSize: `${fallbackTileWidth}px ${trackHeight}px`, + backgroundPosition: "left center", + pointerEvents: "none", + }} + /> + )} + <VideoFilmstrip + element={element} + mediaAsset={mediaAsset} + pixelsPerSecond={pixelsPerSecond} + trackHeight={trackHeight} + /> + <MediaElementHeader + name={mediaAsset.name} + leading={ + FEATURES.effects && hasElementEffects({ element }) ? ( + <EffectsButton element={element} track={track} /> + ) : null + } + hasFade={true} + /> + </> + ); +} + +function ImageElementContent({ + element, + track, +}: { + element: ImageElement; track: TimelineTrack; }) { const mediaAssets = useEditor((e) => e.media.getAssets()); const mediaAsset = mediaAssets.find((asset) => asset.id === element.mediaId); - const imageUrl = - element.type === "video" - ? mediaAsset?.thumbnailUrl - : (mediaAsset?.thumbnailUrl ?? mediaAsset?.url); + const imageUrl = mediaAsset?.thumbnailUrl ?? mediaAsset?.url; if (!imageUrl) { return ( @@ -1106,7 +1165,7 @@ function TiledMediaContent({ } const trackHeight = getTrackHeight({ type: track.type }); - const tileWidth = trackHeight * THUMBNAIL_ASPECT_RATIO; + const tileWidth = trackHeight * MEDIA_TILE_ASPECT_RATIO; return ( <> @@ -1177,8 +1236,9 @@ function ElementContent({ element, track }: ElementContentProps) { case "audio": return <AudioElementContent element={element} trackId={track.id} />; case "video": + return <VideoElementContent element={element} track={track} />; case "image": - return <TiledMediaContent element={element} track={track} />; + return <ImageElementContent element={element} track={track} />; } } |
