Video¶
pythonnative.video plays local and remote video in a native player view:
AVPlayer on iOS, Media3 ExoPlayer on Android, and an HTML5 <video> in the
browser preview.
Setup¶
Video needs no permission. On Android, the plugin adds
androidx.media3:media3-exoplayer and androidx.media3:media3-ui to the build.
Only an app that enables the plugin pays for them.
The view¶
import pythonnative as pn
from pythonnative.video import Video
@pn.component
def Player() -> pn.Node:
player = pn.use_ref()
return pn.Column(
Video(
source=pn.asset("intro.mp4"),
controls=True,
ref=player,
style=pn.style(aspect_ratio=16 / 9),
),
pn.Button("Restart", on_press=lambda: player.current.seek(0)),
)
The view has no intrinsic size, so style must give it one: a width and
height, a flex, or an aspect_ratio inside a sized parent. Without one it
collapses to nothing.
source takes an http(s) URL, a file path, a file:// URL, a bundled
Asset, or a path under app/assets/. Changing it replaces
what's playing.
| Prop | Default | Meaning |
|---|---|---|
controls |
False |
Show the platform's playback controls |
autoplay |
False |
Start as soon as the video is ready |
loop |
False |
Restart at the end |
muted |
False |
Silence the audio without changing volume |
volume |
1.0 |
Audio level from 0.0 to 1.0 |
rate |
1.0 |
Playback speed |
resize_mode |
"contain" |
"contain", "cover", or "stretch" |
poster |
None |
An image shown until playback starts |
progress_interval |
0.25 |
Seconds between on_progress calls |
Events¶
| Event | Arguments | When |
|---|---|---|
on_ready |
VideoInfo |
The source loaded and can play |
on_progress |
position, duration |
While playing, and after a seek, every progress_interval |
on_end |
Playback reached the end. A looping video doesn't fire it | |
on_error |
message |
The source can't load or play |
on_buffering |
bool |
Playback stalled waiting for data, or can continue |
The handle¶
A ref receives a VideoHandle: play(),
pause(), seek(seconds), and await position(). play() restarts a video that
has ended.
Lifecycle¶
The player is released when the view unmounts. When the app goes to the background, playback pauses, and it resumes when the app returns if it was playing. Android also releases the player while the activity is stopped, since a device has few hardware decoders, and rebuilds it at the same position.
Platform differences¶
- Controls. iOS shows
AVPlayerViewController, Android showsPlayerView's controller, and the preview shows the browser's own. - Silent switch. On iOS, a video with sound changes the app's audio session
from the default
solo_ambienttoplayback, so the silent switch doesn't mute it. A category the app chose withAudio.set_modestays as it is. - Autoplay in the preview. A browser may refuse to start a video with sound until the user has interacted with the page. Native platforms don't.
- Ready in the preview.
on_readyfires atloadedmetadata, a little earlier than on device.
What wasn't verified on a simulator¶
These ran on the iOS simulator and an Android API 31 emulator with local clips:
on_ready, on_progress, on_end, on_buffering, on_error, play, pause,
seek, position, replay after the end, native controls, and unmounting. Android
also showed the poster, and released the player in the background and rebuilt it on
return. The rest remains unproven:
- Sound output itself: the clips ran muted, and only positions were observed.
- Playback from a network URL, including stalls that fire
on_bufferingmid-stream, since the verification deliberately ran with no network dependency. - The poster and the pause and resume around backgrounding on iOS.
- The hardware decoder limit that Android's release on stop guards against. An emulator decodes in software.
- The browser preview's manager: its script passes a syntax check and the dev server serves it, but it hasn't played anything in a browser.