Merge branch 'dev_aliyun' of https://bdgit.educoder.net/Hjqreturn/educoder into dev_chen

PCqiandao
杨树林 5 years ago
commit bcdc47879a

@ -38,9 +38,9 @@ class CreateWatchVideoService < ApplicationService
end end
else else
# 开始播放时记录一次 # 开始播放时记录一次
if params[:course_video_id].present? if params[:course_id].present?
# 课堂视频 # 课堂视频
course_video = CourseVideo.find(params[:course_video_id]) course_video = CourseVideo.find_by(video_id: params[:video_id], course_id: params[:course_id])
watch_course_video = WatchCourseVideo.find_or_initialize_by(course_video_id: course_video.id, user_id: user.id) do |d| watch_course_video = WatchCourseVideo.find_or_initialize_by(course_video_id: course_video.id, user_id: user.id) do |d|
d.start_at = current_time d.start_at = current_time
d.duration = params[:duration] d.duration = params[:duration]
@ -52,7 +52,7 @@ class CreateWatchVideoService < ApplicationService
watch_course_video.save! unless watch_course_video.persisted? watch_course_video.save! unless watch_course_video.persisted?
else else
# 非课堂视频 # 非课堂视频
video = Video.find_by(params[:video_id]) video = Video.find(params[:video_id])
watch_video_history = build_video_log(current_time, video.id) watch_video_history = build_video_log(current_time, video.id)
watch_video_history.save! watch_video_history.save!
end end

