diff --git a/src/api/platforms/invid/invidious.ts b/src/api/platforms/invid/invidious.ts index a3b3cb5..deab52e 100644 --- a/src/api/platforms/invid/invidious.ts +++ b/src/api/platforms/invid/invidious.ts @@ -59,8 +59,6 @@ export class InvidiousAPIProvider implements APIProvider { async search(q: string) { let data: InvidiousRenderer[] = await this.request("search", { query: { q } }); - console.log(data); - return { key: null, results: data.filter(x => x.type == "video") @@ -77,8 +75,6 @@ export class InvidiousAPIProvider implements APIProvider { getVideoInfo = async (id: string) => { let v: InvidiousVideoData = await this.request(`videos/${id}`); - console.log({ invidiousVideoData: v }); - return { id, title: v.title, diff --git a/src/api/player/VideoPlayerProvider.tsx b/src/api/player/VideoPlayerProvider.tsx index 1871546..caec079 100644 --- a/src/api/player/VideoPlayerProvider.tsx +++ b/src/api/player/VideoPlayerProvider.tsx @@ -8,6 +8,7 @@ import { parseChapters } from "../../utils/parseChapters"; import { clamp } from "@mantine/hooks"; import { PreferencesContext } from "../pref/Preferences"; import { ActiveChapterList } from "../types/chapter"; +import { useNavigate } from "react-router-dom"; export const VideoPlayerProvider = ({ children @@ -27,6 +28,8 @@ export const VideoPlayerProvider = ({ } = useContext(APIContext); const { pref } = useContext(PreferencesContext); + const navigate = useNavigate(); + const [videoID, setVideoID] = useState(null); const [videoInfo, setVideoInfo] = useState(null); const [activeChapters, setActiveChapters] = useState({ @@ -175,7 +178,7 @@ export const VideoPlayerProvider = ({ let id = videoInfo.recommended[0]?.id; if(!id) return; - setVideoID(id); + navigate({ pathname: "/watch", search: "?"+new URLSearchParams({ v: id }).toString() }); }, waitDuration); }); diff --git a/src/components/cards/CommentCard.tsx b/src/components/cards/CommentCard.tsx index ab4e574..eab8439 100644 --- a/src/components/cards/CommentCard.tsx +++ b/src/components/cards/CommentCard.tsx @@ -7,7 +7,7 @@ import { IconCopy, IconPencil, IconPinned, IconTableImport, IconTableOff } from import { parseChapters } from "../../utils/parseChapters"; import { useContext } from "react"; import { VideoPlayerContext } from "../../api/player/VideoPlayerContext"; -import { cleanDescription, textPartsToString } from "../../utils/cleanDescription"; +import { parseFormattedText, textPartsToString } from "../../utils/parseFormattedText"; import { DateCard } from "./DateCard"; import { TimestampRegex } from "../../utils/timestamp"; @@ -79,7 +79,7 @@ export const CommentCard = ({ )} - + {({ copied, copy }) => ( { + const isMobile = useIsMobile(); const { videoElement, seekToChapterOffset, @@ -80,6 +82,11 @@ export const VideoPlayer = () => { }} onClick={(e) => { if(!e.currentTarget.classList.contains("clickListener")) return; + if(isMobile && !shouldShowControls) { + setShowControls(true); + hideCallback(); + return; + } let xPer = e.clientX / containerRef.current.getBoundingClientRect().width; if(xPer == 0 || xPer == 1) return; togglePlay(); diff --git a/src/components/player/controls/PlayPauseButton.tsx b/src/components/player/controls/PlayPauseButton.tsx index a93fe60..68bd722 100644 --- a/src/components/player/controls/PlayPauseButton.tsx +++ b/src/components/player/controls/PlayPauseButton.tsx @@ -1,22 +1,31 @@ -import { useContext } from "react"; +import { useContext, useState } from "react"; import { VideoPlayerContext } from "../../../api/player/VideoPlayerContext"; import { ActionIcon, Tooltip } from "@mantine/core"; import { IconPlayerPlay, IconPlayerPause } from "@tabler/icons-react"; +import { useVideoEventListener } from "../../../hooks/useVideoEventListener"; +import { useIsMobile } from "../../../hooks/useIsMobile"; export const PlayPauseButton = () => { - const { playState, togglePlay } = useContext(VideoPlayerContext); + const isMobile = useIsMobile(); + const { videoElement, playState, togglePlay } = useContext(VideoPlayerContext); + let playing = playState == "playing"; + //let [playing, setPlaying] = useState(!videoElement.paused); + let disabled = playState == "loading" || playState == "error"; + + //useVideoEventListener(videoElement, "play", () => setPlaying(true)); + //useVideoEventListener(videoElement, "pause", () => setPlaying(false)); return ( togglePlay()} variant="light" color="violet" > - {playState == "playing" ? ( + {playing ? ( ) : ( diff --git a/src/components/player/controls/PlayerTimestamp.tsx b/src/components/player/controls/PlayerTimestamp.tsx index 22b4583..3d1f4ca 100644 --- a/src/components/player/controls/PlayerTimestamp.tsx +++ b/src/components/player/controls/PlayerTimestamp.tsx @@ -3,39 +3,64 @@ import { VideoPlayerContext } from "../../../api/player/VideoPlayerContext"; import { useVideoEventListener } from "../../../hooks/useVideoEventListener"; import { CopyButton, Group, Text, Tooltip } from "@mantine/core"; import { secondsToTimestamp } from "../../../utils/timestamp"; +import { useIsMobile } from "../../../hooks/useIsMobile"; +import { useDisclosure } from "@mantine/hooks"; export const PlayerTimestamp = () => { const { videoElement, activeChapters } = useContext(VideoPlayerContext); + const [showRemaining, { toggle: toggleRemaining }] = useDisclosure(false); const [progress, setProgress] = useState(0); - + useVideoEventListener(videoElement, "timeupdate", () => { setProgress(videoElement.currentTime); }); - + const progressText = secondsToTimestamp(progress); const currentChapter = activeChapters.chapters[activeChapters.chapters.findIndex((x) => x.time > progress) - 1]; + + const isMobile = useIsMobile(); + const fz = isMobile ? "xs" : "md"; return ( - + {({ copied, copy }) => ( - copy()} span> + copy()} + style={{ cursor: "pointer" }} + span + > {progressText} )} - + / - - {secondsToTimestamp(videoElement.duration || 0)} + toggleRemaining()} + style={{ cursor: "pointer" }} + > + {secondsToTimestamp(showRemaining ? ( + Math.ceil(videoElement.duration || 0) - progress + ) : ( + videoElement.duration || 0 + ))} {showRemaining && "rem."} {currentChapter && ( - + {currentChapter.label} )} diff --git a/src/components/player/layout/LayoutBottom.tsx b/src/components/player/layout/LayoutBottom.tsx index 5236868..46931f7 100644 --- a/src/components/player/layout/LayoutBottom.tsx +++ b/src/components/player/layout/LayoutBottom.tsx @@ -7,28 +7,33 @@ import { FormatsButton } from "../../options/links/FormatsButton"; import { OptionsButton } from "../../options/links/OptionsButton"; import { ToggleSidebarButton } from "../../tabs/links/ToggleSidebarButton"; import { FullscreenButton } from "../controls/FullscreenButton"; +import { useIsMobile } from "../../../hooks/useIsMobile"; export const LayoutBottom = (props: StackProps) => { + const isMobile = useIsMobile(); + + let gap = isMobile ? "5px" : "xs"; + return ( e.stopPropagation()} {...props} > - - + + - - + + {!isMobile && } - + {!isMobile && } diff --git a/src/components/player/layout/LayoutTop.tsx b/src/components/player/layout/LayoutTop.tsx index 5f6feeb..9f5632c 100644 --- a/src/components/player/layout/LayoutTop.tsx +++ b/src/components/player/layout/LayoutTop.tsx @@ -2,9 +2,11 @@ import { Grid, Group, Loader, Stack, Text, Title } from "@mantine/core"; import { IconAlertTriangle } from "@tabler/icons-react"; import { useContext } from "react"; import { VideoPlayerContext } from "../../../api/player/VideoPlayerContext"; +import { useIsMobile } from "../../../hooks/useIsMobile"; export const LayoutTop = () => { const { playState, videoInfo } = useContext(VideoPlayerContext); + const isMobile = useIsMobile(); return ( { - + <Title order={isMobile ? 6 : 4}> {!videoInfo ? ( playState == "error" ? "Error" : "Loading..." ) : ( videoInfo?.title || "Loading..." )} - + {playState == "error" && ( videoInfo ? "playback error" : "error while fetching details" )} diff --git a/src/components/ui/MarkdownText.tsx b/src/components/ui/MarkdownText.tsx index a825aad..d667921 100644 --- a/src/components/ui/MarkdownText.tsx +++ b/src/components/ui/MarkdownText.tsx @@ -1,7 +1,7 @@ import { Box, Button, Text } from "@mantine/core"; import { useMemo } from "react"; import { TimestampButton } from "./TimestampButton"; -import { cleanDescription } from "../../utils/cleanDescription"; +import { parseFormattedText } from "../../utils/parseFormattedText"; import { ExternalLink } from "./ExternalLink"; export const MarkdownText = ({ @@ -10,7 +10,7 @@ export const MarkdownText = ({ text: string; }) => { let elements = useMemo(() => { - let parts = cleanDescription(text); + let parts = parseFormattedText(text); return parts .map(part => { diff --git a/src/utils/parseChapters.ts b/src/utils/parseChapters.ts index fb1a8fe..5a78191 100644 --- a/src/utils/parseChapters.ts +++ b/src/utils/parseChapters.ts @@ -1,10 +1,10 @@ import { Chapter } from "../api/types/chapter"; -import { cleanDescription } from "./cleanDescription"; +import { parseFormattedText } from "./parseFormattedText"; const trimChapterName = (s: string) => s.trim().startsWith("-") ? s.trim().replace("-", "").trim() : s.trim(); export const parseChapters = (description: string): Chapter[] => { - let parts = cleanDescription(description); + let parts = parseFormattedText(description); let chapters: Chapter[] = []; let group = ""; diff --git a/src/utils/cleanDescription.ts b/src/utils/parseFormattedText.ts similarity index 93% rename from src/utils/cleanDescription.ts rename to src/utils/parseFormattedText.ts index 912f3d6..7d8f06b 100644 --- a/src/utils/cleanDescription.ts +++ b/src/utils/parseFormattedText.ts @@ -25,7 +25,7 @@ export type TextPart = { time?: number; }; -export const cleanDescription = (text = "") => { +export const parseFormattedText = (text = "") => { let parser = new DOMParser(); let doc = parser.parseFromString(text.replaceAll("
", "\n"), "text/html"); @@ -100,6 +100,13 @@ export const cleanDescription = (text = "") => { href: href, }) } + } else if(node.nodeName == "I") { + parts.push({ + type: "text", + data: node.nodeValue, + bold: false, + italic: true, + }); } else if(node.nodeName == "B") { parts.push({ type: "text",