Skip to content

Commit

Permalink
Added support for canvas start time on audio and video elements
Browse files Browse the repository at this point in the history
  • Loading branch information
stephenwf committed Jun 19, 2024
1 parent 1f62da1 commit b8c36bf
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 11 deletions.
3 changes: 3 additions & 0 deletions src/canvas-panel/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { Audio, AudioHTML } from './render/Audio';
import { Video, VideoHTML } from './render/Video';
import { Model, ModelHTML } from './render/Model';
import { ViewerMode } from '@atlas-viewer/atlas';
import { PlaceholderCanvas } from './render/PlaceholderCanvas';

interface CanvasPanelProps {
manifest: string;
Expand Down Expand Up @@ -117,6 +118,7 @@ type CanvasPanelType = ForwardRefExoticComponent<CanvasPanelProps & RefAttribute
AudioHTML: typeof AudioHTML;
VideoHTML: typeof VideoHTML;
ModelHTML: typeof ModelHTML;
PlaceholderCanvas: typeof PlaceholderCanvas;
};

export const CanvasPanel = forwardRef<SimpleViewerContext, CanvasPanelProps>(function CanvasPanel(
Expand Down Expand Up @@ -173,3 +175,4 @@ CanvasPanel.Model = Model;
CanvasPanel.AudioHTML = AudioHTML;
CanvasPanel.VideoHTML = VideoHTML;
CanvasPanel.ModelHTML = ModelHTML;
CanvasPanel.PlaceholderCanvas = PlaceholderCanvas;
22 changes: 19 additions & 3 deletions src/canvas-panel/render/Audio.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,19 @@ import { useSimpleMediaPlayer } from '../../hooks/useSimpleMediaPlayer';
import { SingleAudio } from '../../features/rendering-strategy/resource-types';
import { MediaPlayerProvider } from '../../context/MediaContext';
import { useOverlay } from '../context/overlays';
import { useCanvasStartTime } from '../../hooks/useCanvasStartTime';

export function AudioHTML({ media, children }: { media: SingleAudio; children: ReactNode }) {
export function AudioHTML({
media,
startTime,
children,
}: {
media: SingleAudio;
startTime?: number | null;
children: ReactNode;
}) {
const [{ element, currentTime, progress }, state, actions] = useSimpleMediaPlayer({ duration: media.duration });
const mediaUrl = startTime ? `${media.url}#t=${startTime}` : media.url;

return (
<MediaPlayerProvider
Expand All @@ -15,7 +25,7 @@ export function AudioHTML({ media, children }: { media: SingleAudio; children: R
progress={progress}
element={element}
>
<audio ref={element} src={media.url} />
<audio ref={element} src={mediaUrl} />
{children}
</MediaPlayerProvider>
);
Expand All @@ -30,7 +40,13 @@ export function Audio({
mediaControlsDeps?: any[];
children: ReactNode;
}) {
useOverlay('portal', 'audio', AudioHTML, { media, children }, [media, ...(mediaControlsDeps || [])]);
const start = useCanvasStartTime();

useOverlay('portal', 'audio', AudioHTML, { media, startTime: start ? start.startTime : null, children }, [
media,
start,
...(mediaControlsDeps || []),
]);

return null;
}
4 changes: 1 addition & 3 deletions src/canvas-panel/render/Canvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -196,9 +196,7 @@ export function RenderCanvas({

const renderPlaceholderCanvas =
strategy.type === 'media' && strategy.media.type === 'Video' && placeholderCanvas ? (
<CanvasContext canvas={placeholderCanvas.id}>
<PlaceholderCanvas renderViewerControls={renderViewerControls} />
</CanvasContext>
<PlaceholderCanvas renderViewerControls={renderViewerControls} />
) : null;

return (
Expand Down
10 changes: 7 additions & 3 deletions src/canvas-panel/render/PlaceholderCanvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { SingleImageStrategy } from '../../features/rendering-strategy/image-str
import { EmptyStrategy } from '../../features/rendering-strategy/strategies';
import { useCanvas } from '../../hooks/useCanvas';
import { RenderCanvas } from './Canvas';
import { CanvasContext } from '../../context/CanvasContext';

interface PlaceholderCanvasProps {
renderViewerControls?: (strategy: SingleImageStrategy | EmptyStrategy) => ReactNode;
Expand All @@ -12,8 +13,11 @@ interface PlaceholderCanvasProps {
export function PlaceholderCanvas(props: PlaceholderCanvasProps) {
const canvas = useCanvas();

if (!canvas) return null;
if (!canvas || !canvas.placeholderCanvas) return null;

return <RenderCanvas renderViewerControls={props.renderViewerControls} />;
// return null;
return (
<CanvasContext canvas={canvas.placeholderCanvas.id}>
<RenderCanvas renderViewerControls={props.renderViewerControls} />
</CanvasContext>
);
}
12 changes: 10 additions & 2 deletions src/canvas-panel/render/Video.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,21 @@ import { MediaPlayerProvider } from '../../context/MediaContext';
import { useOverlay } from '../context/overlays';
import { useThumbnail } from '../../hooks/useThumbnail';
import { useCanvas } from '../../hooks/useCanvas';
import { useManifest } from '../../hooks/useManifest';
import { useCanvasStartTime } from '../../hooks/useCanvasStartTime';

export interface VideoComponentProps {
element: RefObject<HTMLVideoElement>;
media: SingleVideo;
playPause: () => void;
poster?: string;
startTime?: number;
}

export function VideoHTML({ element, media, playPause, poster }: VideoComponentProps) {
export function VideoHTML({ element, media, startTime, playPause, poster }: VideoComponentProps) {
const Component = 'div' as any;
const mediaUrl = startTime ? `${media.url}#t=${startTime}` : media.url;

return (
<Component className="video-container" part="video-container" onClick={playPause}>
<style>
Expand All @@ -33,7 +38,7 @@ export function VideoHTML({ element, media, playPause, poster }: VideoComponentP
}
`}
</style>
<video poster={poster} ref={element} src={media.url} style={{ width: '100%', objectFit: 'contain' }} />
<video poster={poster} ref={element} src={mediaUrl} style={{ width: '100%', objectFit: 'contain' }} />
</Component>
);
}
Expand All @@ -51,6 +56,8 @@ export function Video({
videoComponent?: FC<VideoComponentProps>;
}) {
const canvas = useCanvas();
const start = useCanvasStartTime();

const posterCanvasId = (canvas && canvas.placeholderCanvas && canvas.placeholderCanvas.id) || undefined;
const poster = useThumbnail({}, false, { canvasId: posterCanvasId });
const [{ element, currentTime, progress }, state, actions] = useSimpleMediaPlayer({ duration: media.duration });
Expand All @@ -64,6 +71,7 @@ export function Video({
media,
playPause: actions.playPause,
poster: poster?.id,
startTime: start ? start.startTime : null,
},
[poster]
);
Expand Down
31 changes: 31 additions & 0 deletions src/hooks/useCanvasStartTime.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { useMemo } from 'react';
import { useCanvas } from './useCanvas';
import { useManifest } from './useManifest';
import { getParsedTargetSelector } from '../utils';
import { expandTarget } from '@iiif/helpers';

export function useCanvasStartTime() {
const manifest = useManifest();
const canvas = useCanvas();

return useMemo(() => {
if (!manifest || !canvas || !manifest.start) {
return null;
}

const parsed = expandTarget(manifest.start as any);
if (!parsed) {
return null;
}

if (parsed.source.id !== canvas.id) {
return null;
}

if (!parsed || !parsed.selector || parsed.selector.type !== 'TemporalSelector') {
return null;
}

return parsed.selector.temporal;
}, [manifest, canvas]);
}
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export * from './hooks/useCanvas';
export * from './hooks/useCanvasChoices';
export * from './hooks/useCanvasClock';
export * from './hooks/useCanvasSubset';
export * from './hooks/useCanvasStartTime';
// export * from './hooks/useCanvasSelector';
// export * from './hooks/useCanvasTimeline';
export * from './hooks/useCollection';
Expand Down

0 comments on commit b8c36bf

Please sign in to comment.