skip to Main Content

I have a Livewire component that uploads a video to YouTube in chunks. While uploading, I want to update the progress bar on the frontend by dispatching Livewire events for each chunk uploaded. However, the issue is that all the dispatched events are received only at the end of the function execution, instead of being received in real-time as the upload progresses. Why is this happening, and how can I fix it?

enter image description here

Here’s a simplified example of my current implementation. The Component (YoutubeVideoUploader.php):

class YoutubeVideoUploader extends Component
{
    public function uploadVideo()
    {
        for($i = 0; $i < 10; $i++) {
            $this->videoUploadProgress = $i;
            $this->dispatch('uploadProgress', ['progress' => $i]);
            Log::info("uploadProgress: " . $i . "%"); // this works well 
            sleep(1);
        }
    }
}

Blade Template (youtube-video-uploader.blade.php):

<form id="videoUploadForm" wire:submit.prevent="uploadVideo">
    <!-- some fields here and a submit button -->
</form>

<script>
    document.addEventListener('livewire:init', () => {
        Livewire.on('uploadProgress', event => {
            const progress = event[0]?.progress ?? 0;
            // init hours, minutes, seconds..
            console.log(`[${hours}:${minutes}:${seconds}] uploadProgress event:`, progress);
        });
    });
</script>

2

Answers


  1. IMO, every time you invoke sleep(1) in your uploadVideo method, PHP will halt the execution of the currently running script for 1 second.
    The frontend will not receive the dispatched events in that time, so it seems events are buffered and fire all at once at the end of function execution.

    Try to Force Livewire and PHP to flush the data to the frontend by using ob_flush() and flush() while the script is still running.

    ....
    Log::info("uploadProgress: " . $i . "%");            
     ob_flush();
     flush();
     sleep(1);
    ....
    
    Login or Signup to reply.
  2. i dont realy know what is the end use of this but i believe you want a visual clarification of how far the function is. This feat can be accomplished by using the livewire stream function in you componant this wil look like:

    <?php
    namespace AppLivewire;
    
    use LivewireComponent;
    
    class Simple extends Component
    {
        public $videoUploadProgress = 0;
        
       public function uploadVideo()
        {
            for($i = 0; $i < 10; $i++) {
                $this->stream(  
                    to: 'count',
                    content: $this->videoUploadProgress,
                    replace: true,
                );
                $this->videoUploadProgress = $i;
                sleep(1);
            }
        }
        
        public function render()
        {
            return view('livewire.simple');
        }
    }
    

    And then you blade wil look something like this:

    <form id="videoUploadForm" wire:submit="uploadVideo">
        <h1 style="color: white;">time: <span wire:stream="count">{{ $videoUploadProgress }}</span></h1>
        <!-- some fields here and a submit button -->
        <button style="color: white;">test</button>
    </form>
    

    If you want the value in your javascript there is probably an event listener that can recognize the value change and will then retrieve the value. This wil be handy to do with an hidden input field. if this is what you want i can do more research and provide that within this answer. code example

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search