Skip to content

Commit

Permalink
Add danmaku (with gravity)
Browse files Browse the repository at this point in the history
  • Loading branch information
nzws committed Jun 14, 2024
1 parent 8eb30d8 commit 95588db
Show file tree
Hide file tree
Showing 9 changed files with 653 additions and 1 deletion.
2 changes: 2 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -158,3 +158,5 @@ dist
Caddyfile
dockerdata/
packages/api-types/dist

apps/web/public/static/vendor/
1 change: 1 addition & 0 deletions apps/web/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@
"live.player.autoplay-blocked": "Click/tap to play",
"live.player.seek-latest": "Seek to the latest",
"live.player.reload": "Reload the stream",
"live.player.danmaku": "弾幕の表示/非表示切り替え",
"live.player.wide": "Wide in window",
"live.player.maximize": "Maximize",
"page.account-settings.title": "Account settings",
Expand Down
1 change: 1 addition & 0 deletions apps/web/locales/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@
"live.player.autoplay-blocked": "クリック/タップして再生",
"live.player.seek-latest": "最新までシーク",
"live.player.reload": "配信を開き直す",
"live.player.danmaku": "弾幕の表示/非表示切り替え",
"live.player.wide": "ウインドウ内ワイド",
"live.player.maximize": "最大化",
"page.account-settings.title": "アカウント設定",
Expand Down
1 change: 1 addition & 0 deletions apps/web/organisms/live/live-app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ export const LiveApp: FC<Props> = ({ live, streamer }) => {
liveId={live.id}
liveTitle={live.title}
userId={user?.id}
comments={comments}
/>
) : (
<VideoMessageBox
Expand Down
10 changes: 10 additions & 0 deletions apps/web/organisms/live/video/controller.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { FC, Fragment, RefObject, useEffect, useState } from 'react';
import {
FiChevronsUp,
FiMaximize,
FiMessageCircle,
FiPause,
FiPlay,
FiRefreshCw,
Expand Down Expand Up @@ -52,6 +53,7 @@ type LiveProps = {
latency: number;
playType: PlayType<'live'> | undefined;
onChangePlayType: (type: PlayType<'live'>) => void;
onChangeDanmaku: () => void;
};

type VideoProps = {
Expand Down Expand Up @@ -204,6 +206,14 @@ export const Controller: FC<Props> = props => {

<Spacer />

{isLive && (
<Tooltip label={intl.formatMessage({ id: 'live.player.danmaku' })}>
<Button variant="ghost" onClick={props.onChangeDanmaku} size="sm">
<FiMessageCircle />
</Button>
</Tooltip>
)}

{props.playType && (
<Menu>
<MenuButton as={Button} variant="ghost" size="sm">
Expand Down
118 changes: 117 additions & 1 deletion apps/web/organisms/live/video/live-player.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { Controller } from './controller';
import { LiveUrls } from 'api-types/api/v1/lives/_liveId@number/url';
import { useVideoStream } from '~/utils/hooks/use-video-stream';
import { useMuxData } from '~/utils/hooks/use-mux-data';
import { CommentPublic } from 'api-types/common/types';
import { Gravity } from '~/utils/danmaku/gravity/jgravity-fork';

type Props = {
thumbnailUrl: string;
Expand All @@ -19,6 +21,7 @@ type Props = {
liveId?: number;
liveTitle?: string;
userId?: number;
comments: CommentPublic[];
};

export const LivePlayer: FC<Props> = ({
Expand All @@ -29,17 +32,22 @@ export const LivePlayer: FC<Props> = ({
isStreamer,
liveId,
liveTitle,
userId
userId,
comments
}) => {
const videoRef = useRef<HTMLVideoElement>(null);
const containerRef = useRef<HTMLDivElement>(null);
const danmakuRef = useRef<HTMLDivElement>(null);
const gravityRef = useRef<Gravity | null>(null);
const commentAddedRef = useRef<Set<number> | null>(null);
const lastPlayingRef = useRef(0);
const [latency, setLatency] = useState<number>(-1);
const [error, setError] = useState<unknown>();
useAPIError(error);
const { show, events } = usePlayerTouch();
const [canPlay, setCanPlay] = useState(false);
const [maybeBlocked, setMaybeBlocked] = useState(false);
const [danmaku, setDanmaku] = useState(true);
const { playType, setPlayType, play } = useVideoStream('live', videoRef, url);
useMuxData(videoRef, liveId, liveTitle, userId, playType);

Expand Down Expand Up @@ -161,6 +169,61 @@ export const LivePlayer: FC<Props> = ({
}
}, [canPlay, autoSeek]);

useEffect(() => {
const container = danmakuRef.current;
if (!container || !danmaku) {
return;
}

void (async () => {
const gravity = new Gravity({}, container);
await gravity.init();
gravityRef.current = gravity;
})();

return () => {
const gravity = gravityRef.current;
if (gravity) {
gravity.destroy();
gravityRef.current = null;
}
};
}, [danmaku]);

useEffect(() => {
if (!danmaku) {
commentAddedRef.current = null;
return;
}
if (!commentAddedRef.current) {
const set = new Set<number>();
commentAddedRef.current = set;

comments.forEach(comment => {
set.add(comment.id);
});
return;
}

const gravity = gravityRef.current;
if (!gravity) {
return;
}

for (const comment of comments) {
if (commentAddedRef.current?.has(comment.id)) {
break;
}

commentAddedRef.current.add(comment.id);
const dom = gravity.add(comment.content, false);

setTimeout(() => {
gravity.remove(dom);
}, 10000);
}
}, [comments, danmaku]);

return (
<Box
{...events}
Expand All @@ -184,6 +247,8 @@ export const LivePlayer: FC<Props> = ({
<video ref={videoRef} autoPlay playsInline controls={false} />
</VideoContainer>

<DanmakuContainer ref={danmakuRef} />

{!canPlay && (
<LoadingContainer>
<Spinner size="xl" />
Expand All @@ -204,6 +269,7 @@ export const LivePlayer: FC<Props> = ({
latency={latency}
playType={playType}
onChangePlayType={setPlayType}
onChangeDanmaku={() => setDanmaku(prev => !prev)}
/>
</Box>
);
Expand All @@ -216,6 +282,56 @@ const VideoContainer = styled(AspectRatio)`
background-repeat: no-repeat;
`;

const DanmakuContainer = styled(Box)`
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
right: 0;
bottom: 0;
pointer-events: none;
user-select: none;
span {
/* credit: https://qiita.com/NoxGit/items/eb0904822c0f0fe97650 */
text-shadow:
black 2px 0,
black -2px 0,
black 0 -2px,
black 0 2px,
black 2px 2px,
black -2px 2px,
black 2px -2px,
black -2px -2px,
black 1px 2px,
black -1px 2px,
black 1px -2px,
black -1px -2px,
black 2px 1px,
black -2px 1px,
black 2px -1px,
black -2px -1px;
font-size: 1.5rem;
line-height: 1 !important;
color: #fff !important;
opacity: 0.7;
white-space: nowrap;
max-width: 100%;
overflow-x: hidden;
overflow-y: visible;
::-webkit-scrollbar {
display: none;
}
@media (min-width: 768px) {
font-size: 2rem;
}
}
`;

const LoadingContainer = styled(Center)`
position: absolute;
width: 100%;
Expand Down
3 changes: 3 additions & 0 deletions apps/web/public/static/vendor/box2d.min.js

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions apps/web/public/static/vendor/inheritance.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 95588db

Please sign in to comment.