Feat: remember last active segment of audio/video (#681)
* remember last segment index for each audio/video * fix for video * auto scroll to cached segment
This commit is contained in:
@@ -17,20 +17,29 @@ export const AudioPlayer = (props: {
|
||||
segmentIndex?: number;
|
||||
}) => {
|
||||
const { id, md5, segmentIndex } = props;
|
||||
const { setMedia, layout, setCurrentSegmentIndex } = useContext(
|
||||
MediaPlayerProviderContext
|
||||
);
|
||||
const {
|
||||
media,
|
||||
setMedia,
|
||||
layout,
|
||||
setCurrentSegmentIndex,
|
||||
getCachedSegmentIndex,
|
||||
} = useContext(MediaPlayerProviderContext);
|
||||
const { audio } = useAudio({ id, md5 });
|
||||
|
||||
const updateCurrentSegmentIndex = async () => {
|
||||
let index = segmentIndex || (await getCachedSegmentIndex());
|
||||
setCurrentSegmentIndex(index);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!audio) return;
|
||||
setMedia(audio);
|
||||
}, [audio]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!segmentIndex) return;
|
||||
setCurrentSegmentIndex(segmentIndex);
|
||||
}, []);
|
||||
if (!media) return;
|
||||
updateCurrentSegmentIndex();
|
||||
}, [media]);
|
||||
|
||||
if (!layout) return <LoaderSpin />;
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ import { MediaTranscriptionReadButton } from "./media-transcription-read-button"
|
||||
export const MediaTranscription = () => {
|
||||
const containerRef = useRef<HTMLDivElement>();
|
||||
const {
|
||||
decoded,
|
||||
media,
|
||||
currentSegmentIndex,
|
||||
wavesurfer,
|
||||
@@ -83,14 +84,17 @@ export const MediaTranscription = () => {
|
||||
|
||||
useEffect(() => {
|
||||
if (!containerRef?.current) return;
|
||||
if (!decoded) return;
|
||||
|
||||
containerRef.current
|
||||
?.querySelector(`#segment-${currentSegmentIndex}`)
|
||||
?.scrollIntoView({
|
||||
block: "center",
|
||||
inline: "center",
|
||||
} as ScrollIntoViewOptions);
|
||||
}, [currentSegmentIndex, transcription, containerRef]);
|
||||
setTimeout(() => {
|
||||
containerRef.current
|
||||
?.querySelector(`#segment-${currentSegmentIndex}`)
|
||||
?.scrollIntoView({
|
||||
block: "center",
|
||||
inline: "center",
|
||||
} as ScrollIntoViewOptions);
|
||||
}, 300);
|
||||
}, [decoded, currentSegmentIndex, transcription, containerRef]);
|
||||
|
||||
if (!transcription?.result?.timeline) {
|
||||
return null;
|
||||
|
||||
@@ -17,11 +17,20 @@ export const VideoPlayer = (props: {
|
||||
segmentIndex?: number;
|
||||
}) => {
|
||||
const { id, md5, segmentIndex } = props;
|
||||
const { setMedia, layout, setCurrentSegmentIndex } = useContext(
|
||||
MediaPlayerProviderContext
|
||||
);
|
||||
const {
|
||||
media,
|
||||
setMedia,
|
||||
layout,
|
||||
setCurrentSegmentIndex,
|
||||
getCachedSegmentIndex,
|
||||
} = useContext(MediaPlayerProviderContext);
|
||||
const { video } = useVideo({ id, md5 });
|
||||
|
||||
const updateCurrentSegmentIndex = async () => {
|
||||
let index = segmentIndex || (await getCachedSegmentIndex());
|
||||
setCurrentSegmentIndex(index);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!video) return;
|
||||
|
||||
@@ -29,9 +38,9 @@ export const VideoPlayer = (props: {
|
||||
}, [video]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!segmentIndex) return;
|
||||
setCurrentSegmentIndex(segmentIndex);
|
||||
}, []);
|
||||
if (!media) return;
|
||||
updateCurrentSegmentIndex();
|
||||
}, [media]);
|
||||
|
||||
if (!layout) return <LoaderSpin />;
|
||||
|
||||
|
||||
@@ -91,6 +91,8 @@ type MediaPlayerContextType = {
|
||||
createSegment: () => Promise<SegmentType | void>;
|
||||
// remote config
|
||||
ipaMappings: { [key: string]: string };
|
||||
getCachedSegmentIndex: () => Promise<number>;
|
||||
setCachedSegmentIndex: (index: number) => void;
|
||||
};
|
||||
|
||||
export const MediaPlayerProviderContext =
|
||||
@@ -191,6 +193,25 @@ export const MediaPlayerProvider = ({
|
||||
segmentIndex: currentSegmentIndex,
|
||||
});
|
||||
|
||||
const getCachedSegmentIndex = async () => {
|
||||
if (!media) return;
|
||||
|
||||
const index = await EnjoyApp.cacheObjects.get(
|
||||
`${media.mediaType.toLowerCase()}-${media.id}-last-segment-index`
|
||||
);
|
||||
|
||||
return index || 0;
|
||||
};
|
||||
|
||||
const setCachedSegmentIndex = (index: number) => {
|
||||
if (!media) return;
|
||||
|
||||
return EnjoyApp.cacheObjects.set(
|
||||
`${media.mediaType.toLowerCase()}-${media.id}-last-segment-index`,
|
||||
index
|
||||
);
|
||||
};
|
||||
|
||||
const { notes, createNote } = useNotes({
|
||||
targetId: segment?.id,
|
||||
targetType: "Segment",
|
||||
@@ -428,7 +449,6 @@ export const MediaPlayerProvider = ({
|
||||
setCurrentTime(0);
|
||||
|
||||
const subscriptions = [
|
||||
wavesurfer.on("loading", (percent: number) => console.log(`${percent}%`)),
|
||||
wavesurfer.on("timeupdate", (time: number) => setCurrentTime(time)),
|
||||
wavesurfer.on("decode", () => {
|
||||
const peaks: Float32Array = wavesurfer
|
||||
@@ -555,6 +575,14 @@ export const MediaPlayerProvider = ({
|
||||
};
|
||||
}, []);
|
||||
|
||||
/* cache last segment index */
|
||||
useEffect(() => {
|
||||
if (!media) return;
|
||||
if (!currentSegmentIndex) return;
|
||||
|
||||
setCachedSegmentIndex(currentSegmentIndex);
|
||||
}, [currentSegmentIndex]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<MediaPlayerProviderContext.Provider
|
||||
@@ -602,6 +630,8 @@ export const MediaPlayerProvider = ({
|
||||
currentSegment: segment,
|
||||
createSegment,
|
||||
ipaMappings,
|
||||
getCachedSegmentIndex,
|
||||
setCachedSegmentIndex,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"sourceMap": true,
|
||||
"baseUrl": ".",
|
||||
"outDir": "dist",
|
||||
"moduleResolution": "Bundler",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"jsx": "react-jsx",
|
||||
"paths": {
|
||||
|
||||
Reference in New Issue
Block a user