How to Detect Seek Events with the Youtube Player API đ[All Method]ī¸
![How to Detect Seek Events with the Youtube Player API đ[All Method]ī¸](https://howisguide.com/wp-content/uploads/2022/02/How-to-Detect-Seek-Events-with-the-Youtube-Player-API-All-Method.png)
The blog will help about How to Detect Seek Events with the Youtube Player API & learn how to solve different problems that come from coding errors. After completing this guide, you will know how to face these kind problem.
Question: What is the best solution for this problem? Answer: This blog code can help you solve errors How to Detect Seek Events with the Youtube Player API. Question:”What should you do if you run into code errors?” Answer:”By following this blog, you can find a solution.”
The Youtube Player API provides lots of events to notify changes to an embedded player.
For instance, the API fires the followings events for the player:
onReady
fires when a player finishes loading and can begin receiving API callsonStateChange
fires when the playerâs state changesonPlaybackQualityChange
fires when the playback quality changesonPlaybackRateChange
fires when the playback rate changesonError
fires when a player error occursonApiChange
fires when the player has loaded or unloaded a module with exposed API methods
We want to focus on the onStateChange
event. From the documentation, we can see that there is a data
property in the associated event object which will hold one of the values below, depending on the state of the player.
-1
: unstarted0
: ended1
: playing2
: paused3
: buffering5
: video cued
How to Detect Play/Pause Events
From the native player API, we can create simple handlePlay()
, handlePause()
, and handleBuffer()
event handlers.
Heads up. Iâll be working in React.
const handleStateChange = event => {
const playerState = event.data;
switch (playerState) {
case 1: // playing
handlePlay();
break;
case 2: // paused
handlePause();
break;
case 3: // buffering
handleBuffer();
break;
}
};
const handlePlay = () => console.log("Play!");
const handlePause = () => console.log("Pause!");
const handleBuffer = () => console.log("Buffer!");
As long as we properly trigger handleStateChange()
when an onStateChange
event fires, we should be perfectly handling play
and pause
events.
But what about seek events? When a user jumps or skips to a new position in the video timeline?
How to Detect Seek Events
If you run the code above with a properly instantiated YouTube player, youâll notice that a seek event triggers pauses, plays, and buffers all in one seek event.
There are two kinds of seeks which trigger different events: mouse seeks and arrow key seeks. Mouse seek events occur when you click on a separate time in the timeline. Arrow key seeks occur when you use the left and right arrow keys to change the time in the timeline.
- Mouse seeks trigger
pause
,buffer
,play
events (2
,3
,1
), in that order - Arrow key seeks trigger
buffer
andplay
events (3
,1
), in that order
Weâre going to redirect all the event handlers into one single function, instead of using the switch
statement from above, which will then determine if the event was a play
, pause
, or seek
.
Weâll also add an event handler for seek
events.
const handleStateChange = event => handleEvent(event.data);
const handlePlay = () => console.log("Play!");
const handlePause = () => console.log("Pause!");
const handleBuffer = () => console.log("Buffer!");
const handleSeek = () => console.log("Seek!");
Weâll create a new state called sequence
that will record the state changes since the last event.
We also need a method for checking if this sequence
contains an event sequence that would trigger a seek
event (either a [2, 3, 1]
or a [3, 1]
).
Weâll use isSubArrayEnd()
for this purpose, which will check if array B
is a subarray of A
and lies at the end of A
.
const isSubArrayEnd = (A, B) => {
if (A.length < B.length)
return false;
let i = 0;
while (i < B.length) {
if (A[A.length - i - 1] !== B[B.length - i - 1])
return false;
i++;
}
return true;
};
Letâs handle the seek
event logic.
const [sequence, setSequence] = useState([]);
const [timer, setTimer] = useState(null);
const handleEvent = type => {
// Update sequence with current state change event
setSequence([...sequence, type]);
if (type == 1 && isSubArrayEnd(sequence, [2, 3])) {
handleSeek(); // Mouse seek
setSequence([]); // Reset event sequence
} else if (type === 1 && isSubArrayEnd(sequence, [3])) {
handleSeek(); // Arrow keys seek
setSequence([]); // Reset event sequence
} else {
clearTimeout(timer); // Cancel previous event
if (type !== 3) { // If we're not buffering,
let timeout = setTimeout(function () { // Start timer
if (type === 1) handlePlay();
else if (type === 2) handlePause();
setSequence([]); // Reset event sequence
}, 250);
setTimer(timeout);
}
}
};
We know that a seek
event always ends with a play
event. In the first two if
statements, weâre checking that the current event is a play
event and that the previous events follow either the [2, 3, 1]
sequence for mouse seeks or [3, 1]
sequence for arrow key seeks.
Weâre also using a timer
state variable to determine how long we should wait before we know a play
or pause
event is just a play
or pause
event, and not a seek
event.
Suppose we trigger a pause
event. This could be an actual pause
, or just the beginning of a seek
. We know a seek
event should trigger a buffer (3
) then a play (1
) after the pause (2
). We use this timer to allow us to wait and check whether the event is truly a seek
event before deciding too quickly.
If the previous event was a pause
, and we realize we were actually at the beginning of a seek
, then clearTimeout()
will cancel the previous timer, preventing the handlePause()
from the previous iteration from firing.
One Last Note
If we donât care about differentiating between mouse and arrow key seek events, then we can remove the first if
statement.
If you ran the code above, you mightâve also noticed playing the video directly after loading it will trigger a seek
event, so we need to check that we didnât just come from the UNSTARTED
or -1
state.
const [sequence, setSequence] = useState([]);
const [timer, setTimer] = useState(null);
const handleEvent = type => {
// Update sequence with current state change event
setSequence([...sequence, type]);
if (type === 1 && isSubArrayEnd(sequence, [3]) && !sequence.includes(-1)) {
handleSeek(); // Arrow keys seek
setSequence([]); // Reset event sequence
} else {
clearTimeout(timer); // Cancel previous event
if (type !== 3) { // If we're not buffering,
let timeout = setTimeout(function () { // Start timer
if (type === 1) handlePlay();
else if (type === 2) handlePause();
setSequence([]); // Reset event sequence
}, 250);
setTimer(timeout);
}
}
};
Solution
Letâs put this all together. The code below is what I use to track play, pause, and seek events.
const [sequence, setSequence] = useState([]);
const [timer, setTimer] = useState(null);
const handleStateChange = event => handleEvent(event.data);
const handlePlay = () => console.log("Play!");
const handlePause = () => console.log("Pause!");
const handleBuffer = () => console.log("Buffer!");
const handleSeek = () => console.log("Seek!");
const isSubArrayEnd = (A, B) => {
if (A.length < B.length)
return false;
let i = 0;
while (i < B.length) {
if (A[A.length - i - 1] !== B[B.length - i - 1])
return false;
i++;
}
return true;
};
const handleEvent = type => {
// Update sequence with current state change event
setSequence([...sequence, type]);
if (type === 1 && isSubArrayEnd(sequence, [3]) && !sequence.includes(-1)) {
handleSeek(); // Arrow keys seek
setSequence([]); // Reset event sequence
} else {
clearTimeout(timer); // Cancel previous event
if (type !== 3) { // If we're not buffering,
let timeout = setTimeout(function () { // Start timer
if (type === 1) handlePlay();
else if (type === 2) handlePause();
setSequence([]); // Reset event sequence
}, 250);
setTimer(timeout);
}
}
};
Revise the code and make it more robust with proper test case and check an error there before implementing into a production environment.
If you need help at any point, please send me a message and I will do my best to assist you.