@ -13,6 +13,7 @@ import VideoPanel from './video-play'
import './video.css'; import './video.css';
import '../../user/usersInfo/video/InfosVideo.css' import '../../user/usersInfo/video/InfosVideo.css'
import axios from 'axios'; import axios from 'axios';
import { logWatchHistory } from "../../../services/video-service";
const DEFAULT_VIDEO_WIDTH_IN_MD = "90%" // 400 const DEFAULT_VIDEO_WIDTH_IN_MD = "90%" // 400
const DEFAULT_VIDEO_HEIGHT_IN_MD = "55%" // 400 const DEFAULT_VIDEO_HEIGHT_IN_MD = "55%" // 400
@ -31,8 +32,8 @@ class Video extends Component {
videoVisible: false, videoVisible: false,
visible: false, visible: false,
moveVisible:false, moveVisible: false,
moveVideoId:undefined moveVideoId: undefined
} }
} }
@ -70,7 +71,6 @@ class Video extends Component {
} }
} }
// 编辑成功后回调的方法 // 编辑成功后回调的方法
editSuccess = () => { editSuccess = () => {
this.props.showNotification("视频信息修改成功!"); this.props.showNotification("视频信息修改成功!");
@ -82,7 +82,7 @@ class Video extends Component {
let videoId = { let videoId = {
videoId: item.id, videoId: item.id,
title: item.title, title: item.title,
link:item.link link: item.link
} }
this.setState({ this.setState({
videoId, videoId,
@ -118,8 +118,6 @@ class Video extends Component {
_clipboard = null; _clipboard = null;
} }
} else { } else {
// videoEl.current && videoEl.current.play()
setTimeout(() => { setTimeout(() => {
if (!_clipboard) { if (!_clipboard) {
_clipboard = new ClipboardJS('.copybtn'); _clipboard = new ClipboardJS('.copybtn');
@ -148,7 +146,7 @@ class Video extends Component {
axios.delete(url, { axios.delete(url, {
params: { params: {
video_id: item.id, video_id: item.id,
is_link:item.link ? true : undefined is_link: item.link ? true : undefined
} }
}).then(result => { }).then(result => {
if (result) { if (result) {
@ -168,30 +166,30 @@ class Video extends Component {
} }
// 移动到 // 移动到
moveVideo=(id,flag)=>{ moveVideo = (id, flag) => {
if(!flag){ if (!flag) {
this.setState({ this.setState({
moveVisible:true, moveVisible: true,
moveVideoId:id moveVideoId: id
}) })
}else{ } else {
this.props.define({ this.props.define({
title:'提示', title: '提示',
content:"您不是课堂管理员或者视频发布者,暂不能移动视频。", content: "您不是课堂管理员或者视频发布者,暂不能移动视频。",
}) })
} }
} }
setMoveVisible=(flag)=>{ setMoveVisible = (flag) => {
this.setState({ this.setState({
moveVisible:flag, moveVisible: flag,
moveVideoId:undefined moveVideoId: undefined
}) })
} }
render() { render() {
const { visible, videoVisible, videoId , moveVisible , moveVideoId } = this.state; const { visible, videoVisible, videoId, moveVisible, moveVideoId } = this.state;
const CourseId = this.props.match.params.coursesId; const CourseId = this.props.match.params.coursesId;
const VID=this.props.match.params.videoId; const VID = this.props.match.params.videoId;
const login = this.props.user && this.props.user.login; const login = this.props.user && this.props.user.login;
const _inputValue = videoId && this.getCopyText(videoId.file_url, videoId.cover_url); const _inputValue = videoId && this.getCopyText(videoId.file_url, videoId.cover_url);
@ -201,7 +199,7 @@ class Video extends Component {
const { videos, upload, uploadVideo, videoData, changePage, pageSize, page } = this.props; const { videos, upload, uploadVideo, videoData, changePage, pageSize, page } = this.props;
const operation = admin || business; const operation = admin || business;
const {course_identity} = this.props.coursedata; const { course_identity } = this.props.coursedata;
const flagMove = parseInt(course_identity) < 5; const flagMove = parseInt(course_identity) < 5;
return ( return (
@ -214,8 +212,8 @@ class Video extends Component {
{...this.props} {...this.props}
visible={moveVisible} visible={moveVisible}
mainId={videoData && videoData.course_module_id} mainId={videoData && videoData.course_module_id}
setMoveVisible={(flag)=>this.setMoveVisible(flag)} setMoveVisible={(flag) => this.setMoveVisible(flag)}
successFunc={()=>uploadVideo()} successFunc={() => uploadVideo()}
id={moveVideoId} id={moveVideoId}
></MoveBox> ></MoveBox>
<HeadlessModal <HeadlessModal
@ -224,7 +222,7 @@ class Video extends Component {
className="showVideoModal" className="showVideoModal"
width={800 - 1} width={800 - 1}
> >
{videoId && <VideoPanel src={videoId.file_url} />} {videoId && <VideoPanel src={videoId.file_url} videoId={videoId.videoId} courseId={CourseId} logWatchHistory={logWatchHistory} />}
<div className="df copyLine"> <div className="df copyLine">
<Input value={_inputValue} <Input value={_inputValue}
@ -242,7 +240,7 @@ class Video extends Component {
{ {
videos && videos.length > 0 ? videos && videos.length > 0 ?
<React.Fragment> <React.Fragment>
<p className="font-grey-9 mt20 mb20 pl5"> <span className="color-orange">{videoData && videoData.count}</span> </p> <p className="font-grey-9 mt20 mb20 pl5"> <span className="color-orange">{videoData && videoData.count}</span> </p>
<div className="videoContent"> <div className="videoContent">
{ {
@ -259,7 +257,7 @@ class Video extends Component {
getCopyText={this.getCopyText} getCopyText={this.getCopyText}
operation={operation || item.user_id === user_id} operation={operation || item.user_id === user_id}
deleteVideo={(admin || item.user_id === user_id) ? this.deleteVideo : undefined} deleteVideo={(admin || item.user_id === user_id) ? this.deleteVideo : undefined}
moveVideo={videoData && videoData.has_category && flagMove ? ()=>this.moveVideo(item.id,(course_identity > 2 && item.user_id !== user_id)):undefined} moveVideo={videoData && videoData.has_category && flagMove ? () => this.moveVideo(item.id, (course_identity > 2 && item.user_id !== user_id)) : undefined}
> >
</VideoInReviewItem> </VideoInReviewItem>
) )

@ -1,37 +0,0 @@
import React, { useEffect, useRef } from 'react'
export default ({ url }) => {
const ref = useRef()
useEffect(() => {
let player = null
if (window.flvjs.isSupported) {
player = window.flvjs.createPlayer({
type: 'flv',
volume: 0.8,
cors: true,
url,
muted: false
})
if (ref.current) {
player.attachMediaElement(ref.current)
player.load()
player.play()
}
}
return () => {
if (player) {
player.unload()
player.pause()
player.destroy()
player = null
}
}
}, [url, ref.current])
return (
<video ref={ref} controls autoPlay={true} muted={false} className="flv-player"></video>
)
}

@ -1,18 +1,139 @@
import React, { Fragment } from 'react' import React, { useRef, useEffect, useCallback } from 'react'
import ReactFlvPlayer from './flv-player'
const regex = /(android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini)/i
// https://www.showdoc.cc/educoder?page_id=4029884447803706
export default ({ src, videoId, logWatchHistory, courseId = null }) => {
export default ({ src }) => {
const suf = src.split('.').pop() const suf = src.split('.').pop()
const isFlv = suf === 'flv' const isFlv = suf === 'flv'
return ( const el = useRef()
<Fragment>
{ const deviceMatch = navigator.userAgent.toLowerCase().match(regex)
isFlv ? <ReactFlvPlayer url={src} isMuted={true} /> : <video src={src} controls autoPlay={true} controlsList="nodownload"> const device = deviceMatch ? deviceMatch[0] : 'pc'
<source type={`video/${suf}`} src={src} />
您的浏览器不支持 video 标签 let totalDuration = 0
</video> let totalTimePlayed = 0
let sumTimePlayed = 0
let lastUpdatedTime = 0
let lastEffectUpdatedTime = 0
let logId = null
let initLog = false
let timeTick = 20 // 20s
let logCount = 1
const log = useCallback((callback) => {
let params = {}
if (logId) {
params['log_id'] = logId
params['watch_duration'] = totalTimePlayed //
params['total_duration'] = sumTimePlayed //
} else {
if (courseId) {
params['course_video_id'] = videoId
} else {
params['video_id'] = videoId
}
params['duration'] = totalDuration
params['device'] = device
}
async function getLogId() {
let id = await logWatchHistory(params)
logId = id
if (callback) {
callback()
}
}
getLogId()
}, [videoId, courseId])
useEffect(() => {
let player = null
if (window.flvjs.isSupported && isFlv) {
player = window.flvjs.createPlayer({
type: 'flv',
volume: 0.8,
cors: true,
url: src,
muted: false
})
if (el.current) {
player.attachMediaElement(el.current)
player.load()
}
} else {
el.current.setAttribute('src', src)
}
return () => {
if (player) {
player.unload()
player.pause()
player.destroy()
player = null
}
}
}, [el, isFlv, src])
useEffect(() => {
function onPlay() {
if (!initLog) {
initLog = true
log()
}
}
async function onEnded() {
log(() => {
logId = null
logCount = 1
totalTimePlayed = 0
lastUpdatedTime = 0
sumTimePlayed = 0
initLog = false
lastEffectUpdatedTime = 0
})
}
function onTimeupdate() {
let newTime = el.current.currentTime
let timeDiff = newTime - lastUpdatedTime
let effectTimeDiff = newTime - lastEffectUpdatedTime
if (effectTimeDiff > 0) {
totalTimePlayed += effectTimeDiff
lastEffectUpdatedTime = newTime
}
sumTimePlayed += Math.abs(timeDiff)
lastUpdatedTime = newTime
if (sumTimePlayed - logCount * timeTick >= 0) {
logCount++
log()
} }
</Fragment> }
function onCanPlay() {
totalDuration = el.current.duration
if (totalDuration <= 20) {
timeTick = totalDuration / 3
}
el.current.addEventListener('play', onPlay)
}
el.current.addEventListener('canplay', onCanPlay)
el.current.addEventListener('ended', onEnded)
el.current.addEventListener('timeupdate', onTimeupdate)
return () => {
el.current.removeEventListener('canplay', onCanPlay)
el.current.removeEventListener('play', onPlay)
el.current.removeEventListener('ended', onEnded)
el.current.removeEventListener('timeupdate', onTimeupdate)
}
}, [el, src])
return (
<video ref={el} controls autoPlay={false} controlsList="nodownload" muted={false} />
) )
} }

@ -1,22 +1,14 @@
import React, { useState, useEffect, useContext, useRef, memo } from 'react'; import React, { useContext } from 'react';
import {Link} from 'react-router-dom';
import { getUrl2, isDev, ThemeContext } from 'educoder' import { ThemeContext } from 'educoder'
import { Modal } from 'antd' import { Modal } from 'antd'
function HeadlessModal (props) { function HeadlessModal(props) {
// const [ visible, setVisible ] = useState(false)
const theme = useContext(ThemeContext); const theme = useContext(ThemeContext);
const { category, visible, setVisible, className, width } = props; const { visible, setVisible, className, width } = props;
useEffect(() => {
}, [])
return ( return (
<Modal <Modal
visible={visible} visible={visible}
className={`headless ${className}`} className={`headless ${className}`}
title={null} title={null}
@ -48,7 +40,7 @@ function HeadlessModal (props) {
top: -13px; top: -13px;
} }
`}</style> `}</style>
<i className="iconfont icon-htmal5icon19 closeBtn" onClick={ () => setVisible(false) }></i> <i className="iconfont icon-htmal5icon19 closeBtn" onClick={() => setVisible(false)}></i>
{props.children} {props.children}
</Modal> </Modal>
) )

@ -10,6 +10,7 @@ import HeadlessModal from '../common/HeadlessModal'
import ClipboardJS from 'clipboard' import ClipboardJS from 'clipboard'
import VideoPlay from '../../../courses/Video/video-play'; import VideoPlay from '../../../courses/Video/video-play';
import { logWatchHistory } from '../../../../services/video-service';
function useModal(initValue) { function useModal(initValue) {
const [visible, setVisible] = useState(initValue) const [visible, setVisible] = useState(initValue)
@ -219,32 +220,32 @@ function InfoVideo(props) {
} }
function deleteVideo(item){ function deleteVideo(item) {
props.confirm({ props.confirm({
content: '该视频将被删除,不可恢复', content: '该视频将被删除,不可恢复',
subContent: '是否确认删除?', subContent: '是否确认删除?',
onOk: () => { onOk: () => {
const url = `/users/${user && user.login}/videos/${item.id}.json`; const url = `/users/${user && user.login}/videos/${item.id}.json`;
axios.delete(url).then(result => { axios.delete(url).then(result => {
if (result) { if (result) {
props.showNotification(`视频删除成功!`); props.showNotification(`视频删除成功!`);
if (pageObj.current === 1) { if (pageObj.current === 1) {
if (categoryObj.category === 'all') { if (categoryObj.category === 'all') {
fetchvideos() fetchvideos()
} else { } else {
fetchReviewvideos() fetchReviewvideos()
} }
} else { } else {
pageObj.onChange(1) pageObj.onChange(1)
}
} }
} }).catch(error => {
}).catch(error => { console.log(error);
console.log(error); })
})
}, },
onCancel() { onCancel() {
console.log('Cancel'); console.log('Cancel');
}, },
}); });
} }
@ -267,7 +268,7 @@ function InfoVideo(props) {
className="showVideoModal" className="showVideoModal"
width={800 - 1} width={800 - 1}
> >
{videoModalObj.visible && <VideoPlay src={videoId.file_url} />} {videoModalObj.visible && <VideoPlay src={videoId.file_url} videoId={videoId.videoId} logWatchHistory={logWatchHistory} />}
<div className="df copyLine"> <div className="df copyLine">
<Input value={_inputValue} <Input value={_inputValue}
className="dark" className="dark"

@ -0,0 +1,11 @@
import axios from 'axios'
export async function logWatchHistory(params) {
try {
const response = await axios.post('/watch_video_histories.json', params)
return response.data.log_id
} catch (error) {
console.log(error)
}
return 0
}
Loading…
Cancel
Save