Skip to content

Commit

Permalink
add caption/subtitle icon to player
Browse files Browse the repository at this point in the history
  • Loading branch information
greatsamist committed Aug 8, 2024
1 parent f38bbcc commit 609ba08
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 3 deletions.
41 changes: 38 additions & 3 deletions packages/app/components/ui/Player.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,38 @@ 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';
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<HTMLVideoElement>(null);
const [showSubtitles, setShowSubtitles] = useState<boolean>(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) {
Expand Down Expand Up @@ -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')}
/>
>
<track
label="English"
kind="subtitles"
srcLang="en"
src={props.caption ?? '/sample.vtt'}
default
/>
</Player.Video>

{/* <Player.PlayingIndicator asChild matcher={false}>
<div className="shadow border flex flex-col items-center justify-center bg-white rounded-xl h-[60px] absolute top-0 bottom-0 py-0 p-2 m-2">
<span className="text-xs">Powered by</span>
Expand Down Expand Up @@ -207,6 +233,15 @@ export function PlayerWithControls(props: {
/>
</div>
</Link>

<div onClick={toggleSubtitles} className="cursor-pointer">
{showSubtitles ? (
<MdOutlineClosedCaptionDisabled color="#fff" size={26} />
) : (
<MdOutlineClosedCaption color="#fff" size={26} />
)}
</div>

<Player.FullscreenIndicator matcher={false} asChild>
<Settings className="h-6 w-6 flex-shrink-0 text-white transition" />
</Player.FullscreenIndicator>
Expand Down
10 changes: 10 additions & 0 deletions packages/app/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
21 changes: 21 additions & 0 deletions packages/app/lib/utils/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { type ClassValue, clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';
import {
ChunkDataTypes,
IExtendedEvent,
IExtendedNftCollections,
IExtendedOrganization,
Expand Down Expand Up @@ -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')}`;
};
53 changes: 53 additions & 0 deletions packages/app/public/sample.vtt
Original file line number Diff line number Diff line change
@@ -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]

0 comments on commit 609ba08

Please sign in to comment.