diff --git a/src/apis/index.js b/src/apis/index.js index 7a3707bf0..08d62b425 100644 --- a/src/apis/index.js +++ b/src/apis/index.js @@ -24,3 +24,8 @@ export const markAllAsRead = async () => { const currentUser = await getCurrentUser() return apiClient.put(`/v1/users/${currentUser.id}/mark-all-as-read`) } + +export const saveEnclosureProgression = async ( + enclosureId, + progress, // enclosureId: number, progress: number +) => apiClient.put(`/v1/enclosures/${enclosureId}`, { media_progression: progress }) diff --git a/src/components/Article/ArticleDetail.jsx b/src/components/Article/ArticleDetail.jsx index 771168614..26ad0ff59 100644 --- a/src/components/Article/ArticleDetail.jsx +++ b/src/components/Article/ArticleDetail.jsx @@ -223,6 +223,21 @@ const ArticleDetail = forwardRef((_, ref) => { const { id: categoryId, title: categoryTitle } = activeContent.feed.category const { id: feedId, title: feedTitle } = activeContent.feed + const mediaPlayerEnclosure = activeContent.enclosures?.find( + (enclosure) => + enclosure.url !== "" && + (enclosure.mime_type.startsWith("video/") || enclosure.mime_type.startsWith("audio/")), + ) + const enclosure = mediaPlayerEnclosure || null + + const imageEnclosure = activeContent.enclosures?.find( + (enclosure) => + enclosure.mime_type.toLowerCase().startsWith("image/") || + /\.(jpg|jpeg|png|gif)$/i.test(enclosure.url), + ) + + const poster = imageEnclosure?.url || "" + // pretty footnotes useEffect(() => { littlefoot() @@ -285,6 +300,7 @@ const ArticleDetail = forwardRef((_, ref) => { "--article-width": articleWidth, }} > + {enclosure && } {parsedHtml} {}, onError = () => {}, }) => { @@ -103,6 +105,7 @@ const PlyrPlayer = ({ const mediaRef = useRef(null) const playerRef = useRef(null) const hlsRef = useRef(null) + const lastSavedTimeRef = useRef(0) useEffect(() => { if (!src || !activeContent) { @@ -127,6 +130,29 @@ const PlyrPlayer = ({ mediaRef.current.src = src } + if (enclosure) { + playerRef.current.on("loadeddata", () => { + playerRef.current.currentTime = enclosure.media_progression + lastSavedTimeRef.current = enclosure.media_progression + }) + + const updateProgression = () => { + const { currentTime } = playerRef.current + saveEnclosureProgression(enclosure.id, Math.floor(currentTime)) + lastSavedTimeRef.current = currentTime + } + + playerRef.current.on("timeupdate", () => { + const { currentTime } = playerRef.current + if (currentTime - lastSavedTimeRef.current >= 10) { + updateProgression() + } + }) + + playerRef.current.on("pause", updateProgression) + playerRef.current.on("ended", updateProgression) + } + onPlayerInit(playerRef.current) } catch (error) { onError({ type: "init", error }) diff --git a/src/utils/images.js b/src/utils/images.js index a094d371a..c814eb19b 100644 --- a/src/utils/images.js +++ b/src/utils/images.js @@ -16,12 +16,13 @@ export const parseCoverImage = (entry) => { coverSource = video.getAttribute("poster") isVideo = true } else if (entry.enclosures?.[0]) { - const firstEnclosure = entry.enclosures[0] - const isImage = - firstEnclosure.mime_type.toLowerCase().startsWith("image/") || - /\.(jpg|jpeg|png|gif)$/i.test(firstEnclosure.url) - if (isImage) { - coverSource = firstEnclosure.url + const imageEnclosure = entry.enclosures.find( + (enclosure) => + enclosure.mime_type.toLowerCase().startsWith("image/") || + /\.(jpg|jpeg|png|gif)$/i.test(enclosure.url), + ) + if (imageEnclosure) { + coverSource = imageEnclosure.url } // Youtube thumbnail isVideo = coverSource?.startsWith("https://i.ytimg.com") ?? false