Modifying a DOM Property in html.Video - Dash Video Component

I want to add a video player that is time synchronised with my data. Ideally (and currently) I have an html.Video element which plays as expected. Is there anyway to set the video player’s property currentTime = X?

Thanks for any help.

From what google tells me, you need to do this using the Javascript API to the video component. Unfortunately there’s not really a convenient way at the moment to run arbitrary javascript from a callback. (That’s my understanding anyway) For example, inserting an html.Script tag does nothing since you can’t add scripts to a page in this way after the page has loaded.

I’ve been thinking that there could be some merit in creating component whose express purpose is to evaluate Javascript that it is fed. Until someone created such a component, I think you’d need to create you’re own Dash component that exposes the necessary parts of the javascript API as props.

This would be a good candidate for a custom Dash component (Build Your Own Components | Dash for Python Documentation | Plotly) (which you can build if you know React.js or you can contract Plotly to build for you, see Get Pricing).

For example, here are some React components that already exist that control Videos that could be relatively easily ported into Dash:

Great and immediate feedback - I’ll try and build a custom player.

Your responsiveness is awesome and appreciated.

1 Like

Great! Feel free to open a new thread if you need any help working through this, happy to help :+1:

1 Like

I’m working on building the same concept, syncing timestamped CSV Data with video! Would love to know how your going about this :slight_smile:

I’ve made a JavaScript video player component (I’ve actually made 4 - such is the weakness of my JavaScript skills) - but right now - in proof of concept mode - I have:

A JavaScript video playing component that takes a time cursor parameter and “cleverly” advances or slows that video speed - or jumps (depending on the lag) to the cursor time. This allows you vary playback speed, single frame step or jump to a new location. This component can run in your main window or as a separate window reading cursor time regularly from disk or a memcache.

A interval timer in my main window that updates and displays the cursor time and writes it to disk so all my data/video windows can have access to slightly lagged cursor time.

You need to manage the video start wall time and then it can be synced with data wall time.

Let me know if you would like to discuss. I plan to tidy it up in a week or two and would be happy to share it.

Ian

2 Likes

That’s awesome, If you want to talk via discord feel free to add me. ether#9458
I would love to try It out.
I’m working right now to add video-react.js component into dash I’ll let you know how that goes, it exposes current time and seeking time in the state which seem quite useful.

1 Like

I have a video-react component built with marginal results on the speed variation. I had better luck with react-player. I reached out on discord - chat soon.

1 Like

Great, I hit you back up on discord. I’m new to react so currently just binge watching and absorbing all information I can on the topic. I’ll check out react-player also :slight_smile:

1 Like

I’ve got the Data and Video to sync with this code.

import dash
import datetime
from dash.dependencies import Input, Output, State, Event
import dash_core_components as dcc
import dash_html_components as html
import plotly.plotly as py
from plotly import graph_objs as go

from plotly.graph_objs import *
from flask import Flask
import pandas as pd
from collections import deque

import video_engine as rpd

app = dash.Dash()

app.scripts.config.serve_locally = True 
X = deque(maxlen=1)
Y = deque(maxlen=1)
X.append(1)
Y.append(1)

newtime = deque(maxlen=0)

df = pd.read_csv('CleanData.csv') #Reads CSV Data


app.layout = html.Div(children=[
    html.H1(children='the best fucking data analyzer ever',
            style={
                'textAlign': 'center'
                    }),
    dcc.Graph(
        id='sens1',
        style={
            'width': '50%',
            'height': '50%',
            'lineHeight': '60px',
            'borderWidth': '10px',
            'borderStyle': 'solid',
            'borderRadius': '20px',
            'textAlign': 'center',
            'margin': 'left',
        }),
 #   html.H2(id = 'time_counter',
     #    children='''NO TIME YET''',
   #      style={
  #              'textAlign': 'center'
   #                 }),
    html.Div(children=rpd.my_Player(
        id = 'video_player',
        url = 'http://127.0.0.1:8080/testvideo.mp4',
        width = 900,
        height = 720,
        controls = True,
        playing = False,
        seekTo = 0,
        volume = 1 ),
        style={'textAlign': 'center',
                'margin': 'auto'}
    ),
dcc.Slider(id = 'time-slider', value=0, min=0, max=120, step=0.00001,
           marks={0: 'Start', 120: 'End'}),

    
    ])


@app.callback( ##Graph 1
    dash.dependencies.Output('sens1', 'figure'),
    [dash.dependencies.Input('video_player', 'currTime')])
def update_time(newtime):
    X.append(newtime)
    data = go.Scatter(
                x = df['timestamp'],
                y = df['sens1'],
                name = 'EEG 1',
                mode = 'lines'
        )
    data2 = go.Scatter(
                x = df['timestamp'],
                y = df['sens2'],
                name = 'EEG 2',
                mode = 'lines'
    )
    data3 = go.Scatter(
                x = df['timestamp'],
                y = df['sens3'],
                name = 'EEG 3',
                mode = 'lines'
    )
    data4 = go.Scatter(
                x = df['timestamp'],
                y = df['sens4'],
                name = 'EEG 4',
                mode = 'lines'
    )
    
    return {'data': [data, data2, data3], 'layout': go.Layout(xaxis = dict(range=[min(X), max(X)], fixedrange='true'),
                                                              yaxis=dict(range=[min(df['sens3']),max(df['sens3'])]),)}


