youtubeblazorvideo.js

play YouTube videos in Blazor using video.js - how to change source


From this tutorial: How to Integrate YouTube video in Blazor WebAssembly I can pass a YouTube URL to a Blazor component and play the video. However when I want to change the URL to play a different video I cannot see how to change the player's source.

<video id="my-player" controls class="video-js" ></video>

protected override async Task OnAfterRenderAsync(bool firstRender) 
{
   if (firstRender) 
   {
      await jsRuntime.InvokeVoidAsync("loadPlayer", "my-player", new {
         controls = true,
         autoplay = false,
         preload = "auto",
         width = @Width,
         height = @Height,
         techOrder = new[] { "youtube" },
         sources = new[] {
            new { type =  "video/youtube", src = @Url}
         }
      });
   }
}

Solution

  • So I went through the tutorial and it's not really designed for creating multiple instances of the video player, especially the js part.

    There's probably ways you can use the lifecycle methods with more complex js code to do this. But I think the simplest option is to completely re-render a new component when the Url changes. We can do that using the @key blazor attribute. When @key is used and the value changes then Blazor will treat the component as a new one. Which means it'll go through all the lifecycles.

    Here are the steps you can take to make this work:-

    1. Update the js script.

    First, we add logic for handling disposing of the video player instances, which is important because we'll be creating multiple instances of the video player.

    player.js

    function loadPlayer(id, options) {
        disposePlayer(id);
        videojs(id, options);
    }
    
    function disposePlayer(id) {
        const player = videojs.getPlayer(id);
        if (player) {
            player.dispose();
        }
    }
    

    2. Update the component.

    Then, we implement the IAsyncDisposable interface and use the exposed method DisposeAsync() to call the newly added disposePlayer function, so that the Video player instance is correctly disposed.

    Player.razor

    @inject IJSRuntime jsRuntime
    @implements IAsyncDisposable
    
    <h3>Player</h3>
    
    <div class="row align-items-center">
        <div class="col-8">
            <video id="my-player" controls class="video-js"></video>
        </div>
    </div>
    <h3>@Url</h3>
    @code {
        [Parameter]
        public string Url { get; set; }
    
        protected override async Task OnAfterRenderAsync(bool firstRender)
        {
            if (firstRender)
            {
                await jsRuntime.InvokeVoidAsync("loadPlayer", "my-player", new
                {
                    controls = true,
                    autoplay = false,
                    preload = "auto",
                    width = 560,
                    height = 264,
                    techOrder = new[] { "youtube" },
                    sources = new[] {
                        new { type =  "video/youtube", src = @Url}
                }
                });
            }
        }
        public async ValueTask DisposeAsync()
        {
            await jsRuntime.InvokeVoidAsync("disposePlayer", "my-player");
        }
    }
    

    3. Update the parent page component.

    Finally, we update the page where the Player.razor component is being rendered to and add the @key attribute to it. The component will be re-rendered whenever the value assigned to @key changes and since we're creating a new video player for each url, it makes sense that the url should be the key.

    Note I've added a button and list to toggle between urls for demo purposes.

    PlayerPage.razor

    @using BlazorVideoJs.Components
    @page "/playerpage"
    
    <Player @key="_urls[_idx]" Url="@_urls[_idx]" />
    
    <button class="btn btn-lg btn-primary" @onclick="HandleClick">Change video</button>
    @code {
        int _idx = 0;
        List<string> _urls = new() { "https://www.youtube.com/watch?v=xjS6SftYQaQ", "https://www.youtube.com/watch?v=9dLrtDVDWYg", "https://www.youtube.com/watch?v=l40nk18GUzk" };
        void HandleClick()
        {
            _idx++;
            if (_idx == _urls.Count)
            {
                _idx = 0;
            }
        }
    }