Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature Request: Persist mux-player settings in LocalStorage #870

Open
1 task done
PJUllrich opened this issue Feb 12, 2024 · 7 comments
Open
1 task done

Feature Request: Persist mux-player settings in LocalStorage #870

PJUllrich opened this issue Feb 12, 2024 · 7 comments
Labels
enhancement New feature or request

Comments

@PJUllrich
Copy link

Is there an existing issue for this?

  • I have searched the existing issues

Which Mux Elements/Packages does this apply to? Select all that apply

mux-player, mux-player-react

Description

It would be great if the mux-player would persist its configuration across page reloads and navigations. So, if a user e.g. chooses 1.5x playback speed and no captions, this configuration should not reset when the page reloads or the user navigates to another URL on the same domain.

Expected Behavior

If a user chooses some mux-player settings, they are set automatically when the user refreshes the page or plays another video on the same domain.

@PJUllrich PJUllrich added the enhancement New feature or request label Feb 12, 2024
@daytime-em
Copy link

Thank you for your request.

I'll bring this up among the team along with the rest of your requests. We should be getting back to you soon.

Thanks again!

@cjpillsbury
Copy link
Contributor

Hey @PJUllrich this is definitely not a wild request but we'll need to think this through on some of the details. We should have a bit of this in place (volume and selected subtitles lang). Playback rate is definitely another "user preference" that sounds reasonable (so long as there's some way to opt-out or opt-in). This would most likely end up being a feature of Media Chrome, the UI architecture we use "under the hood" for Mux Player.

Other than playback rate and the ones already supported, are there other "user preferences" you'd be interested in seeing "remembered" across page loads?

@PJUllrich
Copy link
Author

Hey @cjpillsbury thanks for the response 🙏 I think that volume, captions language, and playback speed are the most important settings. So, that'd be great to have :)

@benadam11
Copy link

The three big settings we get comments about are playback rate, video quality, and captions. I have hacked around the dom to support the first two, but haven't been able to figure out how to persist caption settings to localstorage. Would love for Mux to add support for this 🙏

@PJUllrich
Copy link
Author

@benadam11 while this is not implemented yet, would you mind sharing your workaround for playback rate and video quality? My users are asking for it almost daily :D

@benadam11
Copy link

@PJUllrich basically I am just selecting their media element and listening for events. I am sure this is very brittle and hacky but "it works".

function usePlaybackController({
  el,
  setPlaybackRate,
}: {
  el: HTMLMediaElement | null;
  setPlaybackRate: (value: PlaybackValue) => void;
}) {
  useEffect(() => {
    const container = el?.shadowRoot
      ?.querySelector("media-theme")
      ?.shadowRoot?.querySelector("media-controller");

    const toggle = (e: any) => {
      setPlaybackRate(e.detail);
    };

    container?.addEventListener("mediaplaybackraterequest", toggle);

    return () => {
      container?.removeEventListener("mediaplaybackraterequest", toggle);
    };
  }, [setPlaybackRate, el]);
}

@PJUllrich
Copy link
Author

PJUllrich commented Mar 15, 2024

Thank you @benadam11 . This here is the solution I ended up with. It supports mute/unmute, playback rate, video quality, and even captions but there's a bug with this where the tick isn't set next to the selected text track correctly. So, if you set the captions to "Off" and reload the page, the captions are off, but if you click on the "CC" button, you'll see that e.g. the "English" caption is selected in fact its turned off. If a user clicks on the "English" caption, it doesn't actually switch. They have to first click "off" and then "English" again. This can be confusing, but I decided its a UX issue small enough to be acceptable given that I can now persist the captions preferences of users.

This is how it looks like:
CleanShot 2024-03-15 at 15 58 23@2x

This is my solution:

const SET_PLAYBACK_RATE_EVENT = "mediaplaybackraterequest";
const SET_SUBTITLES_EVENT = "mediashowsubtitlesrequest";
const DISABLE_SUBTITLES_EVENT = "mediadisablesubtitlesrequest";
const SET_QUALITY_EVENT = "mediarenditionrequest";
const SET_VOLUME_EVENT = "mediavolumerequest";
const PLAY_EVENT = "mediaplayrequest";
const MUTE_EVENT = "mediamuterequest";
const UNMUTE_EVENT = "mediaunmuterequest";