# TESTING
#  Tested
#   Playing (True or False)
#   Volume (between 0 and 1)
#   seekTo
#   muted
#   playbackRate
#  To BE Tested
#   getCurrentTime
#   ![itworks|600x338]
#   controls (volume only)
#   styles
#   playsInline
#   config


app.css.append_css({
    'external_url': 'https://codepen.io/chriddyp/pen/bWLwgP.css'})

app.css.append_css({
    'external_url': 'https://unpkg.com/video-react@0.9.4/dist/video-react.css'})

if __name__ == '__main__':
    app.run_server(debug=True)

Now I want to update 4 different graphs, when I set this code up using 4 different callback functions, they seem to update at different times and it’s super super laggy, Also If i add 4 go.Scatter data sets to the one graph it won’t work at all, 3 seems to be the max for some reason.

Another problem is in the javascript when I change the interval at which data is sent out to dash anything lower that a half of a second and the application doesn’t work.

Is there a way to improve the functionality of this script and have it update quicker/faster?

Maybe create a slider component and have the value be the input for callback functions that update the video/data?

@chriddyp I know your the GOAT on this stuff, I finally succeeded in getting this thing to work after 100+ attempts working roughly 12 hrs a day on it, I’m not much of a programmer but I feel I’ve learned a lot. If you see any obvious improvements that I’m missing to increase the functionality of this please let me know, I think having data synced to video is awesome and could be very useful.
itworks

2 Likes

Awesome stuff @Beyond_EEG! I’m happy to take a look. Is your video_engine code on GitHub somewhere?

It’s @freshwuzhere 's original code for the react module, I couldn’t of done this without his help. I just built ontop of it to get it to work with dash in the way I needed to update the graph to make it like a timeline sort of view. There’s some issues going on getting access to all the variables however.

@freshwuzhere or @Beyond_EEG - Where is this code published? I’m happy to take a look if the code is published on GitHub

1 Like

Code is published here, let me know if you have any questions :slight_smile: I’ll be available any time of day for the next week as I work to develop this

Embarrassing - it is really just a dev/proof of concept code - I was going to tidy up next week and publish then.

I’ve published a component called react_player_dash on npm and pypi.

It will allow a component to be loaded and play a video in Dash

It gives sufficient control for videos from a number of sources (react-player supports a lot - files, youtube, Vimeo etc).

It is alpha version and my javascript skills are rudimentary but it appears to be working.

I will upload some dcoumentation and sufficient Dash examples to get any intrepid users going.

Updated react-player-dash to fix a number of bugs in the component - all functions tested OK in current version 1.0.9 - have included video_demo.py to give live examples of usage in Dash.

4 Likes

Hello all,
A bit of a late post, but perhaps it helps someone as well. In my app, I run an image processing model and produce a MP4 file. Then I try to show the resulting MP4 file in my app. The processed video is stored in my server folder under ./static/.

Previously, I was having trouble getting the page to update with the new video after changing if I kept the same file name all the time. After a few days of struggling with this, I figured out the solution is to change the file name in the URL definition and then the video will update. If the name stays the same, then the page won’t reload the new file (since it determines the need to reload based on the difference in the URL definition).

Here is a mockup app which produces the right behaviour. It simply switches the URL name which forces the app to reload the video. This is based on the ‘dash-player’ library example from Github. Here, the key is I switch between 2 videos (‘movie.mp4’ and ‘movie_orig.mp4’) in order to simulate such a change. It simply switches the name of the URL definition.

import dash_player
import dash
import dash_html_components as html
import dash_core_components as dcc
from dash.dependencies import Input, Output, State

app = dash.Dash(__name__ )
application = app.server

app.scripts.config.serve_locally = True


app.layout = html.Div([
    dash_player.DashPlayer(
        id='video-player',
        #url='http://media.w3.org/2010/05/bunny/movie.mp4',
        url='static/movie.mp4',
        controls=True
    ),

    html.Button('Set seekTo to 10', id='button-seek-to'),
    html.Button('Switch video', id='button-switch-video'),

    html.Div(id='div-current-time', style={'margin-bottom': '20px'}),

    html.Div(id='div-method-output')
])


@app.callback(Output('div-current-time', 'children'),
              [Input('video-player', 'currentTime')])
def update_time(currentTime):
    return 'Current Time: {}'.format(currentTime)


@app.callback(Output('div-method-output', 'children'),
              [Input('video-player', 'secondsLoaded')],
              [State('video-player', 'duration')])
def update_methods(secondsLoaded, duration):
    return 'Second Loaded: {}, Duration: {}'.format(secondsLoaded, duration)


@app.callback(Output('video-player', 'seekTo'),
              [Input('button-seek-to', 'n_clicks')])
def set_seekTo(n_clicks):
    return 10


@app.callback(Output('video-player','url'),
              [Input('button-switch-video','n_clicks')])
def switch_video(n_clicks):
    if (n_clicks is None) or (n_clicks % 2):
        return "static/movie.mp4"
    else:
        return "static/movie_orig.mp4"



if __name__ == '__main__':
    app.run_server(host='0.0.0.0', port=8050,debug=True)
1 Like

Hi @freshwuzhere. Bro, I am working on my university project where i need to control a local video in dashboard on basis of time. The requirement is that at specific times , the video should go slow and at specific times, the speed should be normal. Can you pl suggest or help. I am new to javascript and react