forked from Open-CT/openbrain
Add video pages.
This commit is contained in:
parent
36f2787837
commit
c38af47c59
|
@ -0,0 +1,61 @@
|
|||
package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/openbrainorg/openbrain/object"
|
||||
)
|
||||
|
||||
func (c *ApiController) GetGlobalVideos() {
|
||||
c.Data["json"] = object.GetGlobalVideos()
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
func (c *ApiController) GetVideos() {
|
||||
owner := c.Input().Get("owner")
|
||||
|
||||
c.Data["json"] = object.GetVideos(owner)
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
func (c *ApiController) GetVideo() {
|
||||
id := c.Input().Get("id")
|
||||
|
||||
c.Data["json"] = object.GetVideo(id)
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
func (c *ApiController) UpdateVideo() {
|
||||
id := c.Input().Get("id")
|
||||
|
||||
var video object.Video
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &video)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = object.UpdateVideo(id, &video)
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
func (c *ApiController) AddVideo() {
|
||||
var video object.Video
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &video)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = object.AddVideo(&video)
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
func (c *ApiController) DeleteVideo() {
|
||||
var video object.Video
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &video)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = object.DeleteVideo(&video)
|
||||
c.ServeJSON()
|
||||
}
|
1
go.mod
1
go.mod
|
@ -3,6 +3,7 @@ module github.com/openbrainorg/openbrain
|
|||
go 1.16
|
||||
|
||||
require (
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1585
|
||||
github.com/astaxie/beego v1.12.3
|
||||
github.com/casdoor/casdoor-go-sdk v0.3.3
|
||||
github.com/danaugrs/go-tsne/tsne v0.0.0-20220306155740-2250969e057f
|
||||
|
|
10
go.sum
10
go.sum
|
@ -51,6 +51,8 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF
|
|||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
|
||||
github.com/alicebob/miniredis v2.5.0+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk=
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1585 h1:ECnkjykkSn3Gsibjd8FrcC+8SMDJcUbJOP4iT2hItrw=
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1585/go.mod h1:RcDobYh8k5VP6TNybz9m++gL3ijVI5wueVr0EM10VsU=
|
||||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
|
@ -159,6 +161,7 @@ github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFG
|
|||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
|
||||
github.com/golang-jwt/jwt/v4 v4.1.0 h1:XUgk2Ex5veyVFVeLm0xhusUTQybEbexJXrvPNOKkSY0=
|
||||
github.com/golang-jwt/jwt/v4 v4.1.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
|
@ -301,8 +304,10 @@ github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0f
|
|||
github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
|
@ -875,6 +880,8 @@ gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
|||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
|
||||
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
|
||||
gopkg.in/ini.v1 v1.66.2 h1:XfR1dOYubytKy4Shzc2LHrrGhU0lDCfDGG1yLPmpgsI=
|
||||
gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
|
@ -887,8 +894,9 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
|
|
@ -94,4 +94,9 @@ func (a *Adapter) createTable() {
|
|||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = a.engine.Sync2(new(Video))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
package object
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/openbrainorg/openbrain/util"
|
||||
"github.com/openbrainorg/openbrain/video"
|
||||
"xorm.io/core"
|
||||
)
|
||||
|
||||
type Video struct {
|
||||
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
|
||||
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
||||
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
||||
DisplayName string `xorm:"varchar(500)" json:"displayName"`
|
||||
|
||||
VideoId string `xorm:"varchar(100)" json:"videoId"`
|
||||
CoverUrl string `xorm:"varchar(200)" json:"coverUrl"`
|
||||
|
||||
PlayAuth string `xorm:"-" json:"playAuth"`
|
||||
}
|
||||
|
||||
func GetGlobalVideos() []*Video {
|
||||
videos := []*Video{}
|
||||
err := adapter.engine.Asc("owner").Desc("created_time").Find(&videos)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return videos
|
||||
}
|
||||
|
||||
func GetVideos(owner string) []*Video {
|
||||
videos := []*Video{}
|
||||
err := adapter.engine.Desc("created_time").Find(&videos, &Video{Owner: owner})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return videos
|
||||
}
|
||||
|
||||
func getVideo(owner string, name string) *Video {
|
||||
v := Video{Owner: owner, Name: name}
|
||||
existed, err := adapter.engine.Get(&v)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if existed {
|
||||
v.PlayAuth = video.GetVideoPlayAuth(v.VideoId)
|
||||
return &v
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func GetVideo(id string) *Video {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
return getVideo(owner, name)
|
||||
}
|
||||
|
||||
func UpdateVideo(id string, video *Video) bool {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
if getVideo(owner, name) == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
_, err := adapter.engine.ID(core.PK{owner, name}).AllCols().Update(video)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
//return affected != 0
|
||||
return true
|
||||
}
|
||||
|
||||
func AddVideo(video *Video) bool {
|
||||
affected, err := adapter.engine.Insert(video)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return affected != 0
|
||||
}
|
||||
|
||||
func DeleteVideo(video *Video) bool {
|
||||
affected, err := adapter.engine.ID(core.PK{video.Owner, video.Name}).Delete(&Video{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return affected != 0
|
||||
}
|
||||
|
||||
func (video *Video) GetId() string {
|
||||
return fmt.Sprintf("%s/%s", video.Owner, video.Name)
|
||||
}
|
|
@ -38,4 +38,11 @@ func initAPI() {
|
|||
beego.Router("/api/update-vectorset", &controllers.ApiController{}, "POST:UpdateVectorset")
|
||||
beego.Router("/api/add-vectorset", &controllers.ApiController{}, "POST:AddVectorset")
|
||||
beego.Router("/api/delete-vectorset", &controllers.ApiController{}, "POST:DeleteVectorset")
|
||||
|
||||
beego.Router("/api/get-global-videos", &controllers.ApiController{}, "GET:GetGlobalVideos")
|
||||
beego.Router("/api/get-videos", &controllers.ApiController{}, "GET:GetVideos")
|
||||
beego.Router("/api/get-video", &controllers.ApiController{}, "GET:GetVideo")
|
||||
beego.Router("/api/update-video", &controllers.ApiController{}, "POST:UpdateVideo")
|
||||
beego.Router("/api/add-video", &controllers.ApiController{}, "POST:AddVideo")
|
||||
beego.Router("/api/delete-video", &controllers.ApiController{}, "POST:DeleteVideo")
|
||||
}
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
package video
|
||||
|
||||
var regionId = ""
|
||||
var accessKeyId = ""
|
||||
var accessKeySecret = ""
|
|
@ -0,0 +1,18 @@
|
|||
package video
|
||||
|
||||
import "github.com/aliyun/alibaba-cloud-sdk-go/services/vod"
|
||||
|
||||
var vodClient *vod.Client
|
||||
|
||||
func init() {
|
||||
vodClient = InitVodClient()
|
||||
}
|
||||
|
||||
func InitVodClient() *vod.Client {
|
||||
vodClient, err := vod.NewClientWithAccessKey(regionId, accessKeyId, accessKeySecret)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return vodClient
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package video
|
||||
|
||||
import "github.com/aliyun/alibaba-cloud-sdk-go/services/vod"
|
||||
|
||||
func GetVideoPlayAuth(videoId string) string {
|
||||
r := vod.CreateGetVideoPlayAuthRequest()
|
||||
r.VideoId = videoId
|
||||
r.AcceptFormat = "JSON"
|
||||
|
||||
resp, err := vodClient.GetVideoPlayAuth(r)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
playAuth := resp.PlayAuth
|
||||
return playAuth
|
||||
}
|
|
@ -5,6 +5,7 @@
|
|||
"dependencies": {
|
||||
"@ant-design/icons": "4.6.2",
|
||||
"@craco/craco": "6.1.1",
|
||||
"aliplayer-react": "^0.7.0",
|
||||
"antd": "4.15.5",
|
||||
"casdoor-js-sdk": "^0.2.7",
|
||||
"copy-to-clipboard": "^3.3.1",
|
||||
|
|
|
@ -13,6 +13,8 @@ import WordsetEditPage from "./WordsetEditPage";
|
|||
import WordsetGraphPage from "./WordsetGraphPage";
|
||||
import VectorsetListPage from "./VectorsetListPage";
|
||||
import VectorsetEditPage from "./VectorsetEditPage";
|
||||
import VideoListPage from "./VideoListPage";
|
||||
import VideoEditPage from "./VideoEditPage";
|
||||
import SigninPage from "./SigninPage";
|
||||
import i18next from "i18next";
|
||||
import SelectLanguageBox from "./SelectLanguageBox";
|
||||
|
@ -58,6 +60,8 @@ class App extends Component {
|
|||
this.setState({ selectedMenuKey: '/wordsets' });
|
||||
} else if (uri.includes('/vectorsets')) {
|
||||
this.setState({ selectedMenuKey: '/vectorsets' });
|
||||
} else if (uri.includes('/videos')) {
|
||||
this.setState({ selectedMenuKey: '/videos' });
|
||||
} else {
|
||||
this.setState({selectedMenuKey: 'null'});
|
||||
}
|
||||
|
@ -231,6 +235,13 @@ class App extends Component {
|
|||
</Link>
|
||||
</Menu.Item>
|
||||
);
|
||||
res.push(
|
||||
<Menu.Item key="/videos">
|
||||
<Link to="/videos">
|
||||
{i18next.t("general:Videos")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
@ -286,6 +297,8 @@ class App extends Component {
|
|||
<Route exact path="/wordsets/:wordsetName/graph" render={(props) => this.renderSigninIfNotSignedIn(<WordsetGraphPage account={this.state.account} {...props} />)}/>
|
||||
<Route exact path="/vectorsets" render={(props) => this.renderSigninIfNotSignedIn(<VectorsetListPage account={this.state.account} {...props} />)}/>
|
||||
<Route exact path="/vectorsets/:vectorsetName" render={(props) => this.renderSigninIfNotSignedIn(<VectorsetEditPage account={this.state.account} {...props} />)}/>
|
||||
<Route exact path="/videos" render={(props) => this.renderSigninIfNotSignedIn(<VideoListPage account={this.state.account} {...props} />)}/>
|
||||
<Route exact path="/videos/:videoName" render={(props) => this.renderSigninIfNotSignedIn(<VideoEditPage account={this.state.account} {...props} />)}/>
|
||||
</Switch>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
import React from "react";
|
||||
import Player from 'aliplayer-react';
|
||||
import * as Setting from "./Setting";
|
||||
|
||||
class Video extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
classes: props,
|
||||
player: null,
|
||||
width: !Setting.isMobile() ? this.props.task.video.width : "100%",
|
||||
height: "100%",
|
||||
};
|
||||
}
|
||||
|
||||
updateVideoSize(width, height) {
|
||||
if (this.props.onUpdateVideoSize !== undefined) {
|
||||
this.props.onUpdateVideoSize(width, height);
|
||||
}
|
||||
}
|
||||
|
||||
handleReady(player) {
|
||||
let videoWidth = player.tag.videoWidth;
|
||||
let videoHeight = player.tag.videoHeight;
|
||||
|
||||
if (this.props.onUpdateVideoSize !== undefined) {
|
||||
if (videoWidth !== 0 && videoHeight !== 0) {
|
||||
this.updateVideoSize(videoWidth, videoHeight);
|
||||
}
|
||||
} else {
|
||||
videoWidth = this.props.task.video.videoWidth;
|
||||
videoHeight = this.props.task.video.videoHeight;
|
||||
}
|
||||
|
||||
const myWidth = player.tag.scrollWidth;
|
||||
const myHeight = videoHeight * myWidth / videoWidth;
|
||||
|
||||
player.setPlayerSize(myWidth, myHeight);
|
||||
this.setState({
|
||||
width: myWidth,
|
||||
height: myHeight,
|
||||
});
|
||||
}
|
||||
|
||||
initPlayer(player) {
|
||||
player.on('ready', () => {this.handleReady(player)});
|
||||
}
|
||||
|
||||
render() {
|
||||
const video = this.props.task.video;
|
||||
|
||||
const config = {
|
||||
source: video.source,
|
||||
cover: video.cover,
|
||||
width: !Setting.isMobile() ? video.width : "100%",
|
||||
height: "100%",
|
||||
autoplay: video.autoplay,
|
||||
isLive: video.isLive,
|
||||
rePlay: video.rePlay,
|
||||
playsinline: video.playsinline,
|
||||
preload: video.preload,
|
||||
controlBarVisibility: video.controlBarVisibility,
|
||||
useH5Prism: video.useH5Prism,
|
||||
// components: [
|
||||
// {
|
||||
// name: "RateComponent",
|
||||
// type: Player.components.RateComponent,
|
||||
// }
|
||||
// ]
|
||||
};
|
||||
|
||||
if (video.source !== undefined) {
|
||||
config.source = video.source;
|
||||
} else {
|
||||
config.vid = video.vid;
|
||||
config.playauth = video.playAuth;
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{width: this.state.width, height: this.state.height, margin: "auto"}}>
|
||||
<Player
|
||||
config={config}
|
||||
onGetInstance={player => {
|
||||
this.initPlayer(player);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default Video;
|
|
@ -0,0 +1,207 @@
|
|||
import React from "react";
|
||||
import {Button, Card, Col, Input, Row} from 'antd';
|
||||
import * as VideoBackend from "./backend/VideoBackend";
|
||||
import * as Setting from "./Setting";
|
||||
import i18next from "i18next";
|
||||
import {LinkOutlined} from "@ant-design/icons";
|
||||
import Video from "./Video";
|
||||
|
||||
class VideoEditPage extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
classes: props,
|
||||
videoName: props.match.params.videoName,
|
||||
video: null,
|
||||
};
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
this.getVideo();
|
||||
}
|
||||
|
||||
getVideo() {
|
||||
VideoBackend.getVideo(this.props.account.name, this.state.videoName)
|
||||
.then((video) => {
|
||||
this.setState({
|
||||
video: video,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
parseVideoField(key, value) {
|
||||
if (["score"].includes(key)) {
|
||||
value = Setting.myParseInt(value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
updateVideoField(key, value) {
|
||||
value = this.parseVideoField(key, value);
|
||||
|
||||
let video = this.state.video;
|
||||
video[key] = value;
|
||||
this.setState({
|
||||
video: video,
|
||||
});
|
||||
}
|
||||
|
||||
renderVideoContent() {
|
||||
let task = {};
|
||||
task.video = {
|
||||
vid: this.state.video.videoId,
|
||||
playAuth: this.state.video.playAuth,
|
||||
cover: this.state.video.coverUrl,
|
||||
videoWidth: 1920,
|
||||
videoHeight: 1080,
|
||||
width: "840px",
|
||||
autoplay: false,
|
||||
isLive: false,
|
||||
rePlay: false,
|
||||
playsinline: true,
|
||||
preload: true,
|
||||
controlBarVisibility: "hover",
|
||||
useH5Prism: true,
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{marginTop: "10px", textAlign: "center"}}>
|
||||
{/*{*/}
|
||||
{/* JSON.stringify(this.state.video)*/}
|
||||
{/*}*/}
|
||||
<div style={{fontSize: 30, marginBottom: "20px"}}>
|
||||
{
|
||||
this.state.video.name
|
||||
}
|
||||
</div>
|
||||
<Video task={task} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
renderVideo() {
|
||||
return (
|
||||
<Card size="small" title={
|
||||
<div>
|
||||
{i18next.t("video:Edit Video")}
|
||||
<Button type="primary" onClick={this.submitVideoEdit.bind(this)}>{i18next.t("general:Save")}</Button>
|
||||
</div>
|
||||
} style={{marginLeft: '5px'}} type="inner">
|
||||
<Row style={{marginTop: '10px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{i18next.t("general:Name")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.video.name} onChange={e => {
|
||||
this.updateVideoField('name', e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{i18next.t("general:Display name")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.video.displayName} onChange={e => {
|
||||
this.updateVideoField('displayName', e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{i18next.t("video:Video ID")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.video.videoId} onChange={e => {
|
||||
this.updateVideoField('videoId', e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{i18next.t("video:Cover")}:
|
||||
</Col>
|
||||
<Col span={22} style={(Setting.isMobile()) ? {maxWidth:'100%'} :{}}>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 1}>
|
||||
{i18next.t("general:URL")} :
|
||||
</Col>
|
||||
<Col span={23} >
|
||||
<Input prefix={<LinkOutlined/>} value={this.state.video.coverUrl} onChange={e => {
|
||||
this.updateVideoField('coverUrl', e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 1}>
|
||||
{i18next.t("general:Preview")}:
|
||||
</Col>
|
||||
<Col span={23} >
|
||||
<a target="_blank" rel="noreferrer" href={this.state.video.coverUrl}>
|
||||
<img src={this.state.video.coverUrl} alt={this.state.video.coverUrl} height={90} style={{marginBottom: '20px'}}/>
|
||||
</a>
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{i18next.t("video:Video")}:
|
||||
</Col>
|
||||
<Col span={22} style={(Setting.isMobile()) ? {maxWidth:'100%'} :{}}>
|
||||
{
|
||||
this.state.video !== null ? this.renderVideoContent() : null
|
||||
}
|
||||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
submitVideoEdit() {
|
||||
let video = Setting.deepCopy(this.state.video);
|
||||
VideoBackend.updateVideo(this.state.video.owner, this.state.videoName, video)
|
||||
.then((res) => {
|
||||
if (res) {
|
||||
Setting.showMessage("success", `Successfully saved`);
|
||||
this.setState({
|
||||
videoName: this.state.video.name,
|
||||
});
|
||||
this.props.history.push(`/videos/${this.state.video.name}`);
|
||||
} else {
|
||||
Setting.showMessage("error", `failed to save: server side failure`);
|
||||
this.updateVideoField('name', this.state.videoName);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
Setting.showMessage("error", `failed to save: ${error}`);
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Row style={{width: "100%"}}>
|
||||
<Col span={1}>
|
||||
</Col>
|
||||
<Col span={22}>
|
||||
{
|
||||
this.state.video !== null ? this.renderVideo() : null
|
||||
}
|
||||
</Col>
|
||||
<Col span={1}>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{margin: 10}}>
|
||||
<Col span={2}>
|
||||
</Col>
|
||||
<Col span={18}>
|
||||
<Button type="primary" size="large" onClick={this.submitVideoEdit.bind(this)}>{i18next.t("general:Save")}</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default VideoEditPage;
|
|
@ -0,0 +1,172 @@
|
|||
import React from "react";
|
||||
import {Link} from "react-router-dom";
|
||||
import {Button, Col, Popconfirm, Row, Table} from 'antd';
|
||||
import moment from "moment";
|
||||
import * as Setting from "./Setting";
|
||||
import * as VideoBackend from "./backend/VideoBackend";
|
||||
import i18next from "i18next";
|
||||
|
||||
class VideoListPage extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
classes: props,
|
||||
videos: null,
|
||||
};
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
this.getVideos();
|
||||
}
|
||||
|
||||
getVideos() {
|
||||
VideoBackend.getVideos(this.props.account.name)
|
||||
.then((res) => {
|
||||
this.setState({
|
||||
videos: res,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
newVideo() {
|
||||
return {
|
||||
owner: this.props.account.name,
|
||||
name: `video_${this.state.videos.length}`,
|
||||
createdTime: moment().format(),
|
||||
displayName: `Video ${this.state.videos.length}`,
|
||||
videoId: "",
|
||||
coverUrl: "",
|
||||
playAuth: "",
|
||||
}
|
||||
}
|
||||
|
||||
addVideo() {
|
||||
const newVideo = this.newVideo();
|
||||
VideoBackend.addVideo(newVideo)
|
||||
.then((res) => {
|
||||
Setting.showMessage("success", `Video added successfully`);
|
||||
this.setState({
|
||||
videos: Setting.prependRow(this.state.videos, newVideo),
|
||||
});
|
||||
}
|
||||
)
|
||||
.catch(error => {
|
||||
Setting.showMessage("error", `Video failed to add: ${error}`);
|
||||
});
|
||||
}
|
||||
|
||||
deleteVideo(i) {
|
||||
VideoBackend.deleteVideo(this.state.videos[i])
|
||||
.then((res) => {
|
||||
Setting.showMessage("success", `Video deleted successfully`);
|
||||
this.setState({
|
||||
videos: Setting.deleteRow(this.state.videos, i),
|
||||
});
|
||||
}
|
||||
)
|
||||
.catch(error => {
|
||||
Setting.showMessage("error", `Video failed to delete: ${error}`);
|
||||
});
|
||||
}
|
||||
|
||||
renderTable(videos) {
|
||||
const columns = [
|
||||
{
|
||||
title: i18next.t("general:Name"),
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
width: '140px',
|
||||
sorter: (a, b) => a.name.localeCompare(b.name),
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<Link to={`/videos/${text}`}>
|
||||
{text}
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: i18next.t("general:Display name"),
|
||||
dataIndex: 'displayName',
|
||||
key: 'displayName',
|
||||
width: '200px',
|
||||
sorter: (a, b) => a.displayName.localeCompare(b.displayName),
|
||||
},
|
||||
{
|
||||
title: i18next.t("video:Video ID"),
|
||||
dataIndex: 'videoId',
|
||||
key: 'videoId',
|
||||
width: '250px',
|
||||
sorter: (a, b) => a.videoId.localeCompare(b.videoId),
|
||||
},
|
||||
{
|
||||
title: i18next.t("video:Cover"),
|
||||
dataIndex: 'coverUrl',
|
||||
key: 'coverUrl',
|
||||
width: '200px',
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<a target="_blank" rel="noreferrer" href={text}>
|
||||
<img src={text} alt={text} width={150} />
|
||||
</a>
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: i18next.t("general:Action"),
|
||||
dataIndex: 'action',
|
||||
key: 'action',
|
||||
width: '80px',
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<div>
|
||||
<Button style={{marginTop: '10px', marginBottom: '10px', marginRight: '10px'}} type="primary" onClick={() => this.props.history.push(`/videos/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
||||
<Popconfirm
|
||||
title={`Sure to delete video: ${record.name} ?`}
|
||||
onConfirm={() => this.deleteVideo(index)}
|
||||
okText="OK"
|
||||
cancelText="Cancel"
|
||||
>
|
||||
<Button style={{marginBottom: '10px'}} type="danger">{i18next.t("general:Delete")}</Button>
|
||||
</Popconfirm>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Table columns={columns} dataSource={videos} rowKey="name" size="middle" bordered pagination={{pageSize: 100}}
|
||||
title={() => (
|
||||
<div>
|
||||
{i18next.t("general:Videos")}
|
||||
<Button type="primary" size="small" onClick={this.addVideo.bind(this)}>{i18next.t("general:Add")}</Button>
|
||||
</div>
|
||||
)}
|
||||
loading={videos === null}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Row style={{width: "100%"}}>
|
||||
<Col span={1}>
|
||||
</Col>
|
||||
<Col span={22}>
|
||||
{
|
||||
this.renderTable(this.state.videos)
|
||||
}
|
||||
</Col>
|
||||
<Col span={1}>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default VideoListPage;
|
|
@ -0,0 +1,56 @@
|
|||
import * as Setting from "../Setting";
|
||||
|
||||
export function getGlobalVideos() {
|
||||
return fetch(`${Setting.ServerUrl}/api/get-global-videos`, {
|
||||
method: "GET",
|
||||
credentials: "include"
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function getVideos(owner) {
|
||||
return fetch(`${Setting.ServerUrl}/api/get-videos?owner=${owner}`, {
|
||||
method: "GET",
|
||||
credentials: "include"
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function getVideo(owner, name) {
|
||||
return fetch(`${Setting.ServerUrl}/api/get-video?id=${owner}/${encodeURIComponent(name)}`, {
|
||||
method: "GET",
|
||||
credentials: "include"
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function getVideoGraph(owner, name, clusterNumber, distanceLimit) {
|
||||
return fetch(`${Setting.ServerUrl}/api/get-video-graph?id=${owner}/${encodeURIComponent(name)}&clusterNumber=${clusterNumber}&distanceLimit=${distanceLimit}`, {
|
||||
method: "GET",
|
||||
credentials: "include"
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function updateVideo(owner, name, video) {
|
||||
let newVideo = Setting.deepCopy(video);
|
||||
return fetch(`${Setting.ServerUrl}/api/update-video?id=${owner}/${encodeURIComponent(name)}`, {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
body: JSON.stringify(newVideo),
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function addVideo(video) {
|
||||
let newVideo = Setting.deepCopy(video);
|
||||
return fetch(`${Setting.ServerUrl}/api/add-video`, {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
body: JSON.stringify(newVideo),
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function deleteVideo(video) {
|
||||
let newVideo = Setting.deepCopy(video);
|
||||
return fetch(`${Setting.ServerUrl}/api/delete-video`, {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
body: JSON.stringify(newVideo),
|
||||
}).then(res => res.json());
|
||||
}
|
|
@ -22,6 +22,7 @@
|
|||
"Save": "Save",
|
||||
"URL": "URL",
|
||||
"Vectorsets": "Vectorsets",
|
||||
"Videos": "Videos",
|
||||
"Wordsets": "Wordsets"
|
||||
},
|
||||
"vectorset": {
|
||||
|
@ -32,6 +33,12 @@
|
|||
"File name": "File name",
|
||||
"File size": "File size"
|
||||
},
|
||||
"video": {
|
||||
"Cover": "Cover",
|
||||
"Edit Video": "Edit Video",
|
||||
"Video": "Video",
|
||||
"Video ID": "Video ID"
|
||||
},
|
||||
"wordset": {
|
||||
"All words": "All words",
|
||||
"Distance limit": "Distance limit",
|
||||
|
|
|
@ -21,8 +21,9 @@
|
|||
"Result": "结果",
|
||||
"Save": "保存",
|
||||
"URL": "链接",
|
||||
"Vectorsets": "向量集",
|
||||
"Wordsets": "词汇集"
|
||||
"Vectorsets": "我的向量集",
|
||||
"Videos": "我的视频",
|
||||
"Wordsets": "我的词汇集"
|
||||
},
|
||||
"vectorset": {
|
||||
"Count": "个数",
|
||||
|
@ -32,6 +33,12 @@
|
|||
"File name": "文件名",
|
||||
"File size": "文件大小"
|
||||
},
|
||||
"video": {
|
||||
"Cover": "封面图片",
|
||||
"Edit Video": "编辑视频",
|
||||
"Video": "视频",
|
||||
"Video ID": "视频ID"
|
||||
},
|
||||
"wordset": {
|
||||
"All words": "所有词汇",
|
||||
"Distance limit": "距离上限",
|
||||
|
|
|
@ -2235,6 +2235,13 @@ ajv@^8.0.1:
|
|||
require-from-string "^2.0.2"
|
||||
uri-js "^4.2.2"
|
||||
|
||||
aliplayer-react@^0.7.0:
|
||||
version "0.7.0"
|
||||
resolved "https://registry.yarnpkg.com/aliplayer-react/-/aliplayer-react-0.7.0.tgz#5560e992e5d06f43a10065c7316e2f60552af82d"
|
||||
integrity sha512-KjjRrtRy48DOUiR4ZdvfiLgD+u1yaT6ywZsGrynxePuv4dWsGt8u5Zsd3V3dxIgKeeep3RoxV7zofhcER40JKQ==
|
||||
dependencies:
|
||||
fetch-js-from-cdn "^0.2.0"
|
||||
|
||||
alphanum-sort@^1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3"
|
||||
|
@ -5159,6 +5166,11 @@ fb-watchman@^2.0.0:
|
|||
dependencies:
|
||||
bser "2.1.1"
|
||||
|
||||
fetch-js-from-cdn@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/fetch-js-from-cdn/-/fetch-js-from-cdn-0.2.0.tgz#0664242d20cae6f69be591bcf2b8dc7c606b7d14"
|
||||
integrity sha512-u+adV8XpElTNrqPF6dcsMkmbP4FFW7W/8XPhU2HgfyXaiGA4NpFp3KXdF1/XMj++vjN65bA17RCxScK7Iw17Yw==
|
||||
|
||||
fflate@^0.3.8:
|
||||
version "0.3.11"
|
||||
resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.3.11.tgz#2c440d7180fdeb819e64898d8858af327b042a5d"
|
||||
|
|
Loading…
Reference in New Issue