mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-24 12:58:34 +00:00
Feature/video player in details (#8115)
* details support video player * optimize * pause when moving out of player --------- Co-authored-by: zhouwenxuan <aries@Mac.local>
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
.detail-body .detail-image {
|
.detail-body .detail-image {
|
||||||
height: 144px;
|
height: 144px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -54,3 +55,8 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
transform: translateY(-50px);
|
transform: translateY(-50px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.detail-body .video-js {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { siteRoot, thumbnailSizeForGrid, enableSeafileAI } from '../../../utils/constants';
|
import { siteRoot, thumbnailSizeForGrid, enableSeafileAI, fileServerRoot, MimetypesKind } from '../../../utils/constants';
|
||||||
import { seafileAPI } from '../../../utils/seafile-api';
|
import { seafileAPI } from '../../../utils/seafile-api';
|
||||||
import { Utils } from '../../../utils/utils';
|
import { Utils } from '../../../utils/utils';
|
||||||
import toaster from '../../toast';
|
import toaster from '../../toast';
|
||||||
@@ -13,6 +13,7 @@ import AIIcon from '../../../metadata/components/metadata-details/ai-icon';
|
|||||||
import SettingsIcon from '../../../metadata/components/metadata-details/settings-icon';
|
import SettingsIcon from '../../../metadata/components/metadata-details/settings-icon';
|
||||||
import { eventBus } from '../../common/event-bus';
|
import { eventBus } from '../../common/event-bus';
|
||||||
import { EVENT_BUS_TYPE } from '../../../metadata/constants';
|
import { EVENT_BUS_TYPE } from '../../../metadata/constants';
|
||||||
|
import VideoPlayer from '../../video-player';
|
||||||
|
|
||||||
import './index.css';
|
import './index.css';
|
||||||
|
|
||||||
@@ -22,7 +23,9 @@ class DirentDetails extends React.Component {
|
|||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
direntDetail: '',
|
direntDetail: '',
|
||||||
|
isHovering: false,
|
||||||
};
|
};
|
||||||
|
this.videoPlayerRef = React.createRef();
|
||||||
}
|
}
|
||||||
|
|
||||||
updateDetail = (repoID, dirent, direntPath) => {
|
updateDetail = (repoID, dirent, direntPath) => {
|
||||||
@@ -57,21 +60,78 @@ class DirentDetails extends React.Component {
|
|||||||
eventBus.dispatch(EVENT_BUS_TYPE.CLEAR_MAP_INSTANCE);
|
eventBus.dispatch(EVENT_BUS_TYPE.CLEAR_MAP_INSTANCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderImage = () => {
|
handleVideoHover = (isHovering) => {
|
||||||
|
this.setState({ isHovering }, () => {
|
||||||
|
if (this.videoPlayerRef.current) {
|
||||||
|
const player = this.videoPlayerRef.current.player;
|
||||||
|
if (isHovering) {
|
||||||
|
player.play();
|
||||||
|
} else {
|
||||||
|
player.pause();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
getImageSrc = () => {
|
||||||
|
const { repoID, path, dirent, currentRepoInfo } = this.props;
|
||||||
|
return currentRepoInfo.encrypted
|
||||||
|
? `${siteRoot}repo/${repoID}/raw${Utils.encodePath(`${path === '/' ? '' : path}/${dirent.name}`)}`
|
||||||
|
: `${siteRoot}thumbnail/${repoID}/${thumbnailSizeForGrid}${Utils.encodePath(`${path === '/' ? '' : path}/${dirent.name}`)}?mtime=${this.state.direntDetail.mtime}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
getVideoSrc = () => {
|
||||||
|
const { repoID, path, dirent } = this.props;
|
||||||
|
const encodedPath = Utils.encodePath(Utils.joinPath(path, dirent.name));
|
||||||
|
return `${fileServerRoot}repos/${repoID}/files${encodedPath}?op=download`;
|
||||||
|
};
|
||||||
|
|
||||||
|
renderMedia = () => {
|
||||||
const { dirent } = this.props;
|
const { dirent } = this.props;
|
||||||
if (!dirent) return null;
|
if (!dirent) return null;
|
||||||
const isImg = Utils.imageCheck(dirent.name);
|
|
||||||
if (!isImg) return null;
|
const isImage = Utils.imageCheck(dirent.name);
|
||||||
const { repoID, path, currentRepoInfo } = this.props;
|
const isVideo = Utils.videoCheck(dirent.name);
|
||||||
let src = '';
|
if (!isImage && !isVideo) return null;
|
||||||
if (currentRepoInfo.encrypted) {
|
|
||||||
src = `${siteRoot}repo/${repoID}/raw` + Utils.encodePath(`${path === '/' ? '' : path}/${dirent.name}`);
|
const src = this.getImageSrc();
|
||||||
} else {
|
const videoSrc = this.getVideoSrc();
|
||||||
src = `${siteRoot}thumbnail/${repoID}/${thumbnailSizeForGrid}` + Utils.encodePath(`${path === '/' ? '' : path}/${dirent.name}`) + '?mtime=' + this.state.direntDetail.mtime;
|
const mimetype = MimetypesKind[dirent.name.split('.').pop().toLowerCase()] || 'video/mp4';
|
||||||
}
|
|
||||||
|
let options = {
|
||||||
|
autoplay: false,
|
||||||
|
preload: 'auto',
|
||||||
|
muted: true,
|
||||||
|
sources: [{
|
||||||
|
src: videoSrc,
|
||||||
|
type: mimetype
|
||||||
|
}],
|
||||||
|
controls: true,
|
||||||
|
bigPlayButton: false,
|
||||||
|
controlBar: {
|
||||||
|
playToggle: false,
|
||||||
|
volumnPanel: false,
|
||||||
|
fullscreenToggle: false,
|
||||||
|
pictureInPictureToggle: false,
|
||||||
|
children: ['progressControl', 'remainingTimeDisplay']
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="detail-image">
|
<div
|
||||||
<img src={src} alt="" />
|
className="detail-image"
|
||||||
|
onMouseEnter={() => this.handleVideoHover(true)}
|
||||||
|
onMouseLeave={() => this.handleVideoHover(false)}
|
||||||
|
>
|
||||||
|
{isVideo ? (
|
||||||
|
<VideoPlayer
|
||||||
|
id={`video-player-${dirent.id}`}
|
||||||
|
ref={this.videoPlayerRef}
|
||||||
|
{...options}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<img src={src} alt="" />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -84,7 +144,7 @@ class DirentDetails extends React.Component {
|
|||||||
<Detail>
|
<Detail>
|
||||||
<Header title={dirent?.name || ''} icon={Utils.getDirentIcon(dirent, true)} onClose={this.props.onClose} />
|
<Header title={dirent?.name || ''} icon={Utils.getDirentIcon(dirent, true)} onClose={this.props.onClose} />
|
||||||
<Body>
|
<Body>
|
||||||
{this.renderImage()}
|
{this.renderMedia()}
|
||||||
</Body>
|
</Body>
|
||||||
</Detail>
|
</Detail>
|
||||||
);
|
);
|
||||||
@@ -106,7 +166,7 @@ class DirentDetails extends React.Component {
|
|||||||
<SettingsIcon />
|
<SettingsIcon />
|
||||||
</Header>
|
</Header>
|
||||||
<Body>
|
<Body>
|
||||||
{this.renderImage()}
|
{this.renderMedia()}
|
||||||
{dirent && direntDetail && (
|
{dirent && direntDetail && (
|
||||||
<div className="detail-content">
|
<div className="detail-content">
|
||||||
{dirent.type !== 'file' ? (
|
{dirent.type !== 'file' ? (
|
||||||
|
@@ -23,6 +23,13 @@ class VideoPlayer extends React.Component {
|
|||||||
this.player.el().focus();
|
this.player.el().focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps) {
|
||||||
|
// Update sources if changed
|
||||||
|
if (JSON.stringify(this.props.sources) !== JSON.stringify(prevProps.sources)) {
|
||||||
|
this.player.src(this.props.sources[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// destroy player on unmount
|
// destroy player on unmount
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
if (this.player) {
|
if (this.player) {
|
||||||
|
@@ -60,7 +60,7 @@ class Dirent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
toJson() {
|
toJson() {
|
||||||
return {
|
const json = {
|
||||||
id: this.id,
|
id: this.id,
|
||||||
name: this.name,
|
name: this.name,
|
||||||
mtime: this.mtime,
|
mtime: this.mtime,
|
||||||
@@ -71,6 +71,10 @@ class Dirent {
|
|||||||
modifier_email: this.modifier_email,
|
modifier_email: this.modifier_email,
|
||||||
modifier_contact_email: this.modifier_contact_email,
|
modifier_contact_email: this.modifier_contact_email,
|
||||||
};
|
};
|
||||||
|
if (this.encoded_thumbnail_src) {
|
||||||
|
json.encoded_thumbnail_src = this.encoded_thumbnail_src;
|
||||||
|
}
|
||||||
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user