Video.js in streamlit

Hi, streamlit community :ocean:

Currently, I render a video with the st.markdown API, something like:

st.markdown(
    f"""
<video controls="" type="video/mp4" id="replay" width=100% height="auto" src="{video_path}#t=1" playsinline autoplay muted></video>
""",
    unsafe_allow_html=True,
)

This works nicely in both browsers and mobile apps, however, I’d like to switch to Video.js for more granular controllability as it supports more options to interact with the video (e.g. forward by frame, set a low play rate, etc).

As it’s frequently suggested in the other threads, at first I used streamlit.components.v1.html as following:

with open("path/to/my.html", "r") as f:
    video_html = f.read().replace("{video_path}", video_path)
    html(video_html, height=500)
<link href="https://vjs.zencdn.net/8.16.1/video-js.css" rel="stylesheet" />
<script src="https://vjs.zencdn.net/8.16.1/video.min.js"></script>
<video id="my-player" class="video-js">
  <source src="{video_path}" type="video/mp4">
</video>
<script>
var player = videojs('my-player', {
  controls: true,
  autoplay: 'muted',
  preload: 'auto',
  playsinline: true,
  fluid: true,
});
</script>

This successfully rendered the video with the Video.js framework, however, Streamlit wrapped the code into iFrame which requires to specify the height parameter as the absolute value (e.g. height=500), so it won’t work across different browsers and mobile phones. I couldn’t find a solution to make the inserted iFrame fluid to the parent HTML that automatically adjusts the height.

I also tried to use st.markdown(video_html, unsafe_allow_html=True) and st.html(video_html) instead of streamlit.components.v1.html. They both successfully adjusted the size of the inserted HTML, however, Video.js didn’t work. Maybe these APIs sanitize and interfere the custom JavaScripts code.

I’ve exhausted options and wonder if I have to go though the Custom Component to resolve this issue. Does anyone know if I’m going down the right path?

I’m fine to work on the custom component, but I’d like to make sure that it can solve the issue I’m currently facing.

Thanks in advance!

Although you can hard-code it for specific videos, my intuition is that a custom component is the best way to solve this for general use-cases. It looks like there’s a currentHeight method which you could use to get the appropriate height Class: Player

If you want a simple template for making a light-weight component, you can use this GitHub - blackary/cookiecutter-streamlit-component: Template for creating and publishing simple (vanilla js, html and css) bidirectional streamlit component

@blackary Thanks for reply. I just realized that the iFrame is even required for the Custom Component, so it might not be an option: Intro to custom components - Streamlit Docs

  1. A frontend, which is built out of HTML and any other web tech you like (JavaScript, React, Vue, etc.), and gets rendered in Streamlit apps via an iframe tag.

It looks like there’s a currentHeight method which you could use to get the appropriate height Class: Player

The issue here is that there are the other components aside from the video itself, such as buttons and etc. I need to take all the inner elements into account when calculating the height.

If only StreamLit allow us to just inject the raw HTML/JS/CSS as-is without the iFrame wrapper, that would be a great. e.g. Adding an option to skip JS sanitization in st.html.

The good news is that at least you can adjust the iframe height to automatically fit the contents that are inside the iframe, so you don’t necessarily need to know all the heights if you are using a custom component. You can see an example here: streamlit-folium/streamlit_folium/frontend/src/index.tsx at master · randyzwitch/streamlit-folium · GitHub

@blackary Thanks. I’ll give a shot with custom component!

1 Like