Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(music): original music video link #487

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions src/components/blocks/EmbedVideoPlayer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import React, { Fragment, useEffect, useState } from "react";

const MusicVideoPlayer: React.FC<{
videoUrl: string;
}> = (props) => {
const [isYtbVideo, setIsYtbVideo] = useState(false);
const [ytbVideoId, setYtbVideoId] = useState("");

const [isNicoVideo, setIsNicoVideo] = useState(false);
const [nicoVideoId, setNicoVideoId] = useState("");

useEffect(() => {
if (props.videoUrl.includes("youtube.com")) {
setIsYtbVideo(true);
setYtbVideoId(
new URLSearchParams(new URL(props.videoUrl).search).get("v") || ""
);
} else if (props.videoUrl.includes("youtu.be")) {
setIsYtbVideo(true);
setYtbVideoId(new URL(props.videoUrl).pathname.split("/")[1] || "");
} else if (props.videoUrl.includes("nicovideo.jp")) {
setIsNicoVideo(true);
setNicoVideoId(
new URL(props.videoUrl).pathname.split("/")[2].split("?")[0] || ""
);
}
}, [props.videoUrl]);

return (
<Fragment>
{isYtbVideo && (
<iframe
width="100%"
height="485"
src={`https://www.youtube.com/embed/${ytbVideoId}`}
title="YouTube video player"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
style={{ border: "none" }}
></iframe>
)}
{isNicoVideo && (
<iframe
width="100%"
height="485"
src={`https://embed.nicovideo.jp/watch/${nicoVideoId}?autoplay=0`}
title="niconico"
allowFullScreen
allow="encrypted-media *;"
style={{ border: "none" }}
></iframe>
)}
</Fragment>
);
};

