Skip to content
This repository has been archived by the owner on Dec 17, 2024. It is now read-only.

Commit

Permalink
basic video player (#152)
Browse files Browse the repository at this point in the history
* first commit

* fix with proxy

* fixed empy videos

* return empty video if it cant access /video

* fix for ff video

* bump coverage a bit

* a bit more

* add more coverage

* fix broken test

* only show the video tab if there are videos to show

* diferent test

* ui not loading preview

* videos update

* update to use json endpoint for videos

* implemented review changes

* implemented ui feedback

* remove extra space
  • Loading branch information
eacevedo79 authored and lanwen committed Jun 25, 2019
1 parent c8d580c commit 5e6a1c8
Show file tree
Hide file tree
Showing 9 changed files with 292 additions and 4 deletions.
29 changes: 29 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/rakyll/statik/fs"
"log"
"net/http"
"net/http/httputil"
"net/url"
"os"
"os/signal"
Expand Down Expand Up @@ -65,8 +66,10 @@ func mux(sse *sse.SseBroker) http.Handler {
wsProxy.ServeHTTP(w, r)
log.Printf("[WS_PROXY] [%s] [CLOSED]", r.URL.Path)
})

mux.HandleFunc("/ping", ping)
mux.HandleFunc("/status", status)
mux.HandleFunc("/video/", video)
return mux
}

Expand Down Expand Up @@ -113,6 +116,32 @@ func status(w http.ResponseWriter, req *http.Request) {
w.Write(status)
}

func video(w http.ResponseWriter, req *http.Request) {

req.URL.Path = strings.TrimPrefix(req.URL.Path, "/video")
log.Printf("[VIDEO_PROXY] [/video%s]", req.URL.Path)

u, _ := url.Parse(selenoidUri + "/video" + req.URL.Path)

req, err := http.NewRequest("GET", u.RequestURI(), nil)

proxy := &httputil.ReverseProxy{
Director: func(r *http.Request) {
r.URL = u
},
}

proxy.ServeHTTP(w, req)

if err != nil {
log.Printf("can't get video status (%s)\n", err)
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(`{ "errors": [{"msg": "can't get video"}] }`))
return
}

}

func showVersion() {
fmt.Printf("Git Revision: %s\n", gitRevision)
fmt.Printf("UTC Build Time: %s\n", buildStamp)
Expand Down
37 changes: 35 additions & 2 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ func selenoidApi() http.Handler {
mux.HandleFunc("/status", mockStatus)
return mux
}

func mockStatus(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{
Expand All @@ -40,10 +39,23 @@ func mockStatus(w http.ResponseWriter, _ *http.Request) {
}
}
}
},
"videos":["test_chrome.mp4"]
}`))
}

func videoApi() http.Handler {
mux := http.NewServeMux()
mux.HandleFunc("/video/", mockVideo)
return mux
}

func mockVideo(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("im a video"))
}

func withUrl(path string) string {
return srv.URL + path
}
Expand Down Expand Up @@ -84,6 +96,27 @@ func TestStatus(t *testing.T) {
AssertThat(t, rsp.Header.Get("Content-Type"), Is{"application/json"})
}

func TestVideo(t *testing.T) {

video := httptest.NewServer(videoApi())
selenoidUri = video.URL
rsp, err := http.Get(withUrl("/video/test_chrome.mp4"))

AssertThat(t, err, Is{nil})
AssertThat(t, rsp, Code{http.StatusOK})
AssertThat(t, rsp.Body, Is{Not{nil}})
}

func TestVideoFail(t *testing.T) {

selenoidUri = "http://localhost:1"
rsp, err := http.Get(withUrl("/video/test_chrome1.mp4"))

AssertThat(t, err, Is{nil})
AssertThat(t, rsp, Code{http.StatusBadGateway})
AssertThat(t, rsp.Body, Not{Is{nil}})
}

func TestStatusError(t *testing.T) {
selenoidUri = "http://localhost:1"
rsp, err := http.Get(withUrl("/status"))
Expand Down
21 changes: 21 additions & 0 deletions selenoid/selenoid.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,11 @@ type State struct {
Queued int `json:"queued"`
Pending int `json:"pending"`
Browsers Browsers `json:"browsers"`
Videos Videos `json:"videos"`
}

type Videos []string

/* ------^------- *
* SELENOID TYPES *
* -------------- */
Expand Down Expand Up @@ -93,6 +96,7 @@ func httpDo(ctx context.Context, req *http.Request, handle func(*http.Response,

const (
statusPath = "/status"
videosPath = "/video"
)

func Status(ctx context.Context, baseUrl string) ([]byte, error) {
Expand All @@ -102,6 +106,8 @@ func Status(ctx context.Context, baseUrl string) ([]byte, error) {
}

var state State
var videos []string

if err = httpDo(ctx, req.WithContext(ctx), func(resp *http.Response, err error) error {
if err != nil {
return err
Expand All @@ -112,6 +118,21 @@ func Status(ctx context.Context, baseUrl string) ([]byte, error) {
return nil, err
}

req, err = http.NewRequest("GET", baseUrl+videosPath+"?json", nil)
if err != nil {
return nil, err
}

_ = httpDo(ctx, req.WithContext(ctx), func(resp *http.Response, err error) error {
if err != nil {
return err
}
defer resp.Body.Close()
return json.NewDecoder(resp.Body).Decode(&videos)
})

state.Videos = videos

return json.Marshal(toUI(state, baseUrl))
}

Expand Down
3 changes: 2 additions & 1 deletion selenoid/selenoid_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ func selenoidState() State {
"opera": {
"44.0": {}
}
}
},
"videos":["test_chrome.mp4"]
}`), &state)
return state
}
Expand Down
1 change: 0 additions & 1 deletion ui/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,3 @@ package ui

