mirror of
https://github.com/cloudreve/frontend.git
synced 2025-12-25 19:52:48 +00:00
Feat: use ArtPlayer, support playlist and subtitles
This commit is contained in:
parent
84b05ddd66
commit
c0de15e49e
|
|
@ -13,9 +13,9 @@
|
|||
"@types/node": "^14.0.1",
|
||||
"@types/react": "^16.9.35",
|
||||
"@types/react-dom": "^16.9.8",
|
||||
"@types/streamsaver": "^2.0.1",
|
||||
"@typescript-eslint/eslint-plugin": "^2.33.0",
|
||||
"@typescript-eslint/parser": "^2.33.0",
|
||||
"@types/streamsaver": "^2.0.1",
|
||||
"axios": "^0.21.1",
|
||||
"babel-eslint": "10.0.3",
|
||||
"babel-jest": "^24.9.0",
|
||||
|
|
@ -93,6 +93,7 @@
|
|||
"sass-loader": "7.2.0",
|
||||
"semver": "6.3.0",
|
||||
"streamsaver": "^2.0.6",
|
||||
"artplayer": "^4.3.4",
|
||||
"style-loader": "1.0.0",
|
||||
"terser-webpack-plugin": "1.4.1",
|
||||
"timeago-react": "^3.0.0",
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import { makeStyles } from "@material-ui/core/styles";
|
|||
import { useLocation, useParams, useRouteMatch } from "react-router";
|
||||
import API from "../../middleware/Api";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { changeSubTitle } from "../../redux/viewUpdate/action";
|
||||
import pathHelper from "../../utils/page";
|
||||
import SaveButton from "../Dial/Save";
|
||||
import { codePreviewSuffix } from "../../config";
|
||||
|
|
@ -16,6 +15,8 @@ import Switch from "@material-ui/core/Switch";
|
|||
import MenuItem from "@material-ui/core/MenuItem";
|
||||
import Divider from "@material-ui/core/Divider";
|
||||
import { toggleSnackbar } from "../../redux/explorer";
|
||||
import UseFileSubTitle from "../../hooks/fileSubtitle";
|
||||
|
||||
const MonacoEditor = React.lazy(() =>
|
||||
import(/* webpackChunkName: "codeEditor" */ "react-monaco-editor")
|
||||
);
|
||||
|
|
@ -65,12 +66,9 @@ export default function CodeViewer() {
|
|||
const query = useQuery();
|
||||
const { id } = useParams();
|
||||
const theme = useTheme();
|
||||
UseFileSubTitle(query, math, location);
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const SetSubTitle = useCallback(
|
||||
(title) => dispatch(changeSubTitle(title)),
|
||||
[dispatch]
|
||||
);
|
||||
const ToggleSnackbar = useCallback(
|
||||
(vertical, horizontal, msg, color) =>
|
||||
dispatch(toggleSnackbar(vertical, horizontal, msg, color)),
|
||||
|
|
@ -78,18 +76,10 @@ export default function CodeViewer() {
|
|||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!pathHelper.isSharePage(location.pathname)) {
|
||||
const path = query.get("p").split("/");
|
||||
const extension = query.get("p").split(".");
|
||||
setSuffix(codePreviewSuffix[extension.pop()]);
|
||||
SetSubTitle(path[path.length - 1]);
|
||||
} else {
|
||||
const extension = query.get("name").split(".");
|
||||
setSuffix(codePreviewSuffix[extension.pop()]);
|
||||
SetSubTitle(query.get("name"));
|
||||
}
|
||||
const extension = query.get("p").split(".");
|
||||
setSuffix(codePreviewSuffix[extension.pop()]);
|
||||
// eslint-disable-next-line
|
||||
}, [math.params[0], location]);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
let requestURL = "/file/content/" + query.get("id");
|
||||
|
|
@ -142,11 +132,18 @@ export default function CodeViewer() {
|
|||
<Paper className={classes.root} elevation={1}>
|
||||
<div className={classes.toobar}>
|
||||
<FormControl className={classes.formControl}>
|
||||
<FormControlLabel control={
|
||||
<Switch
|
||||
onChange={(e) => setWordWrap(e.target.checked ? "on" : "off")}
|
||||
/>
|
||||
} label="自动换行" />
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
onChange={(e) =>
|
||||
setWordWrap(
|
||||
e.target.checked ? "on" : "off"
|
||||
)
|
||||
}
|
||||
/>
|
||||
}
|
||||
label="自动换行"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormControl className={classes.formControl}>
|
||||
<Select
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@ import { makeStyles } from "@material-ui/core/styles";
|
|||
import { useLocation, useParams, useRouteMatch } from "react-router";
|
||||
import API from "../../middleware/Api";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { changeSubTitle } from "../../redux/viewUpdate/action";
|
||||
import pathHelper from "../../utils/page";
|
||||
import { toggleSnackbar } from "../../redux/explorer";
|
||||
import UseFileSubTitle from "../../hooks/fileSubtitle";
|
||||
|
||||
const useStyles = makeStyles(() => ({
|
||||
layout: {
|
||||
|
|
@ -30,30 +30,16 @@ export default function DocViewer() {
|
|||
const location = useLocation();
|
||||
const query = useQuery();
|
||||
const { id } = useParams();
|
||||
UseFileSubTitle(query, math, location);
|
||||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const SetSubTitle = useCallback(
|
||||
(title) => dispatch(changeSubTitle(title)),
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
const ToggleSnackbar = useCallback(
|
||||
(vertical, horizontal, msg, color) =>
|
||||
dispatch(toggleSnackbar(vertical, horizontal, msg, color)),
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!pathHelper.isSharePage(location.pathname)) {
|
||||
const path = query.get("p").split("/");
|
||||
SetSubTitle(path[path.length - 1]);
|
||||
} else {
|
||||
SetSubTitle(query.get("name"));
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
}, [math.params[0], location]);
|
||||
|
||||
useEffect(() => {
|
||||
let requestURL = "/file/doc/" + query.get("id");
|
||||
if (pathHelper.isSharePage(location.pathname)) {
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import React, { useCallback, useState } from "react";
|
||||
import { Document, Page, pdfjs } from "react-pdf";
|
||||
import { Paper } from "@material-ui/core";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import { useLocation, useParams, useRouteMatch } from "react-router";
|
||||
import { getBaseURL } from "../../middleware/Api";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { changeSubTitle } from "../../redux/viewUpdate/action";
|
||||
import pathHelper from "../../utils/page";
|
||||
import TextLoading from "../Placeholder/TextLoading";
|
||||
import { toggleSnackbar } from "../../redux/explorer";
|
||||
import UseFileSubTitle from "../../hooks/fileSubtitle";
|
||||
|
||||
pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.js`;
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
|
|
@ -44,30 +45,16 @@ export default function PDFViewer() {
|
|||
const location = useLocation();
|
||||
const query = useQuery();
|
||||
const { id } = useParams();
|
||||
UseFileSubTitle(query, math, location);
|
||||
|
||||
const [pageNumber, setPageNumber] = useState(1);
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const SetSubTitle = useCallback(
|
||||
(title) => dispatch(changeSubTitle(title)),
|
||||
[dispatch]
|
||||
);
|
||||
const ToggleSnackbar = useCallback(
|
||||
(vertical, horizontal, msg, color) =>
|
||||
dispatch(toggleSnackbar(vertical, horizontal, msg, color)),
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!pathHelper.isSharePage(location.pathname)) {
|
||||
const path = query.get("p").split("/");
|
||||
SetSubTitle(path[path.length - 1]);
|
||||
} else {
|
||||
SetSubTitle(query.get("name"));
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
}, [math.params[0], location]);
|
||||
|
||||
const removeTextLayerOffset = () => {
|
||||
const textLayers = document.querySelectorAll(
|
||||
".react-pdf__Page__textContent"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
import React from "react";
|
||||
import {
|
||||
Icon,
|
||||
ListItemIcon,
|
||||
makeStyles,
|
||||
Menu,
|
||||
MenuItem,
|
||||
} from "@material-ui/core";
|
||||
import CheckIcon from "@material-ui/icons/Check";
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
icon: {
|
||||
minWidth: 38,
|
||||
},
|
||||
}));
|
||||
|
||||
export default function SelectMenu({
|
||||
options,
|
||||
anchorEl,
|
||||
handleClose,
|
||||
callback,
|
||||
selected,
|
||||
showIcon = true,
|
||||
}) {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<Menu
|
||||
id="simple-menu"
|
||||
anchorEl={anchorEl}
|
||||
keepMounted
|
||||
open={Boolean(anchorEl)}
|
||||
onClose={handleClose}
|
||||
>
|
||||
{options.map((item) => (
|
||||
<>
|
||||
<MenuItem dense onClick={() => callback(item)}>
|
||||
{showIcon && (
|
||||
<ListItemIcon className={classes.icon}>
|
||||
{item.name !== selected ? (
|
||||
<Icon />
|
||||
) : (
|
||||
<CheckIcon />
|
||||
)}
|
||||
</ListItemIcon>
|
||||
)}
|
||||
|
||||
{item.name}
|
||||
</MenuItem>
|
||||
</>
|
||||
))}
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
|
|
@ -4,12 +4,13 @@ import { makeStyles } from "@material-ui/core/styles";
|
|||
import { useLocation, useParams, useRouteMatch } from "react-router";
|
||||
import API from "../../middleware/Api";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { changeSubTitle } from "../../redux/viewUpdate/action";
|
||||
import Editor from "for-editor";
|
||||
import SaveButton from "../Dial/Save";
|
||||
import pathHelper from "../../utils/page";
|
||||
import TextLoading from "../Placeholder/TextLoading";
|
||||
import { toggleSnackbar } from "../../redux/explorer";
|
||||
import UseFileSubTitle from "../../hooks/fileSubtitle";
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
layout: {
|
||||
width: "auto",
|
||||
|
|
@ -17,9 +18,8 @@ const useStyles = makeStyles((theme) => ({
|
|||
marginLeft: theme.spacing(3),
|
||||
marginRight: theme.spacing(3),
|
||||
[theme.breakpoints.up(1100 + theme.spacing(3) * 2)]: {
|
||||
width: 1100,
|
||||
marginLeft: "auto",
|
||||
marginRight: "auto",
|
||||
marginLeft: theme.spacing(12),
|
||||
marginRight: theme.spacing(12),
|
||||
},
|
||||
marginBottom: 50,
|
||||
},
|
||||
|
|
@ -50,28 +50,15 @@ export default function TextViewer() {
|
|||
const location = useLocation();
|
||||
const query = useQuery();
|
||||
const { id } = useParams();
|
||||
UseFileSubTitle(query, math, location);
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const SetSubTitle = useCallback(
|
||||
(title) => dispatch(changeSubTitle(title)),
|
||||
[dispatch]
|
||||
);
|
||||
const ToggleSnackbar = useCallback(
|
||||
(vertical, horizontal, msg, color) =>
|
||||
dispatch(toggleSnackbar(vertical, horizontal, msg, color)),
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!pathHelper.isSharePage(location.pathname)) {
|
||||
const path = query.get("p").split("/");
|
||||
SetSubTitle(path[path.length - 1]);
|
||||
} else {
|
||||
SetSubTitle(query.get("name"));
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
}, [math.params[0], location]);
|
||||
|
||||
useEffect(() => {
|
||||
let requestURL = "/file/content/" + query.get("id");
|
||||
if (pathHelper.isSharePage(location.pathname)) {
|
||||
|
|
|
|||
|
|
@ -1,13 +1,27 @@
|
|||
import React, { useCallback, useEffect } from "react";
|
||||
import DPlayer from "react-dplayer";
|
||||
import { Paper } from "@material-ui/core";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import React, { Suspense, useCallback, useEffect, useState } from "react";
|
||||
import { Button, Paper } from "@material-ui/core";
|
||||
import { makeStyles, useTheme } from "@material-ui/core/styles";
|
||||
import { useLocation, useParams, useRouteMatch } from "react-router";
|
||||
import { getBaseURL } from "../../middleware/Api";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { changeSubTitle } from "../../redux/viewUpdate/action";
|
||||
import pathHelper from "../../utils/page";
|
||||
import { isMobileSafari } from "../../utils";
|
||||
import UseFileSubTitle from "../../hooks/fileSubtitle";
|
||||
import { getPreviewURL } from "../../middleware/Api";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import { basename, fileNameNoExt, isMobileSafari } from "../../utils";
|
||||
import { list } from "../../services/navigate";
|
||||
import { getViewerURL } from "../../redux/explorer/action";
|
||||
import { subtitleSuffix, videoPreviewSuffix } from "../../config";
|
||||
import { toggleSnackbar } from "../../redux/explorer";
|
||||
import { pathJoin } from "../Uploader/core/utils";
|
||||
import { Launch, PlaylistPlay, Subtitles } from "@material-ui/icons";
|
||||
import TextLoading from "../Placeholder/TextLoading";
|
||||
import SelectMenu from "./SelectMenu";
|
||||
|
||||
const Artplayer = React.lazy(() =>
|
||||
import(
|
||||
/* webpackChunkName: "artplayer" */ "artplayer/examples/react/Artplayer"
|
||||
)
|
||||
);
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
layout: {
|
||||
|
|
@ -24,6 +38,14 @@ const useStyles = makeStyles((theme) => ({
|
|||
},
|
||||
player: {
|
||||
borderRadius: "4px",
|
||||
height: 600,
|
||||
},
|
||||
actions: {
|
||||
marginTop: theme.spacing(2),
|
||||
},
|
||||
actionButton: {
|
||||
marginRight: theme.spacing(1),
|
||||
marginTop: theme.spacing(1),
|
||||
},
|
||||
}));
|
||||
|
||||
|
|
@ -31,77 +53,216 @@ function useQuery() {
|
|||
return new URLSearchParams(useLocation().search);
|
||||
}
|
||||
|
||||
let dp = null;
|
||||
let playing = false;
|
||||
|
||||
export default function VideoViewer() {
|
||||
const math = useRouteMatch();
|
||||
const location = useLocation();
|
||||
const query = useQuery();
|
||||
const { id } = useParams();
|
||||
const dispatch = useDispatch();
|
||||
const SetSubTitle = useCallback(
|
||||
(title) => dispatch(changeSubTitle(title)),
|
||||
const ToggleSnackbar = useCallback(
|
||||
(vertical, horizontal, msg, color) =>
|
||||
dispatch(toggleSnackbar(vertical, horizontal, msg, color)),
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!pathHelper.isSharePage(location.pathname)) {
|
||||
const path = query.get("p").split("/");
|
||||
SetSubTitle(path[path.length - 1]);
|
||||
} else {
|
||||
SetSubTitle(query.get("name"));
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
}, [math.params[0], location]);
|
||||
const { title, path } = UseFileSubTitle(query, math, location);
|
||||
const theme = useTheme();
|
||||
const [art, setArt] = useState(null);
|
||||
const history = useHistory();
|
||||
const [files, setFiles] = useState([]);
|
||||
const [subtitles, setSubtitles] = useState([]);
|
||||
const [playlist, setPlaylist] = useState([]);
|
||||
const [subtitleOpen, setSubtitleOpen] = useState(null);
|
||||
const [subtitleSelected, setSubtitleSelected] = useState("");
|
||||
const [playlistOpen, setPlaylistOpen] = useState(null);
|
||||
const [externalPlayerOpen, setExternalPlayerOpen] = useState(null);
|
||||
const isShare = pathHelper.isSharePage(location.pathname);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (
|
||||
playing &&
|
||||
art !== null &&
|
||||
!isMobileSafari() &&
|
||||
document.pictureInPictureEnabled &&
|
||||
dp
|
||||
art.playing
|
||||
) {
|
||||
dp.video.requestPictureInPicture();
|
||||
dp.video.addEventListener(
|
||||
art.pip = true;
|
||||
art.query(".art-video").addEventListener(
|
||||
"leavepictureinpicture",
|
||||
() => {
|
||||
dp.video.pause();
|
||||
art.pause();
|
||||
},
|
||||
false
|
||||
);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
}, [art]);
|
||||
|
||||
const classes = useStyles();
|
||||
|
||||
useEffect(() => {
|
||||
if (art !== null) {
|
||||
const newURL = getPreviewURL(
|
||||
isShare,
|
||||
id,
|
||||
query.get("id"),
|
||||
query.get("share_path")
|
||||
);
|
||||
if (newURL !== art.url) {
|
||||
if (art.subtitle) {
|
||||
art.subtitle.show = false;
|
||||
}
|
||||
art.switchUrl(newURL);
|
||||
if (path && path !== "") {
|
||||
list(basename(path), isShare ? { key: id } : null, "").then(
|
||||
(res) => {
|
||||
setFiles(
|
||||
res.data.objects.filter(
|
||||
(o) => o.type === "file"
|
||||
)
|
||||
);
|
||||
setPlaylist(
|
||||
res.data.objects.filter(
|
||||
(o) =>
|
||||
o.type === "file" &&
|
||||
videoPreviewSuffix.indexOf(
|
||||
o.name
|
||||
.split(".")
|
||||
.pop()
|
||||
.toLowerCase()
|
||||
) !== -1
|
||||
)
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [art, id, location, path]);
|
||||
|
||||
const switchSubtitle = (f) => {
|
||||
if (art !== null) {
|
||||
const fileType = f.name.split(".").pop().toLowerCase();
|
||||
art.subtitle.switch(
|
||||
getPreviewURL(
|
||||
isShare,
|
||||
id,
|
||||
f.id,
|
||||
pathJoin([basename(query.get("share_path")), f.name])
|
||||
),
|
||||
{
|
||||
type: fileType,
|
||||
}
|
||||
);
|
||||
art.subtitle.show = true;
|
||||
setSubtitleSelected(f.name);
|
||||
ToggleSnackbar("top", "center", `字幕切换到:${f.name} `, "info");
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (files.length > 0) {
|
||||
const options = files.filter((f) => {
|
||||
const fileType = f.name.split(".").pop().toLowerCase();
|
||||
if (subtitleSuffix.indexOf(fileType) !== -1) {
|
||||
if (fileNameNoExt(f.name) === fileNameNoExt(title)) {
|
||||
switchSubtitle(f);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
setSubtitles(options);
|
||||
}
|
||||
}, [files]);
|
||||
|
||||
const switchVideo = (file) => {
|
||||
if (isShare) {
|
||||
file.key = id;
|
||||
}
|
||||
history.push(getViewerURL("video", file, isShare));
|
||||
};
|
||||
|
||||
const setSubtitle = (sub) => {
|
||||
setSubtitleOpen(null);
|
||||
switchSubtitle(sub);
|
||||
};
|
||||
|
||||
const startSelectSubTitle = (e) => {
|
||||
if (subtitles.length === 0) {
|
||||
ToggleSnackbar(
|
||||
"top",
|
||||
"right",
|
||||
`视频目录下没有可用字幕文件 (支持:ASS/SRT/VTT)`,
|
||||
"warning"
|
||||
);
|
||||
return;
|
||||
}
|
||||
setSubtitleOpen(e.currentTarget);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={classes.layout}>
|
||||
<Paper className={classes.root} elevation={1}>
|
||||
<DPlayer
|
||||
onLoad={(d) => (dp = d)}
|
||||
onPlay={() => (playing = true)}
|
||||
onEnded={() => (playing = false)}
|
||||
className={classes.player}
|
||||
options={{
|
||||
video: {
|
||||
url:
|
||||
getBaseURL() +
|
||||
(pathHelper.isSharePage(location.pathname)
|
||||
? "/share/preview/" +
|
||||
id +
|
||||
(query.get("share_path") !== ""
|
||||
? "?path=" +
|
||||
encodeURIComponent(
|
||||
query.get("share_path")
|
||||
)
|
||||
: "")
|
||||
: "/file/preview/" + query.get("id")),
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<Suspense fallback={<TextLoading />}>
|
||||
<Artplayer
|
||||
option={{
|
||||
title: title,
|
||||
theme: theme.palette.secondary.main,
|
||||
flip: true,
|
||||
setting: true,
|
||||
playbackRate: true,
|
||||
aspectRatio: true,
|
||||
hotkey: true,
|
||||
pip: true,
|
||||
fullscreen: true,
|
||||
fullscreenWeb: true,
|
||||
}}
|
||||
className={classes.player}
|
||||
getInstance={(a) => setArt(a)}
|
||||
/>
|
||||
</Suspense>
|
||||
</Paper>
|
||||
<div className={classes.actions}>
|
||||
<Button
|
||||
onClick={startSelectSubTitle}
|
||||
className={classes.actionButton}
|
||||
startIcon={<Subtitles />}
|
||||
variant="outlined"
|
||||
>
|
||||
选择字幕
|
||||
</Button>
|
||||
{playlist.length > 0 && (
|
||||
<Button
|
||||
onClick={(e) => setPlaylistOpen(e.currentTarget)}
|
||||
className={classes.actionButton}
|
||||
startIcon={<PlaylistPlay />}
|
||||
variant="outlined"
|
||||
>
|
||||
播放列表
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
className={classes.actionButton}
|
||||
startIcon={<Launch />}
|
||||
variant="outlined"
|
||||
>
|
||||
用外部播放器打开
|
||||
</Button>
|
||||
</div>
|
||||
<SelectMenu
|
||||
selected={subtitleSelected}
|
||||
options={subtitles}
|
||||
callback={setSubtitle}
|
||||
anchorEl={subtitleOpen}
|
||||
handleClose={() => setSubtitleOpen(null)}
|
||||
/>
|
||||
<SelectMenu
|
||||
selected={title}
|
||||
options={playlist}
|
||||
callback={switchVideo}
|
||||
anchorEl={playlistOpen}
|
||||
handleClose={() => setPlaylistOpen(null)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ export const msDocPreviewSuffix = [
|
|||
"xlsx",
|
||||
"xls",
|
||||
];
|
||||
export const subtitleSuffix = ["ass", "srt", "vrr"];
|
||||
export const audioPreviewSuffix = ["mp3", "ogg", "flac"];
|
||||
export const videoPreviewSuffix = ["mp4", "mkv", "webm"];
|
||||
export const pdfPreviewSuffix = ["pdf"];
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
import { useDispatch } from "react-redux";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { changeSubTitle } from "../redux/viewUpdate/action";
|
||||
import pathHelper from "../utils/page";
|
||||
|
||||
export default function UseFileSubTitle(query, math, location) {
|
||||
const dispatch = useDispatch();
|
||||
const [title, setTitle] = useState("");
|
||||
const [path, setPath] = useState("");
|
||||
const SetSubTitle = useCallback(
|
||||
(title) => dispatch(changeSubTitle(title)),
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!pathHelper.isSharePage(location.pathname)) {
|
||||
const path = query.get("p").split("/");
|
||||
setPath(query.get("p"));
|
||||
SetSubTitle(path[path.length - 1]);
|
||||
setTitle(path[path.length - 1]);
|
||||
} else {
|
||||
SetSubTitle(query.get("name"));
|
||||
setTitle(query.get("name"));
|
||||
setPath(query.get("share_path"));
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
}, [math.params[0], location]);
|
||||
|
||||
return { title, path };
|
||||
}
|
||||
|
|
@ -7,6 +7,22 @@ export const getBaseURL = () => {
|
|||
return baseURL;
|
||||
};
|
||||
|
||||
export const getPreviewURL = (
|
||||
isShare: boolean,
|
||||
shareID: any,
|
||||
fileID: any,
|
||||
path: any
|
||||
): string => {
|
||||
return (
|
||||
getBaseURL() +
|
||||
(isShare
|
||||
? "/share/preview/" +
|
||||
shareID +
|
||||
(path !== "" ? "?path=" + encodeURIComponent(path) : "")
|
||||
: "/file/preview/" + fileID)
|
||||
);
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
|
||||
// @ts-ignore
|
||||
const instance = axios.create({
|
||||
|
|
|
|||
|
|
@ -360,6 +360,36 @@ export const startBatchDownload = (
|
|||
};
|
||||
};
|
||||
|
||||
export const getViewerURL = (
|
||||
viewer: string,
|
||||
file: any,
|
||||
isShare: boolean | ""
|
||||
): string => {
|
||||
const previewPath = getPreviewPath(file);
|
||||
if (isShare) {
|
||||
return (
|
||||
"/s/" +
|
||||
file.key +
|
||||
`/${viewer}?name=` +
|
||||
encodeURIComponent(file.name) +
|
||||
"&share_path=" +
|
||||
previewPath
|
||||
);
|
||||
}
|
||||
|
||||
return `/${viewer}?p=` + previewPath + "&id=" + file.id;
|
||||
};
|
||||
|
||||
export const openViewer = (
|
||||
viewer: string,
|
||||
file: any,
|
||||
isShare: boolean | ""
|
||||
) => {
|
||||
return (dispatch: any, getState: any) => {
|
||||
dispatch(push(getViewerURL(viewer, file, isShare)));
|
||||
};
|
||||
};
|
||||
|
||||
export const openPreview = () => {
|
||||
return (dispatch: any, getState: any) => {
|
||||
const {
|
||||
|
|
@ -379,102 +409,27 @@ export const openPreview = () => {
|
|||
}
|
||||
|
||||
dispatch(changeContextMenu("file", false));
|
||||
const previewPath = getPreviewPath(selected[0]);
|
||||
switch (isPreviewable(selected[0].name)) {
|
||||
case "img":
|
||||
dispatch(showImgPreivew(selected[0]));
|
||||
return;
|
||||
case "msDoc":
|
||||
if (isShare) {
|
||||
dispatch(
|
||||
push(
|
||||
selected[0].key +
|
||||
"/doc?name=" +
|
||||
encodeURIComponent(selected[0].name) +
|
||||
"&share_path=" +
|
||||
previewPath
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
dispatch(
|
||||
push("/doc?p=" + previewPath + "&id=" + selected[0].id)
|
||||
);
|
||||
dispatch(openViewer("doc", selected[0], isShare));
|
||||
return;
|
||||
case "audio":
|
||||
//if (isShare) {
|
||||
// dispatch(openMusicDialog());
|
||||
//}else{
|
||||
dispatch(showAudioPreview(selected[0]));
|
||||
//}
|
||||
return;
|
||||
case "video":
|
||||
if (isShare) {
|
||||
dispatch(
|
||||
push(
|
||||
selected[0].key +
|
||||
"/video?name=" +
|
||||
encodeURIComponent(selected[0].name) +
|
||||
"&share_path=" +
|
||||
previewPath
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
dispatch(
|
||||
push("/video?p=" + previewPath + "&id=" + selected[0].id)
|
||||
);
|
||||
dispatch(openViewer("video", selected[0], isShare));
|
||||
return;
|
||||
case "pdf":
|
||||
if (isShare) {
|
||||
dispatch(
|
||||
push(
|
||||
selected[0].key +
|
||||
"/pdf?name=" +
|
||||
encodeURIComponent(selected[0].name) +
|
||||
"&share_path=" +
|
||||
previewPath
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
dispatch(
|
||||
push("/pdf?p=" + previewPath + "&id=" + selected[0].id)
|
||||
);
|
||||
dispatch(openViewer("pdf", selected[0], isShare));
|
||||
return;
|
||||
case "edit":
|
||||
if (isShare) {
|
||||
dispatch(
|
||||
push(
|
||||
selected[0].key +
|
||||
"/text?name=" +
|
||||
encodeURIComponent(selected[0].name) +
|
||||
"&share_path=" +
|
||||
previewPath
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
dispatch(
|
||||
push("/text?p=" + previewPath + "&id=" + selected[0].id)
|
||||
);
|
||||
dispatch(openViewer("text", selected[0], isShare));
|
||||
return;
|
||||
case "code":
|
||||
if (isShare) {
|
||||
dispatch(
|
||||
push(
|
||||
selected[0].key +
|
||||
"/code?name=" +
|
||||
encodeURIComponent(selected[0].name) +
|
||||
"&share_path=" +
|
||||
previewPath
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
dispatch(
|
||||
push("/code?p=" + previewPath + "&id=" + selected[0].id)
|
||||
);
|
||||
dispatch(openViewer("code", selected[0], isShare));
|
||||
return;
|
||||
default:
|
||||
dispatch(openLoadingDialog("获取下载地址..."));
|
||||
|
|
|
|||
|
|
@ -149,6 +149,9 @@ export function pathJoin(parts, sep) {
|
|||
}
|
||||
|
||||
export function basename(path) {
|
||||
if (!path) {
|
||||
return "";
|
||||
}
|
||||
const pathList = path.split("/");
|
||||
pathList.pop();
|
||||
return pathList.join("/") === "" ? "/" : pathList.join("/");
|
||||
|
|
@ -159,6 +162,10 @@ export function filename(path) {
|
|||
return pathList.pop();
|
||||
}
|
||||
|
||||
export function fileNameNoExt(filename) {
|
||||
return filename.substring(0, filename.lastIndexOf(".")) || filename;
|
||||
}
|
||||
|
||||
export function randomStr(length) {
|
||||
let result = "";
|
||||
const characters =
|
||||
|
|
|
|||
22
yarn.lock
22
yarn.lock
|
|
@ -2357,6 +2357,14 @@ arrify@^1.0.1:
|
|||
resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
|
||||
integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=
|
||||
|
||||
artplayer@^4.3.4:
|
||||
version "4.3.4"
|
||||
resolved "https://registry.yarnpkg.com/artplayer/-/artplayer-4.3.4.tgz#ea578552530258d71f231b207c8e32c1248a3b47"
|
||||
integrity sha512-Ma60cC1n4G/CFPo8zOGglRU1u/OFnJGp1hJ4fe5rWIFFsCDx3D6OvfBZDMlkRreHQt8DBW0BF/oGGM6Rq0nv1g==
|
||||
dependencies:
|
||||
option-validator "^2.0.6"
|
||||
screenfull "^6.0.1"
|
||||
|
||||
asap@^2.0.6, asap@~2.0.6:
|
||||
version "2.0.6"
|
||||
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
|
||||
|
|
@ -7069,7 +7077,7 @@ kind-of@^5.0.0:
|
|||
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d"
|
||||
integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==
|
||||
|
||||
kind-of@^6.0.0, kind-of@^6.0.2:
|
||||
kind-of@^6.0.0, kind-of@^6.0.2, kind-of@^6.0.3:
|
||||
version "6.0.3"
|
||||
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
|
||||
integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==
|
||||
|
|
@ -8051,6 +8059,13 @@ optimize-css-assets-webpack-plugin@5.0.3:
|
|||
cssnano "^4.1.10"
|
||||
last-call-webpack-plugin "^3.0.0"
|
||||
|
||||
option-validator@^2.0.6:
|
||||
version "2.0.6"
|
||||
resolved "https://registry.yarnpkg.com/option-validator/-/option-validator-2.0.6.tgz#a314dae65e26db5f948ef0ff96fc88f18bb76ed6"
|
||||
integrity sha512-tmZDan2LRIRQyhUGvkff68/O0R8UmF+Btmiiz0SmSw2ng3CfPZB9wJlIjHpe/MKUZqyIZkVIXCrwr1tIN+0Dzg==
|
||||
dependencies:
|
||||
kind-of "^6.0.3"
|
||||
|
||||
optionator@^0.8.1, optionator@^0.8.3:
|
||||
version "0.8.3"
|
||||
resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495"
|
||||
|
|
@ -10228,6 +10243,11 @@ schema-utils@^2.0.0, schema-utils@^2.0.1, schema-utils@^2.2.0:
|
|||
ajv "^6.12.4"
|
||||
ajv-keywords "^3.5.2"
|
||||
|
||||
screenfull@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/screenfull/-/screenfull-6.0.1.tgz#3b71e6f06b72d817a8d3be73c45ebe71fa8da1ce"
|
||||
integrity sha512-yzQW+j4zMUBQC51xxWaoDYjxOtl8Kn+xvue3p6v/fv2pIi1jH4AldgVLU8TBfFVgH2x3VXlf3+YiA/AYIPlaew==
|
||||
|
||||
seamless-immutable@^7.1.3:
|
||||
version "7.1.4"
|
||||
resolved "https://registry.yarnpkg.com/seamless-immutable/-/seamless-immutable-7.1.4.tgz#6e9536def083ddc4dea0207d722e0e80d0f372f8"
|
||||
|
|
|
|||
Loading…
Reference in New Issue