Disable iOS Safari lock screen scrubber for media
Asked Answered
S

4

7

Safari on iOS puts a scrubber on its lock screen for simple HTMLAudioElements. For example:

const a = new Audio();
a.src = 'https://example.com/audio.m4a'
a.play();

JSFiddle: https://jsfiddle.net/0seckLfd/

The lock screen will allow me to choose a position in the currently playing audio file.

How can I disable the ability for the user to scrub the file on the lock screen? The metadata showing is fine, and being able to pause/play is also acceptable, but I'm also fine with disabling it all if I need to.

iOS Lock Screen

Sherard answered 27/6, 2019 at 3:13 Comment(11)
I guess the functionality is decided in program code. I would just have a reference to if the user is allowed or not to scrub the file on the lock screen. However, the design guidelines will not allow this since it requires that a user action to have some effect.Ap
@GillsoftAB Unfortunately, there are some situations, such as content license agreements, where allowing the user to scrub is not permitted. I'm working on a way to just go back to the last time offset when the user tries to seek the file, but it's hacky and not a great user experience.Sherard
omit user changes unless some condition. e.g. if (make_ui_change && user_paid) { ... } this is how I've done it. Then any dragged item will realign itself properly (but can still be dragged with no effect).Ap
@GillsoftAB Yeah, that's the approach I'm taking. Unfortunately, there is no event I get directly from these controls. The media element is controlled directly by this OS widget, so I can only respond after-the-fact. I've tried e.preventDefault() and e.stopPropagation() and what not on the seeked and seeking events, but they don't do anything. Therefore, I think the best I can do is keep track of the time, and then seek back to it.Sherard
Ahh, you use React Native or similar? or du you use Apple Music API?Ap
If you use MusicKit JS or similar where JS is used then regular DOM hacks can be used.Ap
@GillsoftAB No, just regular HTML and JavaScript. No native at all, Safari is relaying this media information onto the screen lock widget on its own. I've figured out that it takes the last media element that .play() was called on, and uses that.Sherard
@GillsoftAB RE:MusicKit, thanks, I'll check into that. I've never seen that before. In my case, the music is coming from a custom cloud service.Sherard
developer.apple.com/documentation/musickitjsAp
I guess you are using something similar like this: let aud = document.getElementById("customerAudio"); let ref_time = null; aud.onseeking = function() { ref_time = aud.currentTime; }; Then you will have the problem with timekeeping from the reference point that might sound bad. have you tried to use an active listner to intercept the changes? function seeked_hook() { } document.addEventListener('seeked', seeked_hook, {passive: false}); Another option is the "new" Proxy object that lets you intercept almost anything. Anyway good luck :)Ap
@GillsoftAB Thanks, yeah I've tried that and am working on some wonky inconsistent behavior with the playback and figuring out the time reference before the seek. Unfortunately, something like Proxy or any other wrapper doesn't help, since Safari goes right to the audio element itself.Sherard
I
3

From my understanding you can't block/hide the scrubbing commands unless you can tag the audio as a live stream. That being said, you can use js to refuse scrubbing server-side. Reference the answer here. Although that answer speaks of video, it also works with audio.

Inconstant answered 5/7, 2019 at 15:36 Comment(1)
Thanks, yeah I'm preventing the seeking client-side. Not ideal but probably the only workaround for now. Thanks.Sherard
X
7

DISABLE Player on lock screen completely

if you want to completely remove the lock screen player you could do something like

const a = new Audio();
document.querySelector('button').addEventListener('click', (e) => {
  a.src = 'http://sprott.physics.wisc.edu/wop/sounds/Bicycle%20Race-Full.m4a' 
  a.play();
});

document.addEventListener('visibilitychange', () => {
  if (document.hidden) a.src = undefined
})

https://jsfiddle.net/5s8c9eL0/3/

that is stoping the player when changing tab or locking screen (code to be cleaned improved depending on your needs)

Xanthippe answered 4/7, 2019 at 16:43 Comment(1)
Thanks, stopping the audio isn't an option for me but I upvoted this answer as it may be helpful to others.Sherard
I
3

From my understanding you can't block/hide the scrubbing commands unless you can tag the audio as a live stream. That being said, you can use js to refuse scrubbing server-side. Reference the answer here. Although that answer speaks of video, it also works with audio.

Inconstant answered 5/7, 2019 at 15:36 Comment(1)
Thanks, yeah I'm preventing the seeking client-side. Not ideal but probably the only workaround for now. Thanks.Sherard
M
2

The lock screen / control center scrubber can also be avoided by using Web Audio API.

This is an example of preloading a sound and playing it, with commentary and error handling:


try {
    // <audio> element is simpler for sound effects,
    // but in iOS/iPad it shows up in the Control Center, as if it's music you'd want to play/pause/etc.
    // Also, on subsequent plays, it only plays part of the sound.
    // And Web Audio API is better for playing sound effects anyway because it can play a sound overlapping with itself, without maintaining a pool of <audio> elements.
    window.audioContext = window.audioContext || new AudioContext(); // Interoperate with other things using Web Audio API, assuming they use the same global & pattern.
    const audio_buffer_promise =
        fetch("audio/sound.wav")
            .then(response => response.arrayBuffer())
            .then(array_buffer => audioContext.decodeAudioData(array_buffer))
    var play_sound = async function () {
        audioContext.resume(); // in case it was not allowed to start until a user interaction
        // Note that this should be before waiting for the audio buffer,
        // so that it works the first time (it would no longer be "within a user gesture")
        // This only works if play_sound is called during a user gesture (at least once), otherwise audioContext.resume(); needs to be called externally.

        const audio_buffer = await audio_buffer_promise; // Promises can be awaited any number of times. This waits for the fetch the first time, and is instant the next time.
        // Note that if the fetch failed, it will not retry. One could instead rely on HTTP caching and just fetch() each time, but that would be a little less efficient as it would need to decode the audio file each time, so the best option might be custom caching with request error handling.
        const source = audioContext.createBufferSource();
        source.buffer = audio_buffer;
        source.connect(audioContext.destination);
        source.start();
    };
} catch (error) {
    console.log("AudioContext not supported", error);
    play_sound = function() {
        // no-op
        // console.log("SFX disabled because AudioContext setup failed.");
    };
}
Manille answered 18/12, 2021 at 22:58 Comment(1)
I wasn't able to get this to work. With any file I tried, either in Safari, Chrome or Firefox, it complains it's not able to "decode audio data" or that "the buffer passed to decodeAudioData contains an unknown content type".... Even though the exact same path plays fine with our current <audio> approach. I guess the Web Audio API works a bit differently from the audio element?Yak
W
0

I did a search, in search of a way to help you, but I did not find an effective way to disable the commands, however, I found a way to customize them, it may help you, follow the apple tutorial link

I think what's left to do now is wait, see if ios 13 will bring some option that will do what you want.

Wisner answered 5/7, 2019 at 12:9 Comment(1)
Unfortunately, that link isn't relevant to web pages in Safari.Sherard

© 2022 - 2024 — McMap. All rights reserved.