//go:generate yarn install
//go:generate yarn build

63 changes: 63 additions & 0 deletions ui/src/components/Videos/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import React from "react";
import {CSSTransition, TransitionGroup} from "react-transition-group";

import "./style.scss";


const Videos = (props) => {
const {videos} = props;
const list = Object.keys(videos);

return (

<div className="videos">
<div className="videos__section-title">
Videos
</div>
<TransitionGroup className={`videos__list videos__list_count-${list.length}`}>
{list.length && list.map(video => {
const src = "/video/"+videos[video];
return (

<CSSTransition
key={video}
timeout={500}
classNames="videos-container_state"
unmountOnExit
>

<div className="videos-container">
<div className="video-cap video-cap__name"
title={videos[video]}>
{videos[video]}
</div>
<video controls preload="auto">
<source src={src} type="video/mp4" />
</video>
</div>
</CSSTransition>
);
}
)}
</TransitionGroup>
{(
<CSSTransition
in={!list.length}
timeout={500}
exit={false}
classNames="videos__no-any_state"
unmountOnExit
>
<div className="videos__no-any">
<div title="No any" className="icon dripicons-hourglass"/>
<div className="nosession-any-text">NO VIDEOS YET :'(</div>
</div>
</CSSTransition>
)}

</div>
);

};

export default Videos;
128 changes: 128 additions & 0 deletions ui/src/components/Videos/style.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
$color-accent: #59a781;
$color-session-name: #555f6a;
$border-section-color: #353b42;
$small-screen: 900px;
$big-screen: 1280px;

.videos {
width: 100%;

&__section-title {
color: #666;
position: relative;
top: 0;
left: 0;
padding-left: 5%;
border-bottom: 1px solid $border-section-color;
width: 95%;
letter-spacing: 1px;
font-size: 10px;
line-height: 20px;
margin-bottom: 20px;
}

&__no-any {
color: #fff;
display: flex;
flex-wrap: wrap;
flex-direction: column;
align-items: center;
font-size: 1.2em;
justify-content: center;

.nosession-any-text {
margin: 10px;
}


&_state-enter-active {
display: none;
}
}
}

.videos__list {
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
width: 94%;
padding-left: 3%;
padding-right: 3%;

&_count-0, &_count-1, &_count-2 {
justify-content: center;
}

&_count-3 {
@media screen and (max-width: $big-screen) {
justify-content: flex-start;
}

@media screen and (min-width: $big-screen + 1) {
justify-content: center;
}
}

.videos-container {
flex: 0 0 auto;
transition: all 0.5s;

@media screen and (max-width: $small-screen) {
flex-basis: 100%;
max-width: 100%;
}

@media screen and (min-width: $small-screen + 1) and (max-width: $big-screen) {
flex-basis: 50%;
max-width: 50%;
}

@media screen and (min-width: $big-screen + 1) {
flex-basis: 33.333%;
max-width: 33.333%;
}
video{
flex-basis: 98%;
max-width: 98%;
padding: 0 5px;
}
.video-cap {
height: 30px;
line-height: 30px;
box-shadow: 0 1px 6px rgba(0, 0, 0, .12), 0 1px 4px rgba(0, 0, 0, .12);
margin: -3px 5px 0;
padding: 0 5px;

&__name {
overflow: hidden;
background-color: $color-session-name;
font-family: "Source Code Pro", Menlo, Monaco, Consolas, "Courier New", monospace;

-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
font-size: 13px;
}


} //TRANSITIONS
&_state-enter {
opacity: 0.01;
}

&_state-enter-active {
opacity: 1;
transition: opacity 500ms ease-in;
}

&_state-exit {
opacity: 1;
}

&_state-exit-active {
opacity: 0.01;
transition: opacity 500ms ease-out;
}
}


}
Loading

0 comments on commit 5e6a1c8

Please sign in to comment.