flet icon indicating copy to clipboard operation
flet copied to clipboard

Video methods can only be called if they are binded to some events

Open bl1nch opened this issue 1 year ago • 4 comments

Description Methods of ft.Video class can only be called if they are binded to some events of some widgets. For example if I call video.is_playing() in ElevatedButton.on_click() it will work, but if I call it in another place I will get an error.

Code example to reproduce the issue:

This code works:

import flet as ft


def main(page: ft.Page):
    sample_media = [
        ft.VideoMedia("https://user-images.githubusercontent.com/28951144/229373720-14d69157-1a56-4a78-a2f4-d7a134d7c3e9.mp4")
    ]

    page.theme_mode = ft.ThemeMode.DARK
    page.title = "Test app"
    page.padding = 0
    page.horizontal_alignment = ft.CrossAxisAlignment.CENTER
    page.add(
        video := ft.Video(
            expand=True,
            playlist=sample_media,
            playlist_mode=ft.PlaylistMode.SINGLE,
            fill_color=ft.colors.BACKGROUND,
            aspect_ratio=16 / 9,
            volume=100,
            autoplay=True,
            filter_quality=ft.FilterQuality.LOW,
            muted=False,
            wakelock=True,
            show_controls=False,
        ),
        ft.ElevatedButton("Click me", on_click=lambda _: print(video.is_playing()))
    )


ft.app(target=main)

This code doesn't work:

import flet as ft


def main(page: ft.Page):
    sample_media = [
        ft.VideoMedia("https://user-images.githubusercontent.com/28951144/229373720-14d69157-1a56-4a78-a2f4-d7a134d7c3e9.mp4")
    ]

    page.theme_mode = ft.ThemeMode.DARK
    page.title = "Test app"
    page.padding = 0
    page.horizontal_alignment = ft.CrossAxisAlignment.CENTER
    page.add(
        video := ft.Video(
            expand=True,
            playlist=sample_media,
            playlist_mode=ft.PlaylistMode.SINGLE,
            fill_color=ft.colors.BACKGROUND,
            aspect_ratio=16 / 9,
            volume=100,
            autoplay=True,
            filter_quality=ft.FilterQuality.LOW,
            muted=False,
            wakelock=True,
            show_controls=False,
        )
    )
    print(video.is_playing())


ft.app(target=main)

Traceback:

Unhandled error processing page session : Traceback (most recent call last):
  File "C:\Users\bl1nc\PycharmProjects\flet\sdk\python\packages\flet-runtime\src\flet_runtime\app.py", line 242, in on_session_created
    await asyncio.get_running_loop().run_in_executor(
TimeoutError: Timeout waiting for invokeMethod is_playing(None) call

Describe the results you expected: Methods of ft.Video class should be callable from any place

bl1nch avatar Apr 24 '24 04:04 bl1nch

Adding a small delay makes second example work:

import time
import flet as ft


def main(page: ft.Page):
    sample_media = [
        ft.VideoMedia("https://user-images.githubusercontent.com/28951144/229373720-14d69157-1a56-4a78-a2f4-d7a134d7c3e9.mp4")
    ]

    page.theme_mode = ft.ThemeMode.DARK
    page.title = "Test app"
    page.padding = 0
    page.horizontal_alignment = ft.CrossAxisAlignment.CENTER
    page.add(
        video := ft.Video(
            expand=True,
            playlist=sample_media,
            playlist_mode=ft.PlaylistMode.SINGLE,
            fill_color=ft.colors.BACKGROUND,
            aspect_ratio=16 / 9,
            volume=100,
            autoplay=True,
            filter_quality=ft.FilterQuality.LOW,
            muted=False,
            wakelock=True,
            show_controls=False,
        )
    )
    time.sleep(2)
    print(video.is_playing())


ft.app(target=main)

This could be explained by video is not rendered/initialized when we call video.is_playing(). We need to figure out how to chain/synchronize that.

FeodorFitsner avatar Apr 24 '24 17:04 FeodorFitsner

Ideally, you should call is_playing() in on_loaded event handler. Imagine, that video is being loaded in 10 seconds or longer. page.add method is not synchronous anyway, but you are trying to poll if it's playing right away.

Btw, it's not working when calling inside on_loaded too. Will be looking into that issue - looks like a bug.

FeodorFitsner avatar Apr 24 '24 17:04 FeodorFitsner

Could use add the track stream to video? Currently no way to know which video is playing...

syleishere avatar Apr 24 '24 22:04 syleishere

Could use add the track stream to video? Currently no way to know which video is playing...

Perhaps a good way to track this is to add an on_media_changed event with the media source parameter and its index in the playlist. It might also be useful to implement events such as on_player_start and on_player_completed.

bl1nch avatar Apr 25 '24 00:04 bl1nch

As far as original posters issue, I've always just used is_playing_async instead of is_playing and never had any issues.

Doesn't seem to be a way to get current index back, I forked repo and tried to add the call, but comes back as null each time. Only thing I can see possible is keeping track within app itself, for that would need to implement on_completed event handler.

A much better solution however would be to wrap media_kit with audio_service, or even wrapping video_player and video_player_win with audio_service if want a stable implementation supported by flutter team. video_player and video_player_win do not support playlists, so having audio_service up front for queue management would be perfect.

syleishere avatar Aug 03 '24 09:08 syleishere

https://pub.dev/packages/audio_service

Queue management:

_audioHandler.addQueueItem(item);
_audioHandler.insertQueueItem(1, item);
_audioHandler.removeQueueItem(item);
_audioHandler.updateQueue([item, ...]);
_audioHandler.skipToNext();
_audioHandler.skipToPrevious();
_audioHandler.skipToQueueItem(2);

Listen to changes to the currently playing item from the Flutter UI:

_audioHandler.mediaItem.listen((MediaItem item) { ... });

Listen to changes to the queue from the Flutter UI:

_audioHandler.queue.listen((List<MediaItem> queue) { ... });

Listen to playback state changes from the Flutter UI:

_audioHandler.playbackState.listen((PlaybackState state) {
  if (state.playing) ... else ...
  switch (state.processingState) {
    case AudioProcessingState.idle: ...
    case AudioProcessingState.loading: ...
    case AudioProcessingState.buffering: ...
    case AudioProcessingState.ready: ...
    case AudioProcessingState.completed: ...
    case AudioProcessingState.error: ...
  }
});

Listen to a stream of continuous changes to the current playback position:

AudioService.position.listen((Duration position) { ... });

Looks beautiful, could take a playlist from flet user, add all media to the queue.

syleishere avatar Aug 03 '24 09:08 syleishere