const PLAYBACK_RATE_KEY = "playbackrate";
const SUBTITLES_KEY = "subtitles";
const VIDEO_QUALITY_KEY = "videoquality";
const MUTE_KEY = "mutevideo";

export default {
  mounted() {
    this.mediaController = this.el
      .querySelector("mux-player")
      .shadowRoot?.querySelector("media-theme")
      ?.shadowRoot?.querySelector("media-controller");

    // window.mc = this.mediaController;

    if (this.mediaController) {
      this.addEventListeners();
    }
  },
  addEventListeners() {
    this.mediaController.addEventListener(SET_PLAYBACK_RATE_EVENT, (event) => {
      this.saveValue(PLAYBACK_RATE_KEY, event.detail);
    });
    this.mediaController.addEventListener(SET_SUBTITLES_EVENT, (event) => {
      this.saveValue(SUBTITLES_KEY, event.detail);
    });
    this.mediaController.addEventListener(SET_QUALITY_EVENT, (event) => {
      this.saveValue(VIDEO_QUALITY_KEY, event.detail);
    });
    this.mediaController.addEventListener(MUTE_EVENT, (_event) => {
      this.saveValue(MUTE_KEY, "true");
    });
    this.mediaController.addEventListener(UNMUTE_EVENT, (_event) => {
      this.saveValue(MUTE_KEY, "false");
    });
   // We need to listen to this event because if the video is muted and the user changes the volume
   // the video is automatically unmuted.
    this.mediaController.addEventListener(SET_VOLUME_EVENT, (event) => {
      if (event.detail > 0 && localStorage.getItem(MUTE_KEY) === "true") {
        console.log("unmute");
        this.saveValue(MUTE_KEY, "false");
      }
    });
    this.mediaController.addEventListener(PLAY_EVENT, () => {
      // console.log("playing...");
      this.setAllValues();
    });
  },
  setAllValues() {
    this.setValue(PLAYBACK_RATE_KEY, SET_PLAYBACK_RATE_EVENT);
    this.setValue(VIDEO_QUALITY_KEY, SET_QUALITY_EVENT);
    this.setMute();
    this.setSubtitles();
  },
  saveValue(key, value) {
    if (value !== null) {
      // console.log(`Saving ${key} to localStorage with ${value}.`);
      localStorage.setItem(key, value);
    }
  },
  setValue(key, event_name) {
    const value = localStorage.getItem(key);
    if (value !== null) {
      // console.log(`Setting ${key} to ${value} with ${event_name}.`);
      this.sendEvent(event_name, value);
    }
  },
  setSubtitles() {
    const value = localStorage.getItem(SUBTITLES_KEY);
    if (value) {
      // The trick here is to first disable all current subtitles and then set the persisted one
      // If you don't disable the subtitles first, changing them via an Event won't have an effect.
      this.disableCurrentSubtitle();
      this.sendEvent(SET_SUBTITLES_EVENT, value);
    }
  },
  setMute() {
    const value = localStorage.getItem(MUTE_KEY);
    if (value === "true") {
      this.sendEvent(MUTE_EVENT, "true");
    }
  },
  disableCurrentSubtitle() {
    const value = this.mediaController.getAttribute("mediaSubtitlesShowing");

    if (value) {
      const elements = value.split(":");
      const subtitles = [
        {
          kind: "subtitles",
          language: elements[1],
          label: elements[2],
        },
      ];
      this.sendEvent(DISABLE_SUBTITLES_EVENT, subtitles);
    }
  },
  sendEvent(event_name, value) {
    // console.log(`Sending event ${event_name} with ${value}`);
    const event = new CustomEvent(event_name, {
      detail: value,
      composed: true,
      bubbles: true,
    });
    this.mediaController.dispatchEvent(event);
  },
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants