diff --git a/packages/app/components/ui/Player.tsx b/packages/app/components/ui/Player.tsx index b2d7952cc..74a32fe64 100644 --- a/packages/app/components/ui/Player.tsx +++ b/packages/app/components/ui/Player.tsx @@ -14,7 +14,7 @@ import { import * as Player from '@livepeer/react/player'; import * as Popover from '@radix-ui/react-popover'; import { CheckIcon, ChevronDownIcon, XIcon } from 'lucide-react'; -import React from 'react'; +import React, { useState } from 'react'; import { useRef, useEffect } from 'react'; // @ts-ignore import mux from 'mux-embed'; @@ -22,13 +22,30 @@ import Image from 'next/image'; import { Src } from '@livepeer/react'; import LogoDark from '@/public/logo_dark.png'; import Link from 'next/link'; +import { + MdOutlineClosedCaption, + MdOutlineClosedCaptionDisabled, +} from 'react-icons/md'; export function PlayerWithControls(props: { src: Src[] | null; name?: string; thumbnail?: string; + caption?: string; }) { - const videoRef = useRef(null); + const videoRef = useRef(null); + const [showSubtitles, setShowSubtitles] = useState(false); + + const toggleSubtitles = () => { + const videoElement = videoRef.current; + if (videoElement) { + const tracks = videoElement.textTracks; + for (let i = 0; i < tracks.length; i++) { + tracks[i].mode = showSubtitles ? 'hidden' : 'showing'; + } + setShowSubtitles(!showSubtitles); + } + }; useEffect(() => { if (videoRef.current) { @@ -63,7 +80,16 @@ export function PlayerWithControls(props: { id={`player-${props.src[0].src}`} title={props.name ?? 'video'} className={cn('h-full w-full transition')} - /> + > + + + {/*
Powered by @@ -207,6 +233,15 @@ export function PlayerWithControls(props: { />
+ +
+ {showSubtitles ? ( + + ) : ( + + )} +
+ diff --git a/packages/app/lib/types.ts b/packages/app/lib/types.ts index 50715b7c4..4dd94cf13 100644 --- a/packages/app/lib/types.ts +++ b/packages/app/lib/types.ts @@ -231,3 +231,13 @@ export interface ChannelPageParams { export interface INFTSessions extends IExtendedSession { videoType: string; } + +interface ChunkTypes { + text: string; + timestamp: [number, number]; +} + +export interface ChunkDataTypes { + chunks: ChunkTypes[]; + text: string; +} diff --git a/packages/app/lib/utils/utils.ts b/packages/app/lib/utils/utils.ts index 57ed756ff..48ec2971c 100644 --- a/packages/app/lib/utils/utils.ts +++ b/packages/app/lib/utils/utils.ts @@ -1,6 +1,7 @@ import { type ClassValue, clsx } from 'clsx'; import { twMerge } from 'tailwind-merge'; import { + ChunkDataTypes, IExtendedEvent, IExtendedNftCollections, IExtendedOrganization, @@ -351,3 +352,23 @@ export const constructYoutubeLiveRedirect = (userEmail: string): string => { return `${baseUrl}?${params.toString()}`; }; + +export const jsonToVtt = (ChunkData: ChunkDataTypes) => { + let vtt = 'WEBVTT\n\n'; + + ChunkData.chunks.forEach((chunk) => { + const startTime = convertToVttTimestamp(chunk.timestamp[0]); + const endTime = convertToVttTimestamp(chunk.timestamp[1]); + vtt += `${startTime} --> ${endTime}\n`; + vtt += `${chunk.text.trim()}\n\n`; + }); + + return vtt.trim(); +}; + +const convertToVttTimestamp = (seconds: number): string => { + const hours = Math.floor(seconds / 3600); + const minutes = Math.floor((seconds % 3600) / 60); + const secs = (seconds % 60).toFixed(3); + return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(secs).padStart(6, '0')}`; +}; diff --git a/packages/app/public/sample.vtt b/packages/app/public/sample.vtt new file mode 100644 index 000000000..8525e5cfb --- /dev/null +++ b/packages/app/public/sample.vtt @@ -0,0 +1,53 @@ +WEBVTT + +00:00:00.000 --> 00:00:10.000 +[Speaker: Jane Smith] +Hi everyone! Today, we're talking about zkSync, a Layer 2 solution for Ethereum. + +00:00:10.000 --> 00:00:20.000 +zkSync helps make transactions faster and cheaper by bundling them together. + +00:00:20.000 --> 00:00:30.000 +It uses zero-knowledge proofs to keep everything secure without revealing details. + +00:00:30.000 --> 00:00:40.000 +This means you get the security of Ethereum but with lower costs. + +00:00:40.000 --> 00:00:50.000 +zkSync is also user-friendly, with instant payments and low fees. + +00:00:50.000 --> 00:01:00.000 +It's great for developers and users who want scalable Ethereum solutions. + +00:01:00.000 --> 00:01:10.000 +So, how does zkSync do this? It uses cryptographic proofs to validate transactions. + +00:01:10.000 --> 00:01:20.000 +These proofs make sure everything is correct without showing any details. + +00:01:20.000 --> 00:01:30.000 +This privacy feature is a big plus over other solutions. + +00:01:30.000 --> 00:01:40.000 +Plus, zkSync works with Ethereum, so developers can use their existing smart contracts easily. + +00:01:40.000 --> 00:01:50.000 +Looking ahead, zkSync will be crucial for Ethereum's growth. + +00:01:50.000 --> 00:02:00.000 +It scales transactions while keeping them secure and private. + +00:02:00.000 --> 00:02:10.000 +If you're curious, check out zkSync's resources and testnet online. + +00:02:10.000 --> 00:02:20.000 +To wrap up, zkSync is a big step for scaling Ethereum. + +00:02:20.000 --> 00:02:30.000 +It offers a secure, scalable, and private solution. + +00:02:30.000 --> 00:02:40.000 +Thanks for joining me! Feel free to reach out with any questions. + +00:02:40.000 --> 00:03:00.000 +[End of session]