export default MusicVideoPlayer;
2 changes: 1 addition & 1 deletion src/components/blocks/SekaiGameNews.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ function InfoInternal(props: { onClick: () => void }) {
function InfoInternalDialog(props: {
url: string;
open: boolean;
onClose: (event: {}, reason: "backdropClick" | "escapeKeyDown") => void;
onClose: (event: unknown, reason: "backdropClick" | "escapeKeyDown") => void;
title: string;
}) {
return (
Expand Down
109 changes: 80 additions & 29 deletions src/pages/music/MusicDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
Switch,
Link,
Box,
IconButton,
} from "@mui/material";
import { Alert } from "@mui/material";
import { TabContext, TabPanel } from "@mui/lab";
Expand All @@ -26,6 +27,7 @@ import {
IMusicTagInfo,
IMusicVocalInfo,
IOutCharaProfile,
IMusicOriginal,
} from "../../types.d";
import { getRemoteAssetURL, useCachedData, useMusicTagName } from "../../utils";
import { charaIcons } from "../../utils/resources";
Expand Down Expand Up @@ -55,6 +57,7 @@ import TypographyHeader from "../../components/styled/TypographyHeader";
import ContainerContent from "../../components/styled/ContainerContent";
import PaperContainer from "../../components/styled/PaperContainer";
import GridOut from "../../components/styled/GridOut";
import EmbedVideoPlayer from "../../components/blocks/EmbedVideoPlayer";

const MusicDetail: React.FC<unknown> = observer(() => {
const { t } = useTranslation();
Expand All @@ -81,6 +84,7 @@ const MusicDetail: React.FC<unknown> = observer(() => {
const [danceMembers] = useCachedData<IMusicDanceMembers>("musicDanceMembers");
const [musicAchievements] =
useCachedData<IMusicAchievement>("musicAchievements");
const [musicOriginals] = useCachedData<IMusicOriginal>("musicOriginals");

const { musicId } = useParams<{ musicId: string }>();

Expand All @@ -91,6 +95,7 @@ const MusicDetail: React.FC<unknown> = observer(() => {
const [musicVocalTypes, setMusicVocalTypes] = useState<string[]>([]);
const [musicDanceMember, setMusicDanceMember] =
useState<IMusicDanceMembers>();
const [musicOriginal, setMusicOriginal] = useState<IMusicOriginal>();
const [selectedPreviewVocalType, setSelectedPreviewVocalType] =
useState<number>(0);
const [selectedVocalType, setSelectedVocalType] = useState<number>(0);
Expand Down Expand Up @@ -165,6 +170,14 @@ const MusicDetail: React.FC<unknown> = observer(() => {
}
}, [difficulties]);

useEffect(() => {
if (musicOriginals) {
setMusicOriginal(
musicOriginals.find((elem) => elem.musicId === Number(musicId))
);
}
}, [musicOriginals, musicId]);

useEffect(() => {
if (music && musicVocal && musicVocal[selectedPreviewVocalType]) {
if ([420, 445, 453, 459, 464, 10001, 10002].includes(music.id)) {
Expand Down Expand Up @@ -441,23 +454,39 @@ const MusicDetail: React.FC<unknown> = observer(() => {
<Alert severity="warning">
<Trans i18nKey="music:alert[0]" components={{ b: <b /> }} />
</Alert>
<Grid container justifyContent="center">
<Grid item xs={12} sm={6}>
<div
onClick={() => {
setActiveIdx(0);
setVisible(true);
}}
>
<Box
component={Image}
src={musicJacket}
bgColor=""
sx={{ cursor: "pointer" }}
/>
</div>
{["original", "mv_2d"].includes(vocalPreviewVal) &&
musicVocalTypes.length &&
musicVocal.length &&
longMusicPlaybackURL ? (
<MusicVideoPlayer
audioPath={longMusicPlaybackURL}
videoPath={musicVideoURL}
onPlay={() => setVocalDisabled(true)}
onPause={() => setVocalDisabled(false)}
onEnded={() => setVocalDisabled(false)}
/>
) : ["original_video"].includes(vocalPreviewVal) &&
musicOriginal?.videoLink ? (
<EmbedVideoPlayer videoUrl={musicOriginal.videoLink} />
) : (
<Grid container justifyContent="center">
<Grid item xs={12} sm={6}>
<div
onClick={() => {
setActiveIdx(0);
setVisible(true);
}}
>
<Box
component={Image}
src={musicJacket}
bgColor=""
sx={{ cursor: "pointer" }}
/>
</div>
</Grid>
</Grid>
</Grid>
)}
<PaperContainer>
<Grid container direction="column" spacing={1}>
<Grid
Expand Down Expand Up @@ -518,6 +547,14 @@ const MusicDetail: React.FC<unknown> = observer(() => {
}
/>
))}
{!!musicOriginal && (
<FormControlLabel
value="original_video"
control={<Radio color="primary"></Radio>}
label={t("music:vocalTab.original_video") as string}
labelPlacement="end"
/>
)}
</RadioGroup>
</Grid>
</Grid>
Expand Down Expand Up @@ -611,19 +648,6 @@ const MusicDetail: React.FC<unknown> = observer(() => {
offset={trimSilence ? music.fillerSec : 0}
/>
)}
{["original", "mv_2d"].includes(vocalPreviewVal) &&
musicVocalTypes.length &&
musicVocal.length &&
longMusicPlaybackURL && (
<MusicVideoPlayer
audioPath={longMusicPlaybackURL}
videoPath={musicVideoURL}
onPlay={() => setVocalDisabled(true)}
onPause={() => setVocalDisabled(false)}
onEnded={() => setVocalDisabled(false)}
/>
)}

<GridOut container direction="column">
<Grid
container
Expand Down Expand Up @@ -817,6 +841,33 @@ const MusicDetail: React.FC<unknown> = observer(() => {
</Typography>
</Grid>
<Divider style={{ margin: "1% 0" }} />
{!!musicOriginal && (
<Fragment>
<Grid
container
direction="row"
wrap="nowrap"
justifyContent="space-between"
alignItems="center"
>
<Typography variant="subtitle1" style={{ fontWeight: 600 }}>
{t("music:vocalTab.original_video")}
</Typography>
<Typography>
<IconButton
component="a"
href={musicOriginal.videoLink}
target="_blank"
size="small"
color="inherit"
>
<OpenInNew />
</IconButton>
</Typography>
</Grid>
<Divider style={{ margin: "1% 0" }} />
</Fragment>
)}
</GridOut>
</ContainerContent>
<TypographyHeader>
Expand Down
6 changes: 6 additions & 0 deletions src/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1563,3 +1563,9 @@ interface ICompactResourceBoxDetailENUM {
resourceBoxPurpose: string[];
resourceType: string[];
}

export interface IMusicOriginal {
id: number;
musicId: number;
videoLink: string;
}
2 changes: 2 additions & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ import {
ICompactResourceBox,
ICompactResourceBoxDetail,
IGachaTicket,
IMusicOriginal,
} from "./../types.d";
import { useAssetI18n, useCharaName } from "./i18n";
import { useLocation } from "react-router-dom";
Expand Down Expand Up @@ -154,6 +155,7 @@ export function useCachedData<
| IMasterLessonReward
| IEventMusic
| IGachaTicket
| IMusicOriginal
>(name: string): [T[] | undefined, boolean, any] {
// const [cached, cachedRef, setCached] = useRefState<T[]>([]);
const { region } = useRootStore();
Expand Down