player icon indicating copy to clipboard operation
player copied to clipboard

<media-player> doesn't respect initial volume and muted props

Open zshall opened this issue 1 year ago • 20 comments

Current Behavior:

When embedding a <media-player> in Svelte with volume set to 0 and muted set to true, the player starts at full volume.

If you bind:this={player} and programatically set player.muted and player.volume it works.

Expected Behavior:

The player should start muted.

Steps To Reproduce:

    <media-player
        class="w-full"
        src={props.src}
        playsInline
        bind:this={player}
        muted={true}
        >
        <media-provider></media-provider>
    </media-player>

Reproduction Link: StackBlitz example

Environment:

Example:

  • Framework: Svelte
  • Meta Framework: SvelteKit
  • Node: 20.0.0
  • OS: Windows@11
  • Browser: Chrome@128

Anything Else?

image

zshall avatar Aug 30 '24 00:08 zshall

In further testing I've noticed that programmatically setting the volume is also a bit off if you do it right around the time you load the player. If I have a method that's called onMount() or onCanPlay() such as this:

function onCanPlay() {
        player.volume = 0.2;
        player.muted = true;
        
        player.play();
    }

It doesn't work. The player is unmuted at full volume when it loads.

Same thing if I reverse the order:

function onCanPlay() {
        player.play();

        player.volume = 0.2;
        player.muted = true;
    }

Player is unmuted at full volume.

A workaround

The only workarounds I've found are to set a timeout for 0 ms that calls this code and then it works:

function onCanPlay() {
        player.play();

        setTimeout(() => {
            if (player) {
                player.volume = 0.2;
                player.muted = true; // works with a 0ms timeout
            }
        }, 0);
    }

This isn't a great solution; is there a preferred way to interact with volume programmatically? My use case is that I have a $store that holds volume and muting info for a number of different video and audio players and I need them to all sync up with the same volume variables, therefore if the program starts muted I need the player to also start muted.

zshall avatar Aug 30 '24 01:08 zshall

Tested and you can't use a remote object to do it either right on launch.

setTimeout(() => {
            remote.changeVolume(0.2);
            remote.mute(true); // works
        }, 0);

This also only works if you setTimeout().

zshall avatar Aug 30 '24 01:08 zshall

this bug is not present in v1.11.30-next first version affected is v1.12.0-next

i am using this in my nuxt app to only have 1 player unmuted

hatsch avatar Sep 06 '24 11:09 hatsch

The same is happening for playbackRate and volume

geerew avatar Sep 11 '24 09:09 geerew

Thanks for reporting! This is a hydration bug with Svelte 4 as mentioned in the docs. You can either upgrade to Svelte 5 or you can mount the player client-side only by dynamically importing it.

👉 Section in docs mentioning this issue.

mihar-22 avatar Sep 15 '24 11:09 mihar-22

Cool I’ll try this today, thanks!

zshall avatar Sep 15 '24 13:09 zshall

@mihar-22 as mentioned earlier, this bug is also affecting nuxt/vuejs. i have ssr disabled in my app. so i doubt it's solely an issue with hydration

version v1.11.30-next is not affected.

hatsch avatar Sep 16 '24 08:09 hatsch

I just tested it and it works as expected, make sure you're setting volume between 0 and 1, for 30% it'd be volume="0.3".

mihar-22 avatar Sep 16 '24 12:09 mihar-22

@mihar-22

I just tested it and it works as expected, make sure you're setting volume between 0 and 1, for 30% it'd be volume="0.3".

Here is a stackblitz reproducing the bug: https://stackblitz.com/edit/vidstack-examples-qufbsu?file=src%2FPlayer.vue

The player's ref of volume is set to 0.1 by default. The volume slider shows this below, but the actual player volume is at 1.

This bug is still present; can you please share how you have yours working?

Daedalus11069 avatar Sep 17 '24 19:09 Daedalus11069

I can also reproduce this using the javascript/cdn import: https://stackblitz.com/edit/stackblitz-starters-jdj5p2?file=index.html Changing the import URL to use @v1.11.30 works as expected, so some issue in newer releases.

Zibbp avatar Sep 24 '24 22:09 Zibbp

same issue on nuxt. Everything works fine with 1.11.30. Thank you for the workaround. @mihar-22, really sorry for the ping, but i think is necessary to re-open the issue

plcdnl avatar Oct 19 '24 08:10 plcdnl

I'm also experiencing this issue on vue.

RedstoneWizard08 avatar Nov 12 '24 02:11 RedstoneWizard08

Same problem here, vue 3.4.31 + vite 5.3.4

truanthh avatar Nov 20 '24 11:11 truanthh

I'm also seeing this on svelte 5 with vidstack 1.12.12. 1.11.30 is okay.

lendle avatar Dec 11 '24 05:12 lendle

1.11.30 is okay

Keihen avatar Dec 28 '24 08:12 Keihen

Is there a real fix to this problem?

plcdnl avatar Mar 01 '25 11:03 plcdnl

Image

Could be this the culprit? Is setted by vidstack and saved in local storage

plcdnl avatar Mar 01 '25 12:03 plcdnl

+1 same issue here

noah1234j avatar May 09 '25 14:05 noah1234j

Hi, sorry in advance if i can be boring. I’ve commented on this issue a few times, but haven’t received any response so far. I’m wondering if this is still on your radar or if there’s anyone maintaining this part of the project.

This problem is still relevant and I’d really appreciate any update or guidance. If there’s anything I can do to help move it forward, I’d be happy to contribute.

plcdnl avatar Jun 03 '25 12:06 plcdnl

This is how I workaround it:

  1. Add a listener on the play event
  2. Set the volume to 0 and mute
  3. Remove the listener so it doesn't mute every time in case the user pauses/plays (so the mute only happens in the first play)

Example on Vue 3 + Vite 6.3.6 + 1.12.13-next:

<script setup lang="ts">
import "vidstack/bundle";
import { computed, onBeforeUnmount, onMounted, useTemplateRef } from "vue";

const props = defineProps<{
  url: string;
  autoplay?: boolean;
  muted?: boolean;
  noVolume?: boolean;
  posterUrl?: string;
}>();

const playerRef = useTemplateRef("player");

const source = computed(() => ({
  src: props.url,
  type: "video/mp4",
}));

// Workaround for bug https://github.com/vidstack/player/issues/1416
const forceMute = () => {
  (playerRef.value as any).muted = true;
  (playerRef.value as any).volume = 0;
  playerRef.value.removeEventListener("play", forceMute);
};

onMounted(() => {
  if (props.muted && playerRef.value) {
    playerRef.value.addEventListener("play", forceMute);
  }
});

onBeforeUnmount(() => {
  if (playerRef.value) {
    playerRef.value.removeEventListener("play", forceMute);
  }
});
</script>

<template>
  <media-player
    ref="player"
    :src="source"
    :autoplay="autoplay"
    :controlsDelay="1000"
    :hideControlsOnMouseLeave="false"
    :loop="true"
    :muted="muted"
    playsinline
  >
    <media-provider></media-provider>
    <media-video-layout :thumbnails="posterUrl"></media-video-layout>
  </media-player>
</template>


gmmarc avatar Oct 13 '25 15:10 gmmarc