diff options
Diffstat (limited to 'src/client/views/nodes/VideoBox.tsx')
-rw-r--r-- | src/client/views/nodes/VideoBox.tsx | 176 |
1 files changed, 148 insertions, 28 deletions
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 22ff5c5ad..81c429a02 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -1,43 +1,163 @@ -import React = require("react") -import { FieldViewProps, FieldView } from './FieldView'; -import { FieldWaiting } from '../../../fields/Field'; -import { observer } from "mobx-react" -import { VideoField } from '../../../fields/VideoField'; -import "./VideoBox.scss" -import { ContextMenu } from "../../views/ContextMenu"; -import { observable, action } from 'mobx'; -import { KeyStore } from '../../../fields/KeyStore'; +import React = require("react"); +import { observer } from "mobx-react"; +import { FieldView, FieldViewProps } from './FieldView'; +import * as rp from "request-promise"; +import "./VideoBox.scss"; +import { action, IReactionDisposer, reaction, observable } from "mobx"; +import { DocComponent } from "../DocComponent"; +import { positionSchema } from "./DocumentView"; +import { makeInterface } from "../../../new_fields/Schema"; +import { pageSchema } from "./ImageBox"; +import { Cast, FieldValue, NumCast } from "../../../new_fields/Types"; +import { VideoField } from "../../../new_fields/URLField"; +import Measure from "react-measure"; +import "./VideoBox.scss"; +import { RouteStore } from "../../../server/RouteStore"; +import { DocServer } from "../../DocServer"; + +type VideoDocument = makeInterface<[typeof positionSchema, typeof pageSchema]>; +const VideoDocument = makeInterface(positionSchema, pageSchema); @observer -export class VideoBox extends React.Component<FieldViewProps> { +export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoDocument) { + private _reactionDisposer?: IReactionDisposer; + private _videoRef: HTMLVideoElement | null = null; + private _loaded: boolean = false; + @observable _playTimer?: NodeJS.Timeout = undefined; + @observable _fullScreen = false; + @observable public Playing: boolean = false; + public static LayoutString() { return FieldView.LayoutString(VideoBox); } + + public get player(): HTMLVideoElement | undefined { + if (this._videoRef) { + return this._videoRef; + } + } + @action + setScaling = (r: any) => { + if (this._loaded) { + // bcz: the nativeHeight should really be set when the document is imported. + var nativeWidth = FieldValue(this.Document.nativeWidth, 0); + var nativeHeight = FieldValue(this.Document.nativeHeight, 0); + var newNativeHeight = nativeWidth * r.offset.height / r.offset.width; + if (!nativeHeight && newNativeHeight !== nativeHeight && !isNaN(newNativeHeight)) { + this.Document.height = newNativeHeight / nativeWidth * FieldValue(this.Document.width, 0); + this.Document.nativeHeight = newNativeHeight; + } + } else { + this._loaded = true; + } + } - public static LayoutString() { return FieldView.LayoutString(VideoBox) } + @action public Play() { + this.Playing = true; + if (this.player) this.player.play(); + if (!this._playTimer) this._playTimer = setInterval(this.updateTimecode, 1000); + } + + @action public Pause() { + this.Playing = false; + if (this.player) this.player.pause(); + if (this._playTimer) { + clearInterval(this._playTimer); + this._playTimer = undefined; + } + } - constructor(props: FieldViewProps) { - super(props); + @action public FullScreen() { + this._fullScreen = true; + this.player && this.player.requestFullscreen(); } - + @action + updateTimecode = () => this.player && (this.props.Document.curPage = this.player.currentTime) componentDidMount() { + if (this.props.setVideoBox) this.props.setVideoBox(this); } - componentWillUnmount() { + this.Pause(); + if (this._reactionDisposer) this._reactionDisposer(); + } + + @action + setVideoRef = (vref: HTMLVideoElement | null) => { + this._videoRef = vref; + if (vref) { + vref.onfullscreenchange = action((e) => this._fullScreen = vref.webkitDisplayingFullscreen); + if (this._reactionDisposer) this._reactionDisposer(); + this._reactionDisposer = reaction(() => this.props.Document.curPage, () => + vref.currentTime = NumCast(this.props.Document.curPage, 0), { fireImmediately: true }); + } + } + videoContent(path: string) { + let style = "videoBox-cont" + (this._fullScreen ? "-fullScreen" : ""); + return <video className={`${style}`} ref={this.setVideoRef} onPointerDown={this.onPointerDown}> + <source src={path} type="video/mp4" /> + Not supported. + </video>; + } + + getMp4ForVideo(videoId: string = "JN5beCVArMs") { + return new Promise(async (resolve, reject) => { + const videoInfoRequestConfig = { + headers: { + connection: 'keep-alive', + "user-agent": 'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:43.0) Gecko/20100101 Firefox/46.0', + }, + + }; + try { + let responseSchema: any = {}; + const videoInfoResponse = await rp.get(DocServer.prepend(RouteStore.corsProxy + "/" + `https://www.youtube.com/watch?v=${videoId}`), videoInfoRequestConfig); + const dataHtml = videoInfoResponse; + const start = dataHtml.indexOf('ytplayer.config = ') + 18; + const end = dataHtml.indexOf(';ytplayer.load'); + const subString = dataHtml.substring(start, end); + const subJson = JSON.parse(subString); + const stringSub = subJson.args.player_response; + const stringSubJson = JSON.parse(stringSub); + const adaptiveFormats = stringSubJson.streamingData.adaptiveFormats; + const videoDetails = stringSubJson.videoDetails; + responseSchema.adaptiveFormats = adaptiveFormats; + responseSchema.videoDetails = videoDetails; + resolve(responseSchema); + } + catch (err) { + console.log(` + --- Youtube --- + Function: getMp4ForVideo + Error: `, err); + reject(err); + } + }); + } + onPointerDown = (e: React.PointerEvent) => { + e.preventDefault(); + e.stopPropagation(); } - render() { - let field = this.props.doc.Get(this.props.fieldKey) - let path = field == FieldWaiting ? "http://techslides.com/demos/sample-videos/small.mp4": - field instanceof VideoField ? field.Data.href : "http://techslides.com/demos/sample-videos/small.mp4"; - - return ( - <div> - <video width = {200} height = {200} controls className = "videobox-cont"> - <source src = {path} type = "video/mp4"/> - Not supported. - </video> - </div> - ) + let field = Cast(this.Document[this.props.fieldKey], VideoField); + if (!field) { + return <div>Loading</div>; + } + + // this.getMp4ForVideo().then((mp4) => { + // console.log(mp4); + // }).catch(e => { + // console.log("") + // }); + // // + let content = this.videoContent(field.url.href); + return NumCast(this.props.Document.nativeHeight) ? + content : + <Measure offset onResize={this.setScaling}> + {({ measureRef }) => + <div style={{ width: "100%", height: "auto" }} ref={measureRef}> + {content} + </div> + } + </Measure>; } }
\ No newline at